转:步步LINUX C--进程间通信(二)信号

折月煮酒 提交于 2020-01-01 00:25:11

源地址:http://blog.csdn.net/jmy5945hh/article/details/7529651

linux间进程通信的方法在前一篇文章中已有详细介绍。http://blog.csdn.net/jmy5945hh/article/details/7350564

 

本篇详细介绍及代码测试第二种方式,即信号(Signal)。

 

1 信号简介

信号全称为软中断信号,主要用于进程控制。

信号是进程间通信机制中唯一的异步通信机制,可以看作是异步通知。信号机制经过POSIX实时扩展后,功能更加强大,除了基本通知功能外,还可以传递附加信息。

信号的来源包括硬件来源与软件来源。

 

生存周期从被创建开始,到进程接收到信号。某些情况下允许信号排队。

内核对信号的处理机制:

(1)发送信号的方法为在进程所在的进程表项的信号域设置对应于该信号的位。

(2)睡眠进程的睡眠优先级高于信号时,信号不会唤醒进程。

(3)内核处理信号的时机是进程从内核态返回用户态时或者进程要切换到低优先级睡眠态时。

(4)处理方法:退出,忽略或通过signal函数调用用户定义函数处理。

典型应用:LINUX下程序在命令行运行时,按下CTRL+C将向程序发送SIGINT信号,强制进程结束。

 

在命令行使用kill -l命令可以列出所有LINUX系统支持的信号

 

值得注意的是,信号的编号不是连续的。在SIGRTMIN之前的信号被称为不可靠信号,之后的称为可靠信号

主要的区别在于,不可靠信号:

1/进程每次处理信号后,就将对信号的响应设置为默认动作。在某些情况下,将导致对信号的错误处理;因此,用户如果不希望这样的操作,那么就要在信号处理函数结尾再一次调用signal(),重新安装该信号。

2/不支持排队,信号可能丢失。 因此,早期unix下的不可靠信号主要指的是进程可能对信号做出错误的反应以及信号可能丢失。

 

2 信号的处理

注册处理函数

 

  1. #include <signal.h>   
  2. void (*signal (int signum, void (*handler) (int) ) ) (int);  
  3. /* 等效形式 */  
  4. typedef void(*sighandler_t) (int);  
  5. sighandler_t signal(int signum, sighandler_t handler);  
#include <signal.h>
void (*signal (int signum, void (*handler) (int) ) ) (int);
/* 等效形式 */
typedef void(*sighandler_t) (int);
sighandler_t signal(int signum, sighandler_t handler);


成功返回之前的处理信号配置;出错返回SIG_ERR。

 

不能捕捉SIGKILL或者SIGSTOP信号。handler取值为SIG_IGN表示忽略,为SIG_DFL表示执行系统默认操作,为函数名时表示执行特定操作。

 

处理函数增强版

 

  1. #include <signal.h>   
  2. int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);  
#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);


成功后返回0;出错返回-1。

 

 

  1. struct sigaction  
  2. {  
  3.     void (*sa_sighandler) (int);  
  4.     void (*sa_sigaction) (int, siginfo_t *, void *);  
  5.     sigset_t sa_mask;  
  6.     int sa_flags;  
  7.     void (*sa_restorer) (void);  
  8. }  
struct sigaction
{
	void (*sa_sighandler) (int);
	void (*sa_sigaction) (int, siginfo_t *, void *);
	sigset_t sa_mask;
	int sa_flags;
	void (*sa_restorer) (void);
}

 

sa_flags参数

 

取值  含义
SA_NOCLDSTOP 用于指定信号SIGCHLD。
SA_NOCLDWAIT 对信号SIGCHLD,子进程结束时,不创建僵死进程。
SA_NODEFER 处理信号时如果产生了其他信号,则先去处理其他信号。
SA_NOMASK 与SA_NODEFER相似。
SA_RESETHAND 处理完信号后,注销处理函数。
SA_ONESHOT 同SA_RESETHAND相似
SA_RESTART 信号发生时正处在程序阻塞处,则处理完信号从阻塞处返回。
SA_SIGINFO 指示sa_sigaction有效。不设置表示sa_handler有效。

 

 

信号集

 

  1. #include <signal.h>   
  2. int sigemptyset( sigset_t *set );  
  3. int sigfillset( sigset_t *set );  
  4. int sigaddset( sigset_t *set, int signum );  
  5. int sigdelset( sigset_t *set, int signum );  
  6. int sigismember( const sigset_t *set, int signum );  
#include <signal.h>
int sigemptyset( sigset_t *set );
int sigfillset( sigset_t *set );
int sigaddset( sigset_t *set, int signum );
int sigdelset( sigset_t *set, int signum );
int sigismember( const sigset_t *set, int signum );


前四个函数成功返回0;出错返回-1.最后一个函数为真返回1;为假返回0。

 

信号集用于表示由多个信号所组成的数据类型。empty置空,fill置满,add添加,del删除,member检测。

 

3 信号的发送

向其他进程发送信号

 

  1. #include <signal.h>   
  2. #include <sys/types.h>   
  3. int kill( pid_t pid, int signum );  
#include <signal.h>
#include <sys/types.h>
int kill( pid_t pid, int signum );


成功后返回0;出错返回-1。发送对象可以使一个进程组。

 

 

向本进程发送信号

 

  1. #include <signal.h>   
  2. #include <sys/types.h>   
  3. int raise( int signum );  
#include <signal.h>
#include <sys/types.h>
int raise( int signum );


成功后返回0;出错返回-1。

 

 

实时信号的发送

 

  1. #include <signal.h>   
  2. #include <sys/types.h>   
  3. int sigqueue( pid_t pid, int signum, const union sigval val );  
#include <signal.h>
#include <sys/types.h>
int sigqueue( pid_t pid, int signum, const union sigval val );

 

  1. typedef union sigval  
  2. {  
  3.     int sival_int;  
  4.     void *sigval_ptr; // 指向要传递的信号参数   
  5. } sigval_t;  
typedef union sigval
{
	int sival_int;
	void *sigval_ptr; // 指向要传递的信号参数
} sigval_t;

 

支持信号带有参数。成功后返回0;出错返回-1。发送对象只能是单个进程。

 

指定时间的发送

 

  1. #include <unistd.h>   
  2. unsigned int alarm( unsigned int seconds );  
#include <unistd.h>
unsigned int alarm( unsigned int seconds );


seconds参数指定了下一次发送SIGALRM信号的时间。如果调用前设置了闹钟,则返回闹钟剩余时间,否则返回0。

 

 

  1. #include <sys/time.h>   
  2. int setitimer( int which, const struct itimerval *value, struct itimerval *oldvalue);  
  3.   
  4. struct itimerval  
  5. {  
  6.     struct timeval it_interval;  
  7.     struct timeval it_value;  
  8. };  
#include <sys/time.h>
int setitimer( int which, const struct itimerval *value, struct itimerval *oldvalue);

struct itimerval
{
	struct timeval it_interval;
	struct timeval it_value;
};

 

 

which参数

 

取值 定时器类型 信号
ITIMER_REAL 根据系统时间设定绝对时间。 SIGALRM
ITIMER_VIRTUAL 设定程序执行时间,用户模式下可跟踪。 SIGVTALRM
TIMER_PROF 从用户进程开始后开始计时。 SIGPROF

 

 


成功后返回0;出错返回-1。相比于alarm,setitimer支持三种类型的定时器。

 

3 信号的阻塞

 

  1. #include <signal.h>   
  2. int sigprocmask( int how, const sigset_t *set, sigset_t *oldset );  
#include <signal.h>
int sigprocmask( int how, const sigset_t *set, sigset_t *oldset );

 

how参数

 

取值 功能
SIG_BLOCK 将set信号集的信号与掩码做逻辑或运算
SIG_UNBLOCK 将set信号集的信号与掩码做逻辑减运算
SIG_SETMASK 以set信号集对信号掩码进行赋值操作

 

 

成功后返回0;出错返回-1。用于信号的阻塞延迟处理。

 

 

  1. #include <signal.h>   
  2. int sigsuspend( const sigset_t *sigmask );  
#include <signal.h>
int sigsuspend( const sigset_t *sigmask );

 

成功后无返回值;出错返回-1。调用此函数后进程挂起,直到接收到信号复原信号集,然后调用处理函数。

 

 

 

3 信号通信测试代码:

 

  1. #include <stdio.h>  
  2. #include <signal.h>  
  3. #include <sys/types.h>  
  4. #include <unistd.h>  
  5.   
  6. void op(int, siginfo_t*, void*);  
  7.   
  8. int main(int argc,char**argv) {  
  9.     struct sigaction act;  
  10.     int sig;  
  11.     pid_t fpid, spid;         
  12.       
  13.     if((spid =fork()) == 0) {  
  14.         int signum;  
  15.         union sigval mysigval;  
  16.         signum = 5; //信号值取5  
  17.         fpid = getppid(); //获取父进程ID  
  18.         mysigval.sival_int = 8; // 用于测试  
  19.   
  20.         if(sigqueue(fpid, signum, mysigval) == -1) //子进程发送信号  
  21.             printf("send error\n");  
  22.         sleep(2);  
  23.     }  
  24.   
  25.     else if(spid > 0) {  
  26.         struct sigaction act;  
  27.         int sig = 5;  
  28.       
  29.         sigemptyset(&act.sa_mask);  
  30.         act.sa_sigaction=op;  
  31.         act.sa_flags=SA_SIGINFO;  
  32.         if(sigaction(sig, &act, NULL) < 0) //父进程指定对于信号的处理  
  33.             printf("install sigal error\n");  
  34.         sleep(2);  
  35.     }  
  36. }  
  37.   
  38. void op(int signum, siginfo_t *info, void *myact) { // 信号处理函数  
  39.     printf("the int value is %d \n", info->si_int);  
  40. }  


运行结果:

 

 

  1. jimmy@MyPet:~/code/ipc$ gcc -g -o ipc ipc.c   
  2. jimmy@MyPet:~/code/ipc$ ./ipc  
  3. the int value is 8   

 

非正常测试结果及总结

1)将子进程中的signum改为与父进程中的sig值不一致,不能进行信号通信。

2)信号通信是惟一的进程间异步通信模式。

3)可以在终端输入kill -l查看系统支持的信号。其中前半部分(<=32)是不可靠信号,后半部分是可靠信号。

4)进程对于实时信号的默认处理方式都是终止进程。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!