信号与管道(fifo)(转)

转:http://blog.chinaunix.net/uid-26833883-id-3228615.html

一、信号的介绍

 
信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式。
 
信号可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用它来通知用户空间进程发生了那些系统事件。
 
如果该进程当前并未处于执行态,则该信号就由内核保存起来,直到该进程恢复执行再传递个它;如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞取消时才被传递给进程。
 
二、linux操作系统支持的信号
 

A. kill  -l



B.常用信号的含义





三、信号的产生
 
A.用户在终端按下某些键时,终端驱动程序会发送信号给前台进程,例如ctr+c产生SIGINT,  ctr + \产生SIGQUI信号,ctr + z产生SIGTSTP。
 
B. 硬件异常产生信号,这些条件由硬件检测到并通知内核,然后内核向当前进程发送适当的信号。例如当前进程执行了除以0的指令,CPU的运算单元会产生异常, 内核将这个异常解释为SIGFPE信号发送给进程。再比如当前进程访问了非法内存地址,MMU会产生异常,内核将这个异常解释为SIGSEGV信号发送给 当前进程 。
 
C.一个进程调用int kill(pid_t pid,int sig)函数可以给另一个进程发送信号
 
D.可以用kill命令给某个进程发送信号,如果不明确指定信号则发送SIGTERM信号,该信号的默认处理动作是终止进程。
 
E.当内核检测到某种软件条件发生时也可以通过信号通知进程,例如闹钟超时产生SIGALRM信号,向读端已关闭的管道写数据时产生SIGPIPE信号。
 
四、进程对信号的处理
 
A.忽略此信号
B.执行该信号的默认处理动作
C.提供一个信号处理函数,要求内核在处理该信号时切换到用户态执行这个处理函数,这种方式成为捕捉(Catch)一个信号。
 
 
五、相关信号API
 

A.通过系统调用向一个指定的进程发送信号



参数说明:
第一个参数:指定发送信号的接收线程
第二个参数:信号的signum
 
案例一、
父进程从终端输入signum,然后发给子进程
 
 1     #include <stdio.h>
 2     #include <sys/types.h>
 3     #include <signal.h>
 4     #include <stdlib.h>
 5 
 6     int main()
 7     {
 8         int pid;
 9 
10         if((pid = fork()) < 0)
11         {
12         
13             perror("Fail to fork");
14             exit(EXIT_FAILURE);
15         
16         }else if(pid == 0){
17             
18             while(1);
19         
20         }else{
21             
22             int signum;
23             
24             while(scanf("%d",&signum) == 1)
25             {
26                 kill(pid,signum);
27                 system("ps -aux | grep a.out");
28             }
29         }
30 
31         return 0;
32     }
View Code

 

B.捕捉一个信号



对应的API



其原型:

 

5. 信号递达,信号未决,可靠信号,不可靠信号,信号集,信号屏蔽字

  信号在进程中有两种状态:递达(Delivery和未决(Pending

  递达:执行信号的处理动作称为信号递达。

  未决:信号已经发出,信号从产生到递达之间的状态(可能被阻塞,也可能在途中)

 

signal与sigaction

int sigemptyset(sigset_t *set);

int sigfillset(sigset_t *set);

int sigaddset(sigset_t *set, int signo);

int sigdelset(sigset_t *set, int signo);

int sigismember(const sigset_t *set, int signo);

int sigpending(sigset_t *set); //获取当前进程未决信号,存于set信号集

int sigprocmask(int how, const sigset_t *set, sigset_t *oset);

如果oset是非空指针,则读取进程的当前信号屏蔽字通过oset参数传出。如果set是非空指针,则更改进程的信号屏蔽字,参数how指示如何更改。如果oset和set都是非空指针,则先将原来的信号屏蔽字备份到oset里,然后根据set和how参数更改信号屏蔽字。假设当前的信号屏蔽字为mask,下表说明了how参数的可选值。

 

 

6. Shell命令发送信号:kill...

  按键发送信号:ctrl+c ctrl+\...

  软件发送信号:

 子进程结束向父进程发送信号

 发送信号函数:killraisesigqueueabortalarmsetitimer

 程序发生错误发送信号终止(如段错误)

 

4.避免僵尸进程①signal(SIGCHLD,SIG_IGN);②wait()

  调用wait或者waitpid函数,此方法父进程会被挂起(当子进程结束时会自动发送SIGCHLD信号给父进程如果不想让父进程挂起,可以在父进程中加入一条语句:signal(SIGCHLD,SIG_IGN);表示父进程忽略SIGCHLD信号,该信号是子进程退出的时候向父进程发送的。

  当一个子进程结束运行时,它与其父进程之间的关联还会保持到父进程也正常地结束运行或者父进程调用了wait才告终止。

  进程表中代表子进程的数据项是不会立刻释放的,虽然不再活跃了,可子进程还停留在系统里,因为它的退出码还需要保存起来以备父进程中后续的wait调用使用。它将称为一个“僵进程”。

  子进程在终止时会给父进程发SIGCHLD,该信号的默认处理动作是忽略,父进程可以自定义SIGCHLD信号的处理函数,这样父进程只需要专心处理自己的工作,不必关心子进程了,子进程终止时会通知父进程,父进程在信号处理函数中调用wait清理子进程即可。

 1     #include <stdio.h>
 2     #include <signal.h>
 3     #include <unistd.h>
 4     #include <stdlib.h>
 5 
 6     void child_exit_handler(int signum)
 7     {
 8         if(signum == SIGCHLD)
 9         {
10             printf("Child exit.\n");
11             wait(NULL);
12         }
13     }
14 
15     int main()
16     {
17         int pid;
18         int i = 0;
19 
20         //想内核注册,处理 SIGCHLD信号的方式
21         signal(SIGCHLD,child_exit_handler);
22 
23         if((pid = fork()) < 0)
24         {
25             perror("Fail to fork");
26             exit(EXIT_FAILURE);
27 
28         }else if(pid == 0){
29             
30             for(i = 0;i < 5;i ++)
31             {
32                 printf("child loop.\n");
33                 sleep(1);
34             }
35         
36         }else{
37             
38             for(i = 0;i < 5;i ++)
39             {
40                 printf("Father loop.\n");
41                 sleep(2);
42             }
43 
44         }
45 
46         exit(EXIT_SUCCESS);
47     }
View Code

 

C.闹钟函数alarm




larm()也称为闹钟函数,它可以在进程中设置一个定时器。当定时器指定的时间到时,内核就向进程发送SIGALARM信号。
 
seconds:指定的秒数,如果参数seconds为0,则之前设置的闹钟会被取消,并将剩下的时间返回。
 
成功:如果调用此alarm()前,进程中已经设置了闹钟时间,则放回上一个闹钟时间的剩余时间,否则返回0。
 
alarm(100);
........
 
......
 
alarm(5);
 
出错:-1
 
 1     #include <stdio.h>
 2     #include <signal.h>
 3     #include <stdlib.h>
 4 
 5     void handler(int signum)
 6     {
 7         if(signum == SIGALRM)
 8         {
 9             printf("Recv SIGALARM.\n");
10         }
11 
12         exit(EXIT_SUCCESS);
13     }
14 
15     int main()
16     {
17         int count = 0;
18         int n = 0;
19 
20         signal(SIGALRM,handler);
21 
22         n = alarm(10);
23 
24         printf("n = %d.\n",n);
25         
26         sleep(2);
27 
28         n = alarm(5);
29 
30         printf("n = %d.\n",n);
31         
32         while(1)
33         {
34             printf("count = %d.\n", ++count);
35             sleep(1);
36         }
37 
38         return 0;
39     }
View Code

运行结果如下:

 

案例二、综合案例
 
使用FIFO实现clientA与clientB之间聊天
A.输入quit后,两个进程退出
B.如果在20秒内,没有等到另一端发来的消息,则认为对方已不在,此时终止。
 

clientA:

  1     #include <stdio.h>
  2     #include <stdlib.h>
  3     #include <signal.h>
  4     #include <sys/types.h>
  5     #include <sys/stat.h>
  6     #include <errno.h>
  7     #include <string.h>
  8     #include <fcntl.h>
  9 
 10     #define MAX 100
 11 
 12     void signal_handler(int signum)
 13     {
 14         static int flag = 0;
 15 
 16         switch(signum)
 17         {
 18             case SIGALRM:
 19                 if(flag == 0)
 20                 {
 21                     printf("The people is leaving,the system is closed in 10 seconds \
 22                             and you can input 'ctrl + c' cancel.\n");
 23                     alarm(10);
 24                 }else{
 25                     
 26                     kill(getppid(),SIGKILL);
 27                     usleep(500);
 28                     exit(EXIT_SUCCESS);
 29                 }
 30 
 31                 flag = 1;            
 32                 break;
 33 
 34             case SIGINT:
 35                 printf("The alarm is cancel.\n");
 36                 alarm(0);
 37                 break;
 38         }
 39 
 40     }
 41 
 42     int child_recv_fifo(char *fifo_name)
 43     {
 44         int n,fd;
 45         char buf[MAX];
 46 
 47         if((fd = open(fifo_name,O_RDONLY)) < 0)
 48         {
 49             fprintf(stderr,"fail to open %s : %s.\n",fifo_name,strerror(errno));
 50             return -1;
 51         }
 52 
 53         signal(SIGALRM,signal_handler);
 54         signal(SIGINT,signal_handler);
 55         alarm(15);//璁剧疆瀹氭椂鍣?    
 56         while(1)
 57         {
 58             n = read(fd,buf,sizeof(buf));
 59             buf[n] = '\0';
 60 
 61             printf("Read %d bytes : %s.\n",n,buf);
 62 
 63             if(strncmp(buf,"quit",4) == 0 || n == 0)
 64             {
 65                 kill(getppid(),SIGKILL);
 66                 usleep(500);
 67                 exit(EXIT_SUCCESS);
 68             }
 69 
 70             alarm(15);
 71         }
 72 
 73         return 0;
 74     }
 75 
 76     int father_send_fifo(char *fifo_name,int pid)
 77     {
 78         int n,fd;
 79         char buf[MAX];
 80 
 81         if((fd = open(fifo_name,O_WRONLY)) < 0)
 82         {
 83             fprintf(stderr,"Fail to open %s : %s.\n",fifo_name,strerror(errno));
 84             return -1;
 85         }
 86 
 87         signal(SIGINT,SIG_IGN);
 88 
 89         while(1)
 90         {
 91             getchar();
 92             printf(">");
 93 
 94             fgets(buf,sizeof(buf),stdin);
 95             buf[strlen(buf)-1] = '\0';
 96 
 97             write(fd,buf,strlen(buf));
 98 
 99             if(strncmp(buf,"quit",4) == 0)
100             {
101                 kill(pid,SIGKILL);
102                 usleep(500);
103                 exit(EXIT_SUCCESS);
104             }
105         }
106 
107         return 0;
108     }
109 
110     int main(int argc,char *argv[])
111     {
112         int pid;
113 
114         if(argc < 3)
115         {
116             fprintf(stderr,"usage %s argv[1].\n",argv[0]);
117             exit(EXIT_FAILURE);
118         }
119 
120         if(mkfifo(argv[1],0666) < 0 && errno != EEXIST)
121         {
122             perror("Fail to mkfifo");
123             exit(EXIT_FAILURE);
124         }
125 
126         if(mkfifo(argv[2],0666) < 0 && errno != EEXIST)
127         {
128             perror("Fail to mkfifo");
129             exit(EXIT_FAILURE);
130         }
131         
132         if((pid = fork()) < 0)
133         {
134         
135             perror("Fail to fork");
136             exit(EXIT_FAILURE);
137         
138         }else if(pid == 0){
139             
140             child_recv_fifo(argv[2]);
141         
142         }else{
143 
144             father_send_fifo(argv[1],pid);
145         }
146 
147         exit(EXIT_SUCCESS);
148     }
View Code

 

client B

 

  1     #include <stdio.h>
  2     #include <stdlib.h>
  3     #include <signal.h>
  4     #include <sys/types.h>
  5     #include <sys/stat.h>
  6     #include <errno.h>
  7     #include <string.h>
  8     #include <fcntl.h>
  9 
 10     #define MAX 100
 11 
 12     void signal_handler(int signum)
 13     {
 14         static int flag = 0;
 15 
 16         switch(signum)
 17         {
 18             case SIGALRM:
 19                 if(flag == 0)
 20                 {
 21                     printf("The people is leaving,the system is closed in 10 seconds \
 22                             and you can input 'ctrl + c' cancel.\n");
 23                     alarm(10);
 24                 }else{
 25                     
 26                     kill(getppid(),SIGKILL);
 27                     usleep(500);
 28                     exit(EXIT_SUCCESS);
 29                 }
 30 
 31                 flag = 1;            
 32                 break;
 33 
 34             case SIGINT:
 35                 printf("The alarm is cancel.\n");
 36                 alarm(0);
 37                 break;
 38         }
 39 
 40     }
 41 
 42     int child_recv_fifo(char *fifo_name)
 43     {
 44         int n,fd;
 45         char buf[MAX];
 46 
 47         if((fd = open(fifo_name,O_RDONLY)) < 0)
 48         {
 49             fprintf(stderr,"fail to open %s : %s.\n",fifo_name,strerror(errno));
 50             return -1;
 51         }
 52 
 53         signal(SIGALRM,signal_handler);
 54         signal(SIGINT,signal_handler);
 55         alarm(15);//璁剧疆瀹氭椂鍣?    
 56         while(1)
 57         {
 58             n = read(fd,buf,sizeof(buf));
 59             buf[n] = '\0';
 60 
 61             printf("Read %d bytes : %s.\n",n,buf);
 62 
 63             if(strncmp(buf,"quit",4) == 0 || n == 0)
 64             {
 65                 kill(getppid(),SIGKILL);
 66                 usleep(500);
 67                 exit(EXIT_SUCCESS);
 68             }
 69 
 70             alarm(15);
 71         }
 72 
 73         return 0;
 74     }
 75 
 76     int father_send_fifo(char *fifo_name,int pid)
 77     {
 78         int n,fd;
 79         char buf[MAX];
 80 
 81         if((fd = open(fifo_name,O_WRONLY)) < 0)
 82         {
 83             fprintf(stderr,"Fail to open %s : %s.\n",fifo_name,strerror(errno));
 84             return -1;
 85         }
 86 
 87         signal(SIGINT,SIG_IGN);
 88 
 89         while(1)
 90         {
 91             getchar();
 92             printf(">");
 93 
 94             fgets(buf,sizeof(buf),stdin);
 95             buf[strlen(buf)-1] = '\0';
 96 
 97             write(fd,buf,strlen(buf));
 98 
 99             if(strncmp(buf,"quit",4) == 0)
100             {
101                 kill(pid,SIGKILL);
102                 usleep(500);
103                 exit(EXIT_SUCCESS);
104             }
105         }
106 
107         return 0;
108     }
109 
110     int main(int argc,char *argv[])
111     {
112         int pid;
113 
114         if(argc < 3)
115         {
116             fprintf(stderr,"usage %s argv[1].\n",argv[0]);
117             exit(EXIT_FAILURE);
118         }
119 
120         if(mkfifo(argv[1],0666) < 0 && errno != EEXIST)
121         {
122             perror("Fail to mkfifo");
123             exit(EXIT_FAILURE);
124         }
125 
126         if(mkfifo(argv[2],0666) < 0 && errno != EEXIST)
127         {
128             perror("Fail to mkfifo");
129             exit(EXIT_FAILURE);
130         }
131         
132         if((pid = fork()) < 0)
133         {
134         
135             perror("Fail to fork");
136             exit(EXIT_FAILURE);
137         
138         }else if(pid == 0){
139             
140             child_recv_fifo(argv[1]);
141         
142         }else{
143 
144             father_send_fifo(argv[2],pid);
145         }
146 
147         exit(EXIT_SUCCESS);
148     }
View Code

 

 

D.将进程挂起函数pause



解释如下:

  pause()会令目前的进程暂停(进入睡眠状态),直到被信号signal中断。

 
posted @ 2013-10-04 16:39  kaijia9  阅读(236)  评论(0编辑  收藏  举报