对Linux中进程信号的理解与练习。

上次学习了进程间的通信,这次来学习更难得进程信号。

一。信号。

1.首先我们一般用kill -l来查看系统的所有信号。

[ymk@localhost d6]$ kill -l
 1) SIGHUP	 2) SIGINT	 3) SIGQUIT	 4) SIGILL	 5) SIGTRAP
 6) SIGABRT	 7) SIGBUS	 8) SIGFPE	 9) SIGKILL	10) SIGUSR1
11) SIGSEGV	12) SIGUSR2	13) SIGPIPE	14) SIGALRM	15) SIGTERM
16) SIGSTKFLT	17) SIGCHLD	18) SIGCONT	19) SIGSTOP	20) SIGTSTP
21) SIGTTIN	22) SIGTTOU	23) SIGURG	24) SIGXCPU	25) SIGXFSZ
26) SIGVTALRM	27) SIGPROF	28) SIGWINCH	29) SIGIO	30) SIGPWR
31) SIGSYS	34) SIGRTMIN	35) SIGRTMIN+1	36) SIGRTMIN+2	37) SIGRTMIN+3
38) SIGRTMIN+4	39) SIGRTMIN+5	40) SIGRTMIN+6	41) SIGRTMIN+7	42) SIGRTMIN+8
43) SIGRTMIN+9	44) SIGRTMIN+10	45) SIGRTMIN+11	46) SIGRTMIN+12	47) SIGRTMIN+13
48) SIGRTMIN+14	49) SIGRTMIN+15	50) SIGRTMAX-14	51) SIGRTMAX-13	52) SIGRTMAX-12
53) SIGRTMAX-11	54) SIGRTMAX-10	55) SIGRTMAX-9	56) SIGRTMAX-8	57) SIGRTMAX-7
58) SIGRTMAX-6	59) SIGRTMAX-5	60) SIGRTMAX-4	61) SIGRTMAX-3	62) SIGRTMAX-2
63) SIGRTMAX-1	64) SIGRTMAX	

系统中信号列表一共有这62个信号。

2.信号的产生:PCB中有pending 和blocked,里面是比特位的集合,进行产生和阻塞。
 硬件
 软件
 每个信号的缺省处理:
 man 7 signal
 每个信号到达进程后:
 1.按缺省方式处理。
 2.忽略。不能忽略SIGKILL  SIGSTOP
 3.捕获信号并处理,不能捕获并处理SIGKILL  SIGSTOP

二。信号的处理:

typedef void (*__sighandler_t)(int);
	void (*signal(int signum,void (*handler)(int) ))(int);

应用:

  1 #include<stdio.h>
  2 #include<signal.h>
  3 
  4 void handler(int s)
  5 {
  6     printf("recv %d\n",s);
  7 }
  8 
  9 int main(void)
 10 {
 11     signal(SIGINT,handler);
 12 
 13     for(;;)
 14     {
 15         printf("hehe\n");
 16         sleep(1);
 17     }
 18 }

结果是:
[ymk@localhost d6]$ ./s
hehe
hehe
hehe
hehe
^Crecv 2
hehe
hehe
^\Quit (core dumped)
[ymk@localhost d6]$ 

这个程序就是循环打印hehe,当收到SIGINT信号时,就会执行handler函数,打印出recv 2。

三,信号的安装:

1.给pid进程发送signum信号:

首先是发送信号的代码:kill.c
  1 #include<stdio.h>
  2 #include<signal.h>
  3 #include<stdlib.h>
  4 #include<unistd.h>
  5 
  6 int main(void)
  7 {
  8     int pid;
  9     int num;
 10     while(1){
 11         printf("id: ");
 12         scanf("%d",&pid);
 13         printf("signum:");
 14         scanf("%d",&num);
 15 
 16         kill(pid,num);
 17         }
 18 }

在执行一个循环的程序:
[ymk@localhost d6]$ ./a.out
hehe
hehe
hehe
hehe
hehe
hehe
hehe
hehe

最后执行kill.c,用ps -ef | grep a.out查询a.out 的id,然后传给它3号信号结束进程:
[ymk@localhost d6]$ ps -ef | grep a.out
ymk       25367  25079  0 23:30 pts/1    00:00:00 ./a.out
ymk       25369  25132  0 23:30 pts/2    00:00:00 grep --color=auto a.out

[ymk@localhost d6]$ ./k
id: 25367
signum:3

hehe
hehe
hehe
Quit (core dumped)
[ymk@localhost d6]$ 


这就完成了传送信号。

2.给本进程传信号:raise(int signum);

  1 #include<stdio.h>
  2 #include<signal.h>
  3 
  4 void handler(int s)
  5 {
  6     printf("recv %d\n",s);
  7 }
  8 
  9 int main(void)
 10 {
 11     signal(SIGINT,handler);
 12 
 13     for(;;)
 14     {
 15         printf("hehe\n");
 16         sleep(1);
 17         raise(SIGQUIT);
 18     }
 19 }

结果:

[ymk@localhost d6]$ ./a.out
hehe
Quit (core dumped)
[ymk@localhost d6]$ 

这就是给本进程传送信号终止进程。

四。可靠信号和不可靠信号:

1.在Linux中,我们把1~31信号叫做不可靠信号:会出现信号丢失。

把34~64信号叫做可靠信号。它会创造节点保存,所以不会丢失。

五。可重入和不可重入信号:

1.不可重入函数不能放在信号处理函数中使用。
 1.静态变量。
 2.malloc/free.
 3.标准I/O。

六。信号的内核表示:

        进程执行信号处理函数的动作称之为信号抵达。(delivery).
	从信号产生到抵达中间的状态称之为信号的未决(pending)。
	内核可以阻塞信号,当一个被阻塞的信号产生时,就是处于未决状态。
	
	sigset_t set;  修改进程,信号的注册,将对应的信号位置1.
	sigemptyset(&set);//清空。
	sigaddset(&set,SIGINT);//将信号放入集合。
	sigdelset(&set,SIGINT);//将信号从集合删除。
	sigismember(&set,SIGINT);//判断信号在不在这个集合。
	
	//设置内核信号屏蔽。阻塞信号:
	int sigprocmask(int how,const sigset_t *set,sigset_t *old);     
	SIG_BLOCK: mask=mask | (*set)
	SIG_UNBLOCK; mask=mask &   (~*set);
	SIG_SETMASK; mask=*set;

	//获取未决信号集。
	int sigpending(sigset_t *set);

应用:

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 #include<signal.h>
  5 
  6 void handler(int s)
  7 {
  8     printf("recv %d\n",s);
  9 }
 10 
 11 void handler_quit(int s)
 12 {
 13     sigset_t set;
 14     sigemptyset(&set);
 15     sigaddset(&set,SIGINT);
 16     sigprocmask(SIG_UNBLOCK,&set,NULL);
 17 }
 18 int main(void)
 19 {
 20     signal(SIGINT,handler);
 21     signal(SIGQUIT,handler_quit);
 22     sigset_t set;
 23     sigemptyset(&set);
 24     sigaddset(&set,SIGINT);
 25     sigprocmask(SIG_BLOCK,&set,NULL);
 26 
 27     for(;;)
 28     {
 29         int i;
 30         sigset_t pset;
 31         sigemptyset(&pset);
 32         sigpending(&pset);
 33         for(i=1;i<_NSIG;i++)
 34 
 35             if(sigismember(&pset,i))
 36             {
 37                 printf("1");
 38             }
 39             else
 40                 printf("0");
 41         printf("\n");
 42         sleep(1);
 43 
 44     }
 45 }

这个程序会打印出所有信号位的值。当没接受到信号时得到的全是0,当收到2号信号也就是(crtl+c)时,它会屏蔽这个信号,此时它处于未决状态,信号位就被置成1,当接收到3号信号(crtl+\)时,会解除屏蔽,并打印出本来要打印的recv 2.然后将1置成0.这就是整个过程了。

七。竞态条件:它是由于时间的问题造成的。

1.int pause(void);//暂停当前进程,直到为信号打断才返回。
 int alarm(int sec);//到sec规定的秒之后,给当前进程发送SIGALRM。
 sec=0:删除以前设置的定时器。

首先我们先用一下alarm函数来感受一下超时情况:

  1 #include<stdio.h>
  2 #include<signal.h>
  3 #include<stdlib.h>
  4 
  5 int main(void)
  6 {
  7     int num;
  8     alarm(5);
  9     printf("info:");
 10     scanf("%d",&num);
 11     alarm(0);
 12     printf("num=%d\n",num);
 13 }

运行结果:
[ymk@localhost d6]$ ./t
info:Alarm clock
[ymk@localhost d6]$ 
就是5秒之内若没有输入值,就会超时退出。

应用:自己生成一个sleep函数。

  1 #include<stdio.h>
  2 #include<signal.h>
  3 #include<unistd.h>
  4 #include<stdlib.h>
  5 
  6 void handler(int s)
  7 {
  8 }
  9 int mysleep(int sec)
 10 {
 11     signal(SIGALRM,handler);
 12     alarm(sec);
 13     pause();
 14     return alarm(0);
 15 
 16 }
 17 int main(void)
 18 {
 19     int n=1;
 20     for(;;)
 21     {
 22         printf("mmc,%d\n",n++);
 23         mysleep(1);
 24     }
 25 }

结果与用了sleep一样。原理就是:alarm函数会隔sec发送信号,然后才执行pause,循环又接收到alarm信号,继续执行,就这样一直循环下去,作用就跟sleep函数一样。

2.//增强版pause:
 int sigsuspend(sigset_t *set);   
                //相当于信号屏蔽的切换。内核用它时会用它的信号屏蔽block集合,用完后恢复成原样。
 pause+信号屏蔽。

虽然上述代码可以实现sleep,但是它还有一些漏洞,就是在alarm执行到pause执行期间,它的时间有可能导致出现问题,所以我们要用增强版pause。

  1 #include<stdio.h>
  2 #include<signal.h>
  3 #include<unistd.h>
  4 #include<stdlib.h>
  5 
  6 void handler(int s)
  7 {
  8 }
  9 int mysleep(int sec)
 10 {
 11     signal(SIGALRM,handler);
 12     sigset_t set;
 13     sigemptyset(&set);
 14     sigaddset(&set,SIGALRM);
 15     sigprocmask(SIG_BLOCK,&set,NULL);
 16     alarm(sec);
 17 //  pause();
 18     sigset_t myset;
 19     sigemptyset(&myset);
 20     sigsuspend(&myset);
 21 
 22     sigprocmask(SIG_UNBLOCK,&set,NULL);
 23     return alarm(0);
 24 
 25 }
 26 int main(void)
 27 {
 28     int n=1;
 29     for(;;)
 30     {
 31         printf("mmc,%d\n",n++);
 32         mysleep(1);
 33     }
 34 }

这个程序就是让alarm信号先屏蔽,但是sigsuspend这个函数在内核用它时会用它的信号屏蔽block集合,用完后恢复成原样。它返回后在执行解除屏蔽就行了。

八。一些其他信号:

1.SIGCHLD信号:就是当子进程结束时给父进程传送一个信号,这样就能更好的处理僵尸进程了。

应用:

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 #include<signal.h>
  5 
  6 void handler(int s)
  7 {
  8     wait(NULL);
  9 }
 10 int main(void)
 11 {
 12     int i;
 13     signal(SIGCHLD,handler);
 14     if(fork()==0){
 15     for(i=0;i<20;i++)
 16     {
 17         printf("c");
 18         fflush(stdout);
 19         sleep(1);
 20     }
 21     }
 22     else{
 23         for(;;){
 24             printf("p");
 25             fflush(stdout);
 26             sleep(1);
 27             }
 28     }
 29 }

它的结果就是当子进程结束后,父进程接收到这个信号,执行回收子进程,这样就防止了僵尸进程的产生。

2.int sigaction(int signum,struct sigaction *act,NULL);

struct sigaction{
 void (*sa_handler)(int);  //信号处理函数。
 sigset_t sa_mask; //信号处理函数执行期间的信号屏蔽。
 int sa_flag;//=0;

应用:

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<signal.h>
  4 
  5 void handler(int s)
  6 {
  7     printf("recv %d\n",s);
  8 }
  9 int main(void)
 10 {
 11     struct sigaction act;
 12     act.sa_handler=handler;
 13     sigemptyset(&act.sa_mask);
 14     act.sa_flags=0;
 15     sigaction(SIGINT,&act,NULL);
 16     for(;;)
 17     pause();
 18 
 19 
 20 }




它的功能就是自身带了屏蔽信号的作用,就是方便了许多。

以上就是进程信号的内容了,这次的函数虽然不多,但是都很复杂,希望大家能够多加练习。

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值