菜鸟日记--------进程与信号
本文摘要:介绍了进程和信号的基本概念。进程是程序运行的基本单元,包含线程,具有独立性、动态性等特性,分为就绪、执行、阻塞三态。系统通过进程号管理进程,常见进程类型包括父/子进程、僵尸进程等。文章详细说明了fork/vfork等进程创建函数,以及getpid/wait等进程管理函数。信号是进程间通信方式,Linux支持62种信号,重点介绍了SIGKILL(9)、SIGALRM(14)等常用信号及其处
目录
一、进程初步
1. 进程与线程
什么是进程?进程是程序运行的最基本单元,是一个二进制程序;线程则是程序运行的最小单元。其中进程包含线程。
2. 进程号
进程号是系统管理进程的唯一标识,32位内核限制进程号小于等于32676,达到阈值时重置进程号,64位进程号可达2的22次方。其中1---300进程号被系统进程和守护进程占用,用户使用的进程号在300以后。

3. 进程状态
通过指令ps-aux可以产看当前系统的进程状态,不同的状态由不同标识符表示,这里列举一些代表性的供了解:
S:睡眠 D:不可中断的睡眠(等待),通常等待输入或输出完成 T:停止 N:低优先级任务
Z:僵尸进程 X:死进程 +:后台进程 和用户交互的任务 I:进程是多线程的 <:高优先级

4. 进程特点
进程的特点可以分为四点:独立性、动态性、异步性、并发性,
- 独立性:每个进程之间相互独立,具有独立的空间---系统在每个进程生成之后会分配一个虚拟空间
- 动态性:进程的实质是程序的一次执行过程,进程是动态产生的,动态消亡的
- 并发性:任何进程都可以同其他进程一起并发执行
- 异步性:由于进程间的相互制约,使进程具有执行的间断性,即进程按各自独立的、不可预知的速度向前推进。
5. 进程三态
进程的三态是阻塞态(等待态)、就绪态、执行态

6. 特殊进程
- 父进程:创建/启动一个进程的进程称之为该进程的父进程
- 子进程:被创建的进程称为子进程
- 0号进程:操作系统的引导程序
- 祖先进程:操作系统启动的第一个程序
- 孤儿进程:父进程先于子进程死亡,孤儿进程会被祖先进程收养(回收资源)
- 僵尸进程:一种非常特殊的进程,它几乎已经放弃了所有的内存空间没有任何可执行代码也不能被调度,仅仅在进程列表中保留一个位置,,记载该进程的退出状态等信息供其他进程收集,除此之外僵尸进程不再占有任何空间。
7. 进程常用函数
(1) 创建子进程------fork
该函数用来创建子进程,创建出来的子进程会复制父进程的资源独立运行,并且与父进程一起抢占CPU资源------父子进程抢占资源异步执行。
#include <stdio.h>
#include <unistd.h>
int main() {
pid_t pid = fork();
if (pid == -1) {
perror("fork failed");
return 1;
} else if (pid == 0) {
printf("This is the child process, PID: %d\n", getpid());
} else {
printf("This is the parent process, child PID: %d\n", pid);
}
return 0;
}
(2) 创建子进程------vfork
创建一个新子进程,但子进程与父进程共享地址空间,直到子进程调用exec()或_exit()。这比fork()更高效,常用于子进程立即执行新程序。子进程在父进程的空间中运行,因此父进程会被阻塞,直到子进程结束。
#include <stdio.h>
#include <unistd.h>
int main() {
pid_t pid = vfork();
if (pid == -1) {
perror("vfork failed");
return 1;
} else if (pid == 0) {
printf("Child process using vfork, PID: %d\n", getpid());
_exit(0); // 必须使用_exit避免影响父进程
} else {
printf("Parent process, child PID: %d\n", pid);
}
return 0;
}
(3) 获取进程PID------getpid
返回调用进程的PID(进程ID)
#include <stdio.h>
#include <unistd.h>
int main() {
printf("Current process PID: %d\n", getpid());
return 0;
}
(4) 获取父进程PID------getppid
返回调用进程的父进程的PID(PPID)。如果父进程已结束,可能返回1
#include <stdio.h>
#include <unistd.h>
int main() {
printf("Parent process PID: %d\n", getppid());
return 0;
}
(5) 进程等待------wait
阻塞调用进程,直到任意一个子进程结束。返回结束子进程的PID,并可通过指针参数获取退出状态。如果没有子进程,返回-1。
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
pid_t pid = fork();
if (pid == 0) {
printf("Child process exiting\n");
exit(0); // 子进程正常退出
} else {
int status;
pid_t child_pid = wait(&status);
printf("Child %d exited with status %d\n", child_pid, WEXITSTATUS(status));
}
return 0;
}
(6) 进程等待------waitpid
等待指定PID的子进程结束,提供更多控制(如非阻塞选项)
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
pid_t pid = fork();
if (pid == 0) {
sleep(1); // 子进程延迟退出
exit(0);
} else {
int status;
pid_t child_pid = waitpid(pid, &status, 0); // 阻塞等待
printf("Child %d exited\n", child_pid);
}
return 0;
}
(7) 进程终止------exit()
正常终止进程,执行清理操作(如刷新缓冲区、调用atexit注册的函数)。参数是退出状态(0表示成功,非0表示错误)。
#include <stdio.h>
#include <stdlib.h>
int main() {
printf("Exiting process\n");
exit(0); // 正常退出
}
二、信号
1. 信号定义
信号是进程与进程之间的通讯方式,Linux下62个信号每一个信号都有自己独特的含义,前31个信号继承unix的非实时信号,后31个是linux自己拓展的实时信号
2. 进程间的通讯方式
- 信号--软中断模拟机制,类似于通知
- 管道--可以进行数据传输,具有单向导通性以及阻塞
- 共享内存--多个进程共享一块数据,可以随时读取以及更改
- 信号量集--同步保护资源
3. 特殊信号
2 ------ 结束进程--ctrl+c
3 ------ 结束进程--ctrl+\
9 ------ 死亡信号--SIGKILL
10/12 ------ 预留信号
14 ------ 闹钟信号
17 ------- 捕捉信号变化,自定义处理函数
18 ------ 进程继续执行
19 ------ 进程暂停执行---能力不可被更改
20 ------ 进程暂停---能力可以被更改
常用的信号 17 18 19 14 其他了解即可
4. 信号函数
信号函数这里就介绍4个:kill、signal、alarm、pause。本阶段学习仅需弄懂kill和signal即可后两个使用频率不高
(1) 发送信号------kill
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
pid_t target_pid = 1234;
if (kill(target_pid, SIGTERM) == -1) {
perror("kill");
exit(EXIT_FAILURE);
}
printf("Sent SIGTERM to process %d\n", target_pid);
return 0;
}
(2)闹钟信号-------alarm
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
void alarm_handler(int sig) {
printf("Received SIGALRM signal!\n");
// 可以在这里执行超时处理逻辑
}
int main() {
// 注册 SIGALRM 信号的处理函数
if (signal(SIGALRM, alarm_handler) == SIG_ERR) {
perror("signal");
exit(EXIT_FAILURE);
}
printf("Setting alarm for 5 seconds...\n");
unsigned int remaining = alarm(5); // 设置5秒定时器
// 这里可以做其他事情...
pause(); // 暂停等待信号 (这里为了演示,实际应用中可能用 select/poll/epoll 等)
return 0;
}
(3)注册信号的处理函数-------signal
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void sigint_handler(int sig) {
printf("\nCaught SIGINT (Ctrl+C)! Exiting gracefully...\n");
// 进行清理工作...
exit(EXIT_SUCCESS);
}
int main() {
// 注册 SIGINT 处理函数
if (signal(SIGINT, sigint_handler) == SIG_ERR) {
perror("signal");
exit(EXIT_FAILURE);
}
printf("Press Ctrl+C to test...\n");
while (1) {
sleep(1); // 主循环等待信号
}
return 0;
}
(4)阻塞信号-------pause
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void sigusr1_handler(int sig) {
printf("Received SIGUSR1 signal!\n");
}
int main() {
if (signal(SIGUSR1, sigusr1_handler) == SIG_ERR) {
perror("signal");
exit(EXIT_FAILURE);
}
printf("Process %d waiting for SIGUSR1 signal...\n", getpid());
pause(); // 阻塞在此处,直到收到 SIGUSR1 (或其他未被忽略的信号)
printf("Resuming after signal handler...\n");
return 0;
}更多推荐


所有评论(0)