Linux 系统应用编程——进程间通信(上)

    现在再Linux应用较多的进程间通信方式主要有以下几种:

1)无名管道(pipe)及有名管道(fifo):无名管道可用于具有亲缘关系进程间的通信;有名管道除具有管道相似的功能外,它还允许无亲缘关系进程使用;

2)信号(signal):信号是在软件层次上对中断机制的一种模拟,它是比较复杂的通信方式,用于通知进程某事件发生。一个进程收到一个信号与处理器收到一个中断请求处理的过程类似;

3)消息队列(message queue):消息队列是消息的链接表,包括POSIX消息队列和System V 消息队列。它克服了前两种通信方式中信息量有限的缺点。具有写权限的进程可以按照一定的规则向消息队列中添加新消息;对消息队列有读权限的进程则可以从消息队列中读取消息。

4)共享内存(shared memory):可以说这时最有效的进程间通信方式。它使得多个进程可以访问同一块内存空间,不同进程可以及时查看对方进程中对共享数据的更新。这种通信方式需要依靠某种同步机制,如互斥锁和信号量等。

5)信号量(semaphore):主要作为进程之间以及统一进程的不同线程之间的同步和互斥手段。

6)套接字(socket):这时一种使用更广泛的进程间通信机制,它可用于网络中不同主机之间的进程间通信,应用非常广泛。


管道通信

        管道是Linux 中进程间通信的一种方式,它把一个程序的输出直接连接到另一个程序的输入,Linux 的管道主要包括两种:无名管道和有名管道。

一、无名管道

      无名管道是Linux中管道通信的一种原始方法,他有如下特点:

1)只能用于具有亲缘关系的进程之间的通信(也就是父子进程或兄弟进程之间);

2)是一个单工的通信模式,具有固定的读端和写端;

3)管道也可以看成一种特殊的文件,对于它的读写也可是使用普通的read() 、write()等函数,但是它不属于任何文件系统,并且只存在于内存中;(其字节大小为0)


1、无名管道的创建与关闭

       无名管道是基于文件描述符的通信方式。当一个管道创建时,它会创建两个文件描述符:fd[0] 、fd[1] 。其中 fd[0] 固定用于读管道,而 fd[1] 固定用于写管道,如下图,这样就构成了一个单向的数据通道:


管道关闭时只需要用 close() 函数将这两个文件描述符关闭即可。


2、管道创建函数

      创建管道可以通过 pipe() 来实现,其语法如下:

所需头文件#include <unistd.h>
函数原型int pipe(int fd[]);
函数传入值fd :包含两个元素的整型数组,存放管道对应的文件描述符
函数返回值成功:0
出错:-1

3、管道读写说明

       用pipe() 函数创建的管道两端处于一个进程中。由于管道主要是用于不同进程间的通信,通常是先创建一个管道,再调用 fork () 函数创建一个子进程,该子进程会继承父进程所创建的管道。

         需要注意的是,无名管道是单工的工作方式,即进程要么只能读管道,要么只能写管道。父子进程虽然都拥有管道的读端和写端,但是只能使用其中一个(例如,可以约定父进程读管道,而子进程写管道)。这样就应该把不使用的读端或写端文件描述符关闭。


例如:如果将父进程的写端 fd[1] 和子进程的读端 fd[0] 关闭。此时,父子进程之间就建立了一条“子进程写入 父进程读取”的通道。同样,也可以关闭父进程的 fd[0] 和子进程的fd[1] ,这样就可以建立一条“父进程写入子进程读取”的通道。另外,父进程也可以创建 多个子进程,各个子进程都继承了管道的fd[0] 和 fd[1] ,这样就建立子进程之间的数据通道。


4、管道读写注意:

1)只有管道的读端存在时,向管道写入数据才有意义,否则,向管道中写入数据的进程将收到内核传来的 SIGPIPE 信号 (通常为Broken Pipea错误)。

2)向管道写入数据时,Linux 将不保证写入的原子性 , 管道缓冲区只要有空间,写进程就会试图向管道写入数据。如果管道缓冲区已满,那么写操作将一直阻塞。

3)父进程在运行时,它们的先后次序必不能保证。为了确保父子进程已经关闭了相应的文件描述符,可在两个进程中调用 sleep() 函数,当然,用互斥和同步会更好;


下面是一个实例:

[cpp] view plaincopy
  1. #include <stdio.h>  
  2. #include <unistd.h>  
  3. #include <string.h>  
  4. #include <stdlib.h>  
  5.   
  6. int pid,pid1,pid2;  
  7.   
  8. int main(int argc, const char *argv[])  
  9. {  
  10.     int fd[2];  
  11.     char outpipe[100],inpipe[100];  
  12.   
  13.     if(pipe(fd) < 0)  
  14.     {  
  15.         perror("pipe error!");  
  16.         return -1;  
  17.     }  
  18.   
  19.     if((pid1 = fork()) < 0)  
  20.     {  
  21.         perror("fork pid1 error");  
  22.         return -1;  
  23.     }  
  24.     else if(pid1 == 0)  
  25.     {  
  26.         printf("Child1's pid is %d\n",getpid());  
  27.         close(fd[0]);  
  28.         strcpy(outpipe,"Child 1 is sending a message!");  
  29.         if(write(fd[1],outpipe,50) == -1)  
  30.         {  
  31.             perror("Child 1 write to outpipe error");  
  32.             return -1;  
  33.         }  
  34.         exit(0);  
  35.     }  
  36.           
  37.     if((pid2 = fork()) < 0)  
  38.     {  
  39.         perror("fork pid2 error");  
  40.         return -1;  
  41.     }  
  42.     else if(pid2 == 0)  
  43.     {  
  44.         printf("Child2's pid is %d\n",getpid());  
  45.         close(fd[0]);  
  46.         strcpy(outpipe,"Child 2 is sending a message!");  
  47.         sleep(1);  
  48.         if(write(fd[1],outpipe,50) == -1)  
  49.         {  
  50.             perror("Child 2 write to outpipe error");  
  51.             return -1;  
  52.         }  
  53.         exit(0);  
  54.     }  
  55.       
  56.     close(fd[1]);  
  57.     pid = wait(NULL);  
  58.     printf("%d process is over!\n",pid);  
  59.     if(read(fd[0],inpipe,50) == -1)  
  60.     {  
  61.         perror("read Child1 pipe error");  
  62.         return -1;  
  63.     }  
  64.     printf("%s\n",inpipe);  
  65.   
  66.     pid = wait(NULL); //回收第二个结束的子进程  
  67.     printf("%d process is over!\n",pid);  
  68.     if(read(fd[0],inpipe,50) == -1)  
  69.     {  
  70.         perror("read Child1 pipe error");  
  71.         return -1;  
  72.     }  
  73.     printf("%s\n",inpipe);  
  74.   
  75.     return 0;  
  76. }  

执行结果如下:

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/pipe$ ./pipe  
  2. Child2's pid is 8504  
  3. Child1's pid is 8503  
  4. 8503 process is over!  
  5. Child 1 is sending a message!  
  6. 8504 process is over!  
  7. Child 2 is sending a message!  
  8. fs@ubuntu:~/qiang/pipe$   


二、有名管道

         有名管道(FIFO)是对无名管道的一种改进,它具有如下特点:

1)它可以使互不相关的两个进程实现彼此通信;

2)该管道可以通过路径名来指出,并且在文件系统中是可见的。在建立了管道之后,两个进程就可以把它当做普通文件一样进行读写操作,使用非常方便;

3)FIFO严格地遵循先进先出规则,对管道及 FIFO 的读总是从开始处返回数据,对它们的写则把数据添加到末尾。有名管道不支持如lseek()等文件定位操作;

        有名管道(FIFO)的创建可以使用 mkfifo() 函数,该函数类似文件中的open() 操作,可以指定管道的路径和访问权限 (用户也可以在命令行使用 “mknod <管道名>”来创建有名管道)。

        在创建管道成功以后,就可以使用open()、read() 和 write() 这些函数了。与普通文件一样,对于为读而打开的管道可在 open() 中设置 O_RDONLY,对于为写而打开的管道可在 open() 中设置O_WRONLY。

1、对于读进程

     缺省情况下,如果当前FIFO内没有数据,读进程将一直阻塞到有数据写入或是FIFO写端都被关闭。

2、对于写进程

    只要FIFO有空间,数据就可以被写入。若空间不足,写进程会阻塞,知道数据都写入为止;

mkfifo() 函数语法如下:

所需头文件#include <sys/types.h>
#include <sys/state.h>
函数原型 int mkfifo( const char *filename,mode_t mode)
参数mode:管道的访问权限
函数返回值成功:0
出粗:-1

下面是个实例,来学习有名管道的使用

create.c

[cpp] view plaincopy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <sys/stat.h>  
  4. #include <string.h>  
  5. #include <errno.h>  
  6.   
  7. int main(int argc,char *argv[])  
  8. {  
  9.     if(argc < 2)  
  10.     {  
  11.         printf("Usage:%s <filename>",argv[0]);  
  12.         return -1;  
  13.     }  
  14.   
  15.     if(mkfifo(argv[1],0664) < 0)  
  16.     {  
  17.         perror("mkfifo fails");  
  18.         exit(-1);  
  19.     }  
  20.   
  21.     return 0;  
  22. }  
write_fifo.c
[cpp] view plaincopy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <string.h>  
  4. #include <unistd.h>  
  5. #include <sys/stat.h>  
  6. #include <fcntl.h>  
  7. #include <errno.h>  
  8.   
  9. #define BUFFER_SIZE 1024  
  10.   
  11. int main(int argc, const char *argv[])  
  12. {  
  13.     int fd;  
  14.   
  15.     if(argc < 2)  
  16.     {  
  17.         printf("Usage:%s <filename>",argv[0]);  
  18.         return -1;  
  19.     }  
  20.   
  21.     if((fd = open(argv[1],O_WRONLY)) < 0)  
  22.     {  
  23.         perror("open error");  
  24.         exit(-1);  
  25.     }  
  26.   
  27.     printf("open fifo %s for writing success!\n",argv[0]);  
  28.     char buffer[BUFFER_SIZE];  
  29.     ssize_t n;  
  30.   
  31.     while(fgets(buffer,BUFFER_SIZE,stdin))  
  32.     {  
  33.         if((n = write(fd,buffer,strlen(buffer))) == -1)  
  34.         {  
  35.             perror("write fails");  
  36.             break;  
  37.         }  
  38.     }  
  39.     return 0;  
  40. }  
read_fifo.c
[cpp] view plaincopy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <string.h>  
  4. #include <unistd.h>  
  5. #include <sys/stat.h>  
  6. #include <fcntl.h>  
  7. #include <errno.h>  
  8.   
  9. #define BUFFER_SIZE 1024  
  10.   
  11. int main(int argc, const char *argv[])  
  12. {  
  13.     int fd;  
  14.   
  15.     if(argc < 2)  
  16.     {  
  17.         printf("Usage:%s <filename>",argv[0]);  
  18.         return -1;  
  19.     }  
  20.   
  21.     if((fd = open(argv[1],O_RDONLY)) < 0)  
  22.     {  
  23.         perror("open error");  
  24.         exit(-1);  
  25.     }  
  26.   
  27.     printf("open fifo %s for reading success!\n",argv[0]);  
  28.     char buffer[BUFFER_SIZE];  
  29.     ssize_t n;  
  30.   
  31.     while(1)  
  32.     {  
  33.         if((n = read(fd,buffer,BUFFER_SIZE)) == -1)  
  34.         {  
  35.             perror("read fails");  
  36.             return -1;  
  37.         }  
  38.         else if(n == 0)  
  39.         {  
  40.             printf("peer close fifo\n");  
  41.             break;  
  42.         }  
  43.         else  
  44.         {  
  45.             buffer[n] = '\0';  
  46.             printf("read %d bytes from fifo:%s\n",n,buffer);  
  47.         }  
  48.     }  
  49.     return 0;  
  50. }  

执行结果如下:

写端:

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/fifo$ ./create_fifo tmp  
  2. fs@ubuntu:~/qiang/fifo$ ./write_fifo tmp  
  3. open fifo ./write_fifo for writing success!  
  4. xiao  
  5. zhi  
  6. qiang  
  7. ^C  
  8. fs@ubuntu:~/qiang/fifo$   

读端:

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/fifo$ ./read_fifo tmp  
  2. open fifo ./read_fifo for reading success!  
  3. read 5 bytes from fifo:xiao  
  4.   
  5. read 4 bytes from fifo:zhi  
  6.   
  7. read 6 bytes from fifo:qiang  
  8.   
  9. peer close fifo  
  10. fs@ubuntu:~/qiang/fifo$   

这里执行时,可以看到,单独打开读或写,二者会一直阻塞,直到都打开,才会打印第一句话,当写端关闭时,读端也会停止。


三、信号通信

        信号是在软件层次上对中断机制的一种模拟。在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是异步的:一个进程不必通过任何操作在等待信号的到达。事实上,进程也不知道信号到底什么时候到达。事实上,进程也不知道信号到底什么时候到达。信号可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用它来通知用户空间进程发生了那些系统事件。它可以在任何时候发给某一进程,而无需知道该进程的状态。如果该进程当前并未处于执行态,则该信号就由内核保存起来,知道该进程回恢复行再传递给它为止;如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直道阻塞被取消时才被传递给进程。


1、信号的生存周期:



2、进程可以通过3种方式来响应一个信号

1)忽略信号

即对信号不做任何处理,其中,有两个信号不能忽略:SIGKILL及 SIGSTOP;

2)捕捉信号

定义信号处理函数,当信号发生时,执行相应的处理函数。

3)执行默认操作

Linux 对每种信号都规定了默认操作;(后面会给出信号列表)

这里介绍几个常用的信号

信号名含义默认操作
SIGINT该信号在用户输入INTR字符(通常是Ctrl + C)时发出,
终端驱动程序发送该信号并送到前台进程中的每一个进程
终止进程
SIGQUIT该信号和SIGINT类似,但由QUIT字符(通常是Ctrl + \)来
控制
终止进程
SIGKILL该信号用来立即结束程序的运行;
不能被阻塞、处理和忽略;
终止进程
SIGALARM该信号当一个定时器到时的时候发出;终止进程
SIGSTOP该信号用于暂停一个进程;
不能被阻塞、处理和忽略;
暂停进程
SIGTSTP该信号用于交互停止进程(挂起),由Ctrl + Z 来发出终止进程

3、信号处理流程


下面是内核如何实现信号机制,即内核如何向一个进程发送信号、进程如何接收一个信号、进程怎样控制自己对信号的反应、内核在什么实际处理和怎样处理进程收到的信号。

内核对信号的基本处理方法

       内核给一个进程发送软中断信号的方法是,进程所在的进程表项的信号域设置对于该信号的位(内核通过在进程的 struct task_struct 结构中的信号域中设置相应的位来实现向一个进程发送信号)。这里要补充的是,如果信号发送给一个正在睡眠的进程,那么要看该进程进入睡眠的优先级,如果进程睡眠在可被中断的优先级上,则唤醒进程;否则仅设置进程表中信号域相应的位,而不唤醒进程。这一点比较重要,因为进程检查是否收到信号的时机是一个进程在即将从内核态返回到用户态时;或者,在一个进程要进入或离开一个适当的低调度优先级睡眠状态时。

       内核处理一个进程收到的信号的时机是一个进程从内核态返回用户态时。所以,当一个进程在内核态运行时,软中断信号并不立即起作用要等到将返回用户态时才处理。进程只有处理完信号才会返回用户态,进程在用户态下不会有未处理完的信号。

       内核处理一个进程收到的软中断信号是在该进程的上下文中,因此,进程必须处于运行状态。处理信号有三种类型:进程接收到信号后退出;进程忽略该信号;进程收到信号后执行用户自定义的使用系统调用signal() 注册的函数。当进程接收到一个它忽略的信号时,进程丢弃该信号,就像从来没有收到该信号似得,而继续运行。如果进程收到一个要捕捉的信号,那么进程从内核态返回用户态时执行用户定义的函数。而且执行用户定义的函数的方法很巧妙,内核是在用户栈上创建一个新的层,该层中将返回地址的值设置成用户定义的处理函数的地址,这样进程从内核返回弹出栈顶时就返回到用户定义的处理函数处,从函数返回再弹出栈顶时,才返回原来进入内核的地方。这样做的原因是用户定义的处理函数不能且不允许在内核态下执行(如果用户定义的函数在内核态下运行的话,用户就可以获得任何权限)。

       在信号的处理方法中有几点特别要引起注意:

1)在一些系统中,当一个进程处理完中断信号返回用户态之前,内核清除用户区中设定的对该信号的处理例程的地址,即下一次进程对该信号的处理方法又改为默认值,除非在下一次信号到来之前再次调用 signal() 系统调用。这可能会使得进程在调用 signal() 之前又得到该信号而导致退出。在BSD系统中,内核不再清除该地址。但不清楚该地址可能使得进程因为过多过快的得到某个信号而导致堆栈溢出。为了避免出现上述情况。在BSD中,内核模拟了对硬件中断的处理方法,即在处理某个中断时,阻止接收新的该类中断。


4、信号相关函数

1)信号发送:kill() 和 raise()

       kill() 函数同读者熟知的kill 系统命令一样,可以发送信号给进程或进程组(实际上,kill 系统命令就是由 kill () 函数实现的)。需要注意的是,它不仅可以终止进程,也可以向进程发送其他信号;

        与kill() 函数不同的是,raise() 函数只允许进程向自身发送信号;

kill() 函数语法

所需头文件#include <signal.h>
#include <sys/types.h>
函数原型int kill(pid_t pid,int sig);
函数传入值pid 为正数: 发送信号给进程号为pid 的进程
pid 为  0    : 信号被发送到所有和当前进程在同一个进程组的进程
pid 为 -1    :信号发送给所有进程表中的进程(除了进程号最大的进程外)
pid 为 < -1 :信号发送给进程组号为 -pid 的每一个进程  

sig :信号类型
函数返回值成功 :0
出错: -1

raise() 函数的语法

所需头文件#include <signal.h>
#include <sys/types.h>
函数原型int raise(int sig);
函数传入值sig :信号类型
函数返回值成功:0
出错: -1

这里 raise() 等价于 kill ( getpid() , sig) ;

下面举一个实例:

[cpp] view plaincopy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <signal.h>  
  4. #include <sys/types.h>  
  5. #include <sys/wait.h>  
  6. #include <unistd.h>  
  7.   
  8. int main(int argc, char *argv[])  
  9. {  
  10.     pid_t pid;  
  11.     int ret;  
  12.   
  13.     if((pid = fork()) < 0)  
  14.     {  
  15.         perror("fork error");  
  16.         exit(-1);  
  17.     }  
  18.       
  19.     if(pid == 0)  
  20.     {  
  21.         printf("child(pid : %d)is waiting for any signal\n",getpid());  
  22.         raise(SIGSTOP);  
  23.         exit(0);  
  24.     }  
  25.       
  26.     sleep(1);  
  27.     if((waitpid(pid,NULL,WNOHANG)) == 0)  
  28.     {  
  29.         kill(pid,SIGKILL);  
  30.         printf("parent kill child process %d\n",pid);     
  31.     }  
  32.   
  33.     waitpid(pid,NULL,0);  
  34.     return 0;  
  35. }  
执行结果如下:
[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/signal$ ./kill  
  2. child(pid : 9977)is waiting for any signal  
  3. parent kill child process 9977  
  4. fs@ubuntu:~/qiang/signal$   


2)、定时器信号:alarm() 、pause()

        alarm() 也称闹钟信号,它可以在进程中设置一个定时器。当定时器指定的时间到时,它就向进程发送SIGALRAM信号。要注意的是,一个进程只能有一个闹钟时间,如果在调用alarm()函数之前已设置过闹钟信号,则任何以前的闹钟时间都被新值所代替。

       pause()函数是用于将调用进程挂起直至收到信号为止

alarm()函数语法:

所需头文件#include <unistd.h>
函数原型unsigned int alarm(unsigned int second);
函数传入值seconds:指定秒数,系统经过seconds秒之后向该进程发送SIGALARM信号
函数返回值成功:如果调用次alarm()前,进程中已经设置了闹钟时间,
            则返回上一个闹钟剩余的时间,否则返回 0;
出错: -1

pause() 函数语法

所需头文件#include <unistd.h>
函数原型int pause(void);
函数返回值-1;并且把 errno值设为RINTR

下面一个实例,完成一个简单的sleep() 函数的功能,由于SIGALARM 默认的系统动作为终止该进程,因此程序在打印信息之前就已经结束了

执行结果如下:

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/signal$ ./alarm   
  2. Alarm clock  
  3. fs@ubuntu:~/qiang/signal$   

可以看到printf() 里面的内容并没有被打印, Alarm clock 是SIGALARM信号默认处理函数打印。


3)、信号的设置 signal() 和 sigaction() 

         signal() 函数

        要对一个信号进行处理,就需要给出此信号发生时系统所调用的处理函数。可以为一个特定的信号(除去无法捕捉的SIGKILL和SIGSTOP信号)注册相应的处理函数。如果正在运行的程序源代码里注册了针对某一特定信号的处理程序,不论当时程序执行到何处,一旦进程接收到该信号,相应的调用就会发生。

        signal()函数使用时,只需要指定的信号类型和信号处理函数即可。它主要用于前32种非实时信号的处理,不支持信号传递信息。

其语法格式如下:

所需头文件#include <signal.h>
函数原型typeef void (*sighandle_t)(int) ; 函数指针类型
sighandle_t signal(int signum,sighandle_t handler);
函数传入值signum:指定信号代码
Handler:SIG_IGN:忽略该信号
                  SIG_DFL:采用系统默认方式处理信号
                  自定义的信号处理函数;
函数返回值成功:以前的信号处理函数
出错:-1

该函数第二个参数和返回值类型都是指向一个无返回值并且带一个整型参数的函数的指针;且只要signal()  调用了自定义的信号处理函数,即使这个函数什么也不做,这个进程也不会被终止;


下面一个程序利用signal来实现发送信号和接受信号的原理:

程序内容:创建子进程代表售票员,父进程代表司机,同步过程如下:

售票员捕捉 SIGINT(代表开车),发送信号SIGUSR1给司机,司机打印(“let's gogogo!”);

售票员捕捉 SIGQUIT(代表停止),发送信号SIGUSR2给司机,司机打印(“stop the bus!”);

司机捕捉 SIGTSTP (代表车到总站),发SIGUSR1给售票员,售票员打印(“Please get off the bus”);

代码如下:

[cpp] view plaincopy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <unistd.h>  
  4. #include <signal.h>  
  5. #include <sys/types.h>  
  6.   
  7. pid_t pid;  
  8. void driver_handler(int signo);  
  9. void saler_handler(int signo);  
  10.   
  11. int main(int argc,char *argv[])  
  12. {  
  13.     if((pid = fork()) < 0)  
  14.     {  
  15.         perror("fork error");  
  16.         return -1;  
  17.     }  
  18.   
  19.     if(pid > 0)  
  20.     {  
  21.         signal(SIGTSTP,driver_handler);  
  22.         signal(SIGINT,SIG_IGN);  
  23.         signal(SIGQUIT,SIG_IGN);  
  24.         signal(SIGUSR1,driver_handler);  
  25.         signal(SIGUSR2,driver_handler);  
  26.   
  27.         while(1)  
  28.             pause();  
  29.     }  
  30.   
  31.     if(pid == 0)  
  32.     {  
  33.         signal(SIGINT,saler_handler);  
  34.         signal(SIGTSTP,SIG_IGN);  
  35.         signal(SIGQUIT,saler_handler);  
  36.         signal(SIGUSR1,saler_handler);  
  37.         signal(SIGUSR2,SIG_IGN);  
  38.   
  39.         while(1)  
  40.             pause();  
  41.     }  
  42.     return 0;  
  43. }  
  44.   
  45. void driver_handler(int signo)  
  46. {  
  47.     if(signo == SIGUSR1)  
  48.         printf("Let's gogogo!\n");  
  49.     if(signo == SIGUSR2)  
  50.         printf("Stop the bus!\n");  
  51.     if(signo == SIGTSTP)  
  52.         kill(pid,SIGUSR1);  
  53. }  
  54.   
  55. void saler_handler(int signo)  
  56. {  
  57.     pid_t ppid = getppid();  
  58.     if(signo == SIGINT)  
  59.         kill(ppid,SIGUSR1);  
  60.     if(signo == SIGQUIT)  
  61.         kill(ppid,SIGUSR2);  
  62.     if(signo == SIGUSR1)  
  63.     {  
  64.         printf("please get off the bus\n");  
  65.         kill(ppid,SIGKILL);  
  66.         exit(0);  
  67.     }  
  68. }  

执行结果如下:

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/signal$ ./signal   
  2. ^CLet's gogogo!  
  3. ^\Stop the bus!  
  4. ^CLet's gogogo!  
  5. ^\Stop the bus!  
  6. ^CLet's gogogo!  
  7. ^\Stop the bus!  
  8. ^CLet's gogogo!  
  9. ^\Stop the bus!  
  10. ^Zplease get off the bus  
  11. Killed  
  12. fs@ubuntu:~/qiang/signal$   

sigaction() 函数

sigaction() 函数的功能是检查或修改(或两者)与指定信号相关联的处理动作,此函数可以完全代替signal 函数。

函数原型如下:

所需头文件#include <signal.h>
函数原型int sigaction(int signum,  const struct sigaction *act ,
                                                       struct sigaction *oldact );
函数传入值signum:可以指定SIGKILL和SIGSTOP以外的所有信号
act        :act 是一个结构体,里面包含信号处理函数的地址、
                  处理方式等信息;
oldact  :参数oldact 是一个传出参数,sigaction 函数调用成功后,
                  oldact 里面包含以前对 signum 信号的处理方式的信息;
函数返回值成功:0
出错:-1

其中参数signo 是要检测或修改其具体动作的信号编号。若act 指针非NULL,则要修改其动作。如果oact 指针非空,则系统经由 oact 指针返回该信号的上一个动作;

参数结构sigaction定义如下:

[cpp] view plaincopy
  1. struct sigaction  
  2. {  
  3.     void (*sa_handler) (int);  
  4.     void (*sa_sigaction)(int, siginfo_t *, void *);  
  5.     sigset_t sa_mask;  
  6.     int sa_flags;  
  7.     void (*sa_restorer) (void);  
  8. }  
①  sa_handler:此参数和signal()的参数handler相同,此参数主要用来对信号旧的安装函数signal()处理形式的支持;

②  sa_sigaction:新的信号安装机制,处理函数被调用的时候,不但可以得到信号编号,而且可以获悉被调用的原因以及产生问题的上下文的相关信息。

③  sa_mask:用来设置在处理该信号时暂时将sa_mask指定的信号搁置;

④  sa_restorer: 此参数没有使用;

⑤  sa_flags:用来设置信号处理的其他相关操作,下列的数值可用。可用OR 运算(|)组合:

ŸA_NOCLDSTOP:如果参数signum为SIGCHLD,则当子进程暂停时并不会通知父进程
SA_ONESHOT/SA_RESETHAND:当调用新的信号处理函数前,将此信号处理方式改为系统预设的方式
SA_RESTART:被信号中断的系统调用会自行重启
SA_NOMASK/SA_NODEFER:在处理此信号未结束前不理会此信号的再次到来
SA_SIGINFO:信号处理函数是带有三个参数的sa_sigaction。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/402351.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

通过JDBK操作数据库

一、配置程序——让我们程序能找到数据库的驱动jar包1.把.jar文件复制到项目中去,整合的时候方便。2.在eclipse项目右击“构建路径”--“配置构建路径”--“库”--“添加外部jar”--找到数据库的驱动jar包--点击确定。会在左侧包资源管理器中出现“引用的库”&#xff0c;在里面…

Linux 系统应用编程——网络编程(常用命令解析)

1、telnet Telnet协议是TCP/IP协议族中的一员&#xff0c;是Internet远程登陆服务的标准协议和主要方式。它为用户提供了在本地计算机上完成远程主机工作的能力。在终端使用者的电脑上使用telnet程序&#xff0c;用它连接到服务器。终端使用者可以在telnet程序中输入命令&#…

灾难 BZOJ 2815

灾难 【样例输入】 5 0 1 0 1 0 2 3 0 2 0 【样例输出】 4 1 0 0 0 题解&#xff1a; 先跑出拓扑序 我们按拓扑序建立一棵“灭绝树” 灭绝树含义是当一个点灭绝时&#xff0c;它的子树将会全部灭绝 所以答案就是点在灭绝树中的子树大小 一个点如果灭绝&#xff0c;那么需要所有…

centos关于”running yum-complete-transaction first...

2019独角兽企业重金招聘Python工程师标准>>> 今天在用yum安装软件出错几次户&#xff0c;总是有提示信息&#xff1a; There are unfinished transactions remaining. You might consider running yum-complete-transaction first to finish them. The program yum…

js身份证号、电话脱敏处理(用*替换中间数据)

数字类型 certificatecodecopy certificatecode.replace(/^(.{6})(?:\d)(.{4})$/, "\$1****\$2"); 所有类型 enginenocopy engineno.replace(/^(.{2})(?:\w)(.{1})$/, "\$1****\$2"); enginenocopy engineno.replace(/^(.{4})(?:\w)(.{4})$/, &…

Linux 系统应用编程——网络编程(I/O模型)

Unix下可用的5种I/O模型&#xff1a;阻塞I/O非阻塞I/OI/O复用(select和poll)信号驱动I/O(SIGIO)异步I/O(POSIX的aio_系列函数)一个输入操作通常包括两个不同的阶段&#xff1a;1&#xff09;等待数据准备好&#xff1b;2&#xff09;从内核向进程复制数据&#xff1b;对于一个套…

Linux 系统应用编程——网络编程(TCP/IP 数据包格式解析)

图中括号中的数字代表的是当前域所占的空间大小&#xff0c;单位是bit位。 黄色的是数据链路层的头部&#xff0c;一共14字节 绿色的部分是IP头部&#xff0c;一般是20字节 紫色部分是TCP头部&#xff0c;一般是20字节 最内部的是数据包内容 黄色部分&#xff1a;链路层 目的MA…

Linux 系统应用编程——网络编程(TCP 协议三次握手过程)

TCP(Transmission Control Protocol) 传输控制协议 TCP是主机对主机层的传输控制协议&#xff0c;提供可靠的连接服务&#xff0c;采用三次握手确认建立一个连接: 位码即tcp标志位,有6种标示: SYN ( synchronous 建立联机 ) ACK ( acknowledgement 确认 ) PSH ( push 传送…

基于ELK的简单数据分析

原文链接&#xff1a; http://www.open-open.com/lib/view/open1455673846058.html 环境 CentOS 6.5 64位JDK 1.8.0_20Elasticsearch 1.7.3LogStash 1.5.6Kibana 4.1.4介绍 ElasticSearch是有名的开源搜索引擎&#xff0c;现在很多公司使用ELK技术栈做日志分析&#xff0c;比如…

[win10] 在桌面上显示计算机、控制面板、网络

1. 右击桌面&#xff0c;选择个性化 2. 选择”主题", 点击“桌面图标设置” 3. 把想要放桌面的图标给钩上

linux下共享文件夹(windows可访问,linux也可访问)

2019独角兽企业重金招聘Python工程师标准>>> 本文是转字网上的两段&#xff0c;如果是菜鸟&#xff0c;想懂有点难度&#xff0c;我这里给点注释 在linux上共享文件夹windows下看 ******************************************* 首先给linux设一个ip&#xff0c;要和…

Linux---进程调度相关命令解析

进程相关命令 1、ps 查看系统中的进程 使用方式&#xff1a;ps [options] [--help] 说明&#xff1a;显示瞬间进程 (process) 的动态 参数&#xff1a;ps的参数非常多, 在此仅列出几个常用的参数并大略介绍含义 ps命令常用用法&#xff08;方便查看系统进程&#xff09; 1&a…

Linux 系统应用编程——多线程经典问题(生产者-消费者)

“生产者——消费者”问题是Linux多线程编程中的经典问题&#xff0c;主要是利用信号量处理线程间的同步和互斥问题。 “生产者——消费者”问题描述如下&#xff1a; 有一个有限缓冲区&#xff08;这里用有名管道实现 FIFO 式缓冲区&#xff09;和两个线程&#xff1a;生产者和…

Linux 系统应用编程——进程间通信(下)

在前面&#xff0c;我们学习了传统的进程间通信方式——无名管道&#xff08;pipe&#xff09;、有名管道&#xff08;fifo&#xff09;和信号&#xff08;signal&#xff09;。 下面我们来学习 System V IPC 对象&#xff1a; 1、共享内存&#xff08;share memory&#xff0…

.balignl 16,0xdeadbeef浅析

http://zqwt.012.blog.163.com/blog/static/12044684201031102956976/ 最近在分析u-boot的源代码&#xff0c;看到这一行&#xff1a; .balignl 16, 0xdeadbeef不知道为什么要这样写&#xff0c;0xdeadbeef&#xff0c;明显是个单词组&#xff0c;写在这里有何意义呢&am…

使用maven导入任意jar包

http://mvnrepository.com/ 我这里&#xff0c;因为是spark1.5.2版本。 保存&#xff0c;maven会自动下载jar包到本地仓库。 转载于:https://www.cnblogs.com/lchzls/p/6281764.html

Linux下静态IP地址的设置及TFTP服务的搭建

TFTP&#xff08;Trivial File Transfer Protocol,简单文件传输协议&#xff09;是TCP/IP协议族中的一个用来在客户机与服务器之间进行简单文件传输的协议&#xff0c;提供不复杂、开销不大的文件传输服务。TFTP承载在UDP上&#xff0c;提供不可靠的数据流传输服务&#xff0c;…

bzoj 3924 幻想乡战略游戏

题目大意&#xff1a; 有边权点权的树&#xff0c;动态修改点权 每次修改后求带权重心x (\(minimize\) \(S\sum_i val[i]*dist[x][i]\)) 分析&#xff1a; 从暴力找突破口&#xff1a; 对于边x,y&#xff0c;设长度为len&#xff0c;切断后x半边树权值和为\(w_1\)&#xff0c;y…

Linux 系统应用编程——网络编程(基础篇)

一、网络体系结构 1、OSI模型和TCP/IP 模型 网络体系结构指的是网络的分层结构以及每层使用的协议的集合。其中最著名的就是OSI协议参考模型&#xff0c;他是基于国际标准化组织&#xff08;OSI&#xff09;的建议发展起来的。它分为7个层次&#xff1a;应用层、表示层、会话层…

C++中函数的默认参数

使用方法&#xff1a; &#xff08;1&#xff09;在函数声明或定义时&#xff0c;直接对参数赋值&#xff0c;该参数就是默认参数。&#xff08;2&#xff09;在函数调用时&#xff0c;省略部分或全部参数&#xff0c;这时就会使用默认参数进行代替。注意事项&#xff1a; &…