- 信号的使用
- 1.信号的基本概念
- 1.1图示信号发送和处理
- 1.2常见的信号
- 1.3信号的值在系统源码中的定义
- 2.修改信号的响应方式signal()
- 2.1signal()
- 2.2信号的三种响应方式
- 2.3在键盘上按下 Ctrl+c 时,会给当前终端前台执行的进程发送 SIGINT 信号,用 signal() 修改 SIGINT 信号的响应方式
- 3. 发送信号kill()
- 3.1 kill()
- 3.2使用 kill()系统调用实现类似于系统 kill 命令(只传pid)
- 3.3使用 kill()系统调用实现类似于系统 kill 命令(传pid和信号)
- 4.处理僵尸进程
信号是系统响应某个条件而产生的事件,进程接收到信号会执行相应的 *** 作。与信号有关的系统调用在“signal.h”头文件中有声明。
1.1图示信号发送和处理 1.2常见的信号 1.3信号的值在系统源码中的定义2.修改信号的响应方式signal() 2.1signal()
- #define SIGHUP 1
- #define SIGINT 2 //键盘按下 Ctrl+c 时,会产生该信号
- #define SIGQUIT 3
- #define SIGILL 4
- #define SIGTRAP 5
- #define SIGABRT 6
- #define SIGIOT 6
- #define SIGBUS 7
- #define SIGFPE 8
- #define SIGKILL 9 //该信号的响应方式不允许改变
- #define SIGUSR1 10
- #define SIGSEGV 11
- #define SIGUSR2 12
- #define SIGPIPE 13 //读端关闭的描述符,写端写入时产生,该信号会终止程序
- #define SIGALRM 14
- #define SIGTERM 15 //系统 kill 命令默认发送的信号
- #define SIGSTKFLT 16
- #define SIGCHLD 17 //子进程结束后,会默认给父进程发送该信号
- #define SIGCONT 18
- #define SIGSTOP 19
- #define SIGTSTP 20
- #define SIGTTIN 21
- #define SIGTTOU 22
- #define SIGURG 23
sighandler_t signal(int signum, sighandler_t handler);
signum 代表信号的值
handler代表信号的响应方式
默认:SIG_DFL
忽略:SIG_IGN
自定义:自己写的信号处理函数
- 写一个代码,在终端屏幕上一直打印"hello Linux",当在键盘上按下Ctrl + c时观察(Ctrl + c对应的信号是SIGINT,信号代号为2)
代码如下:
#include#include #include int main() { while(1) { printf("hello Linuxn"); sleep(1); } exit(0); }
运行并在键盘上按Ctrl + c
程序终止
2.用signal()改变信号的响应方式,调用自己写的信号处理函数
代码如下:
#include#include #include #include void sig_fun(int sig) { printf("sig = %dn",sig); } int main() { signal(SIGINT,sig_fun); while(1) { printf("hello Linuxn"); sleep(1); } exit(0); }
运行并在键盘上按Ctrl + c
当按下Ctrl+c时,会给我们的进程发送一个SIGINT信号,signal(SIGINT,sin_fun)收到这个信号后,把信号的值传给sin_fun()函数并调用该函数
(我们发现Ctrl+c不能结束进程,此时可以用Ctrl+退出进程)
3.上述代码,我们按下Ctrl+c打印了信号的代号,那么要是当第一次按下Ctrl+c会结束进程,该如何改进呢?
代码如下:
#include#include #include #include void sig_fun(int sig) { printf("sig = %dn",sig); signal(sig,SIG_DFL); } int main() { signal(SIGINT,sig_fun); while(1) { printf("hello Linuxn"); sleep(1); } exit(0); }
运行并在键盘上连续两次按Ctrl + c
当第一次按下Ctrl+c时,打印SININT的代号,第二次按下Ctrl+c时,进程结束,这是因为在我们的sig_fun()函数中置了signal()函数
kill() 可以向指定的进程发送指定的信号
int kill(pid_t pid,int sig);
pid > 0 指定将信号发送个那个进程
pid == 0 信号被发送到和当前进程在同一个进程组的进程
pid == -1 将信号发送给系统上有权限发送的所有的进程
pid < -1 将信号发送给进程组 id 等于 pid 绝对值,并且有权限发送的所有的进程
sig 指定发送信号的类型
前面我们完成了main程序,第一次按下Ctrl+c的时候会打印信号的代号,第二次按下Ctrl+c的时候会结束进程,我们通过编写mykill.c来给main进程发送SIGINT信号(相当于键盘输入Ctrl+c),因为通过kill()函数发送信号的时候参数需要进程号,所以打印hello Linux的同时也打印一下进程号,即把main.c中代码printf(“hello Linuxn”);改为printf(“hello Linux pid = %dn”,getpid());。
mykill.c代码如下:
#include#include #include #include int main(int argc,char* argv) { //只传入进程号,因为第一个参数是./mytest 所以一共有两个参数 if(argc != 2) { printf("mykill argc errorn"); exit(0); } int pid = 0; sscanf("argv[1]","%d",&pid); if(kill(pid,SIGINT) == -1) { printf("kill errorn"); exit(0); } exit(0); }
一个终端运行main程序,一个终端运行,mykill程序,执行mykill程序的时候输入main程序的进程号,即通过kill()给main程序发送SIGINT信号。
执行两个程序如下:
通过执行结果我们发现,执行两次mykill和在键盘按下两次Ctrl+c效果一样,第一次打印信号的代号,第二次结束进程。
3.3使用 kill()系统调用实现类似于系统 kill 命令(传pid和信号)因为要传入进程号和信号,所以main程序的参数个数变为3
代码如下:
#include#include #include #include int main(int argc,char* argv[]) { if(argc != 3) { printf("mykill argc errorn"); exit(0); } int pid = 0; int sig = 0; sscanf(argv[1],"%d",&pid); sscanf(argv[2],"%d",&sig); if(kill(pid,sig) == -1) { printf("kill errorn"); exit(0); } exit(0); }
执行两个程序如下:
编写test.c
#include#include #include #include #include #include int main() { int n = 0; char* s = NULL; pid_t pid = fork(); assert( pid != -1 ); if( pid == 0 ) { n = 3; s = "child"; } else { n = 7; s = "parent"; } for(int i = 0;i < n;i++) { printf("s = %sn",s); sleep(1); } exit(0); }
在test.c中因为子进程先于父进程结束,父进程没有得到子进程的退出码,所以会产生僵尸进程,僵尸进程在僵尸进程博客中有详细介绍。子进程结束的时候,会给父进程发送一个信号:SIGCHLD
让父进程接受这个信号,并且打印SIGCHLD的代号,修改test.c如下:
#include#include #include #include #include #include void sig_fun(int sig) { printf("sig = %dn",sig); } int main() { signal(SIGCHLD,sig_fun); int n = 0; char* s = NULL; pid_t pid = fork(); assert( pid != -1 ); if( pid == 0 ) { n = 3; s = "child"; } else { n = 7; s = "parent"; } for(int i = 0;i < n;i++) { printf("s = %sn",s); sleep(1); } exit(0); }
编译test.c生成可执行文件test并运行test
分析运行结果,当子进程结束的时候,是给父进程发送了一个SIGCHLD,父进程做出响应和处理。
放到后台执行,发现产生僵尸进程
因此,我们让父进程接受到SIGCHLD信号后,调用一下wait(),因为不关心子进程的退出码,只是避免产生僵尸进程,所以将wait()参数置为NULL;修改test.c中的sig_fun函数如下:
void sig_fun(int sig) { printf("sig = %dn",sig); wait(NULL); }
重新编译运行:
通过运行结果我们发现:避免了子进程变成了僵尸进程,并且父进程并没有产生阻塞!父子进程可以并发执行
当父进程把子进程结束时发送的SIGCHLD信号忽略时,也可以避免产生僵尸进程,即, signal(SIGCHLD,SIG_IGN);因为父进程不关注子进程,子进程结束后,进程消失,释放pcb。注:SIGDFL默认的话就是无作为。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)