linux——信号
信号通信,其实就是内核向用户空间进程发送信号,只有内核才能发信号,用户空间进程不能发送信号。
内核可以发送多少种信号呢,使用命令kill -l,就可以查看

我们之前终止进程用的就是第9个
信号通信的框架
信号的发送(发送信号进程):kill、raise、alarm
信号的接收(接收信号进程) : pause()、 sleep、 while(1)
信号的处理(接收信号进程) :signal
1.信号的发送(发送信号进程)
kill:
所需头文件:
#include<signal.h>
#include<sys/types.h>
函数原型:int kill(pid_t pid, int sig);
参数:
函数传入值:pid
正数:要接收信号的进程的进程号
0:信号被发送到所有和pid进程在同一个进程组的进程
‐1:信号发给所有的进程表中的进程(除了进程号最大的进程外)
sig:信号
函数返回值:成功 0 出错 ‐1
首先我们先写一个死循环进程
#include<stdio.h>
int main()
{
while(1);
return 0;
}
然后我们再写kill函数
#include<stdio.h>
#include<stdlib.h>
#include <sys/types.h>
#include <signal.h>
int main(int argc,char*argv[])
{
int sig,pid;
if(argc < 3)
{
printf("input error\n");
return -1;
}
sig = atoi(argv[1]);
pid = atoi(argv[2]);
printf("sig = %d,pid = %d",sig,pid);
kill(pid,sig);
return 0;
}

这个函数等同于终端命令kill -9 <pid>
raise: 发信号给自己 == kill(getpid(), sig)
所需头文件:
#include<signal.h>
#include<sys/types.h>
函数原型:
int raise(int sig);
参数:
函数传入值:sig:信号
函数返回值:
成功 0 出错 ‐1
#include<stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
pid_t pid;
pid=fork();
if(pid>0)
{
sleep(8);
while(1);
}
if(pid == 0)
{
printf("before sig\n");
raise(SIGTSTP);
printf("after sig\n");
}
return 0;
}

父进程在sleep,子进程暂停,因为自己给自己发送了暂停的信号
8秒后

父进程runing,因为到了死循环,子进程还是暂停
#include<stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
pid_t pid;
pid=fork();
if(pid>0)
{
sleep(8);
if(waitpid(pid,NULL,WNOHANG) == 0)
{
kill(pid,9);
}
while(1);
}
if(pid == 0)
{
printf("before sig\n");
raise(SIGTSTP);
printf("after sig\n");
}
return 0;
}

父进程还是sleeping,子进程暂停了,8秒后

父进程进入了while(1),子进程被杀死了,但是因为父进程没回收,所以子进程是僵尸进程
#include<stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
pid_t pid;
pid=fork();
if(pid>0)
{
sleep(8);
if(waitpid(pid,NULL,WNOHANG) == 0)
{
kill(pid,9);
}
wait(NULL);
while(1);
}
if(pid == 0)
{
printf("before sig\n");
raise(SIGTSTP);
printf("after sig\n");
}
return 0;
}

父进程sleeping,子进程暂停,8秒后

子进程被回收了,只有父进程了
alarm : 发送闹钟信号的函数
alarm 与 raise 函数的比较:
相同点:让内核发送信号给当前进程
不同点:
alarm 只会发送SIGALARM信号
alarm 会让内核定时一段时间之后发送信号, raise会让内核立刻发信号
所需头文件#include <unistd.h>
函数原型 unsigned int alarm(unsigned int seconds)
参数:
seconds:指定秒数
返回值:
成功:如果调用此alarm()前,进程中已经设置了闹钟时间,则返回上一个闹钟时间的剩余时间,否则返回0。
出错:‐1
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
int i = 0;
printf("before alarm\n");
alarm(7);
printf("after alarm\n");
while(i<20)
{
i++;
sleep(1);
printf("process %d\n",i);
}
return 0;
}

| 信号名 | 含义 | 默认操作 |
|---|---|---|
| SIGHUP | 该信号在用户终端连接 (正常或非正常) 结束时发出,通常是在终端的控制进程结束时,通知同一会话内的各个作业与控制终端不再关联。 | 终止 |
| SIGINT | 该信号在用户键入 INTR 字符 (通常是 Ctrl-C) 时发出,终端驱动程序发送此信号并送到前台进程中的每一个进程。 | 终止 |
| SIGQUIT | 该信号和 SIGINT 类似,但由 QUIT 字符 (通常是 Ctrl-) 来控制。 | 终止 |
| SIGILL | 该信号在一个进程企图执行一条非法指令时 (可执行文件本身出现错误,或者试图执行数据段、堆栈溢出时) 发出。 | 终止 |
| SIGFPE | 该信号在发生致命的算术运算错误时发出。这里不仅包括浮点运算错误,还包括溢出及除数为 0 等其它所有的算术的错误。 | 终止 |
| SIGKILL | 该信号用来立即结束程序的运行,并且不能被阻塞、处理和忽略。 | 终止 |
| SIGALRM | 该信号当一个定时器到时的时候发出。 | 终止 |
| SIGSTOP | 该信号用于暂停一个进程,且不能被阻塞、处理或忽略。 | 暂停进程 |
| SIGTSTP | 该信号用于暂停交互进程,用户可键入 SUSP 字符 (通常是 Ctrl-Z) 发出这个信号。 | 暂停进程 |
| SIGCHLD | 子进程改变状态时,父进程会收到这个信号 | 忽略 |
| SIGABRT | 该信号用于结束进程 | 终止 |
2.信号的接收
接收信号的进程,要有什么条件:要想使接收的进程能收到信号,这个进程不能结束 :
sleep
pause:进程状态为S
函数原型 int pause(void);
函数返回值 成功:0,出错:‐1
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
int i = 0;
printf("before alarm\n");
pause();
printf("after alarm\n");
while(i<20)
{
i++;
sleep(1);
printf("process %d\n",i);
}
return 0;
}

- 进程调用 pause () → 立刻进入休眠状态
- 不占 CPU,不执行任何代码
- 一直卡在这里,死等
- 只有收到信号(比如 Ctrl+C、SIGALRM、kill)才会继续往下走
3、信号的处理
收到信号的进程,应该怎样处理? 处理的方式:
1.进程的默认处理方式(内核为用户进程设置的默认处理方式)
A:忽略B:终止进程C: 暂停
2.自己的处理方式:
自己处理信号的方法告诉内核,这样你的进程收到了这个信号就会采用你自己的的处理方式。
所需头文件 #include <signal.h>
函数原型 void (*signal(int signum, void (*handler)(int)))(int);
函数传入值
signum:指定信号
handler
SIG_IGN:忽略该信号。
SIG_DFL:采用系统默认方式处理信号
自定义的信号处理函数指针
函数返回值
成功:设置之前的信号处理方式
出错:‐1
signal 函数有二个参数,第一个参数是一个整形变量(信号值),第二个参数是一个函数指针,是我们自己写的处理函数;这个函数的返回值是一个函数指针。
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include <signal.h>
void myfun(int signum)
{
int i = 0;
while(i<10)
{
printf("process signum = %d i = %d\n",signum,i);
sleep(1);
i++;
}
}
int main()
{
int i = 0;
signal(14,myfun);
alarm(7);
printf("before alarm\n");
printf("after alarm\n");
while(i<20)
{
i++;
sleep(1);
printf("process %d\n",i);
}
return 0;
}

signal(14,myfun)d的意思:
告诉系统:如果收到 14 号信号,不要终止程序,而是去执行 myfun 函数!
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include <signal.h>
void myfun(int signum)
{
int i = 0;
while(i<10)
{
printf("process signum = %d i = %d\n",signum,i);
sleep(1);
i++;
}
}
int main()
{
int i = 0;
signal(14,myfun);
alarm(7);
printf("before alarm\n");
printf("after alarm\n");
signal(14,SIG_IGN);
while(i<20)
{
i++;
sleep(1);
printf("process %d\n",i);
}
return 0;
}

signal(14,SIG_IGN)的意思:
收到14号信号直接忽略,不处理也不中断
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include <signal.h>
void myfun(int signum)
{
int i = 0;
while(i<10)
{
printf("process signum = %d i = %d\n",signum,i);
sleep(1);
i++;
}
}
int main()
{
int i = 0;
signal(14,myfun);
alarm(7);
printf("before alarm\n");
printf("after alarm\n");
signal(14,SIG_IGN);
signal(14,SIG_DFL);
while(i<20)
{
i++;
sleep(1);
printf("process %d\n",i);
}
return 0;
}

这段代码总共有3个signal,但是最终会执行最后一个,signal(14,SIG_DFL)的意思是:
收到14号信号,按系统默认动作执行
14 号信号(SIGALRM)的默认动作
终止进程!直接杀死程序!
也就是说:
时间一到(7 秒)内核发 14 号信号程序收到按默认动作 → 直接退出程序!
4、信号父子进程间通信
#include<stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include<stdlib.h>
#include <sys/wait.h>
void myfun(int signum)
{
int i = 0;
while(i < 5)
{
printf("receive signum is %d,i=%d\n",signum,i);
sleep(1);
i++;
}
}
int main()
{
pid_t pid;
pid = fork();
if(pid>0)
{
int i = 0;
signal(10,myfun);
while(1)
{
printf("process i=%d\n",i);
sleep(1);
i++;
}
}
if(pid == 0)
{
sleep(3);
kill(getppid(),10);
sleep(6);
}
return 0;
}

子进程执行3秒后发送10号信号,父进程收到10号信号,开始执行myfun,myfun执行完继续执行父进程,此时子进程执行完了,父进程死循环继续执行,就导致子进程成为了僵尸进程
#include<stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include<stdlib.h>
#include <sys/wait.h>
void myfun(int signum)
{
int i = 0;
while(i < 5)
{
printf("receive signum is %d,i=%d\n",signum,i);
sleep(1);
i++;
}
}
void myfun1(int signum)
{
printf("child signal\n");
wait(NULL);
}
int main()
{
pid_t pid;
pid = fork();
if(pid>0)
{
int i = 0;
signal(10,myfun);
signal(17,myfun1);
while(1)
{
printf("process i=%d\n",i);
sleep(1);
i++;
}
}
if(pid == 0)
{
sleep(3);
kill(getppid(),10);
sleep(6);
exit(0);//kill(getppid(),17)
}
return 0;
}

子进程退出发送了一个17号信号,父进程接收到17号信号,开始执行myfun1,也就回收了子进程
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)