今天要分享的是Linux中的信号机制,信号是一种软件中断,是一种处理异步事件的方法,可以很好地在多个进程之间进行同步和简单的数据交换。
一、发送信号发送信号通常有三种方式,分别是使用kill、raise、sigqueue函数1、kill函数
int kill(pid_t pid,int sig);
第一个参数代表向谁发送,第二个参数代表发送什么信号。调用成功返回0,调用失败返回-1.2、raise函数
int raise(int sig);
这个是向自身发送一个信号,等价于
kill(getpid(),sig);
3、sigqueue函数
int sigqueue(pid_t pid,int sig,const union sigval value);
其中第三个参数的形式为
typedef union sigval{int sival_int;void *sival_ptr;}sigval_t;
这个函数除了能发送信号之外,还能携带一些参数,这些参数就保存在共用体里面。下面写一个简单的代码
#include#include#include#include #include int main(int argc,char* argv[]){ pid_t pid; pid=fork(); if(pid==0){ printf("This is child process\n"); sleep(10); printf("does't receive abort signal\n"); } else{ printf("This is parent process\n"); sleep(5); if(kill(pid,SIGABRT)==-1){ printf("kill function failed\n"); } } return 0;}
在这里使用fork创建了一个子进程,子进程本来调用sleep函数要睡眠10秒,但是父进程在睡眠5秒后向子进程发送一个中止的信号,导致子进程在5s时中止了。从下面的图中也可以看到原来两个进程都是处于睡眠态,后面就结束了进程。
void (*signal(int signum,void(*handler)(int)))(int);
这个函数原型看起来很复杂,可以难倒很多人,我们可以对他进行简化:
typedef void (*sighandler_t)(int);sighandler_t signal(int signum,sighandler_t handler);
这样就清楚很多了。首先看sighandler_t他是一个函数指针,输入参数是int类型,无返回值。再看signal这个函数,他有两个输入参数,第一个是int类型,第二个是一个函数指针,另外,他返回的也是一个函数指针。只有对函数指针非常熟悉,才能看懂上面这个表达式。如果还是不懂,可以对比下面这个表达式
char* function(int a,char* b);
这个应该很容易看出是一个函数了吧,两个输入参数,返回一个char*。如果这个可以看懂,那么上面那个其实是类似的,只不过他不是char* ,而是一个 函数指针类型:void(*)(int).signal函数的第一个参数是信号类型,第二个参数是函数指针,也就是跳转到哪里去执行。也就是说,当收到第一个参数表示的信号之后,就会跳转到第二个参数指向的代码段去执行。2、sigaction函数
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
这个函数不比上面的简单,其中第二个参数里的结构体类型展开是:
struct sigaction { void (*sa_handler)(int); void (*sa_sigaction)(int, siginfo_t *, void *); sigset_t sa_mask; int sa_flags; void (*sa_restorer)(void);}
在这个结构体中,成员 sa_handler 是一个函数指针,其含义与 signal 函数中的信号处理函数类似。成员sa_sigaction 则是另一个信号处理函数,它有三个参数,可以获得关于信号的更详细的信息。当 sa_flags 成员的值包含了 SA_SIGINFO 标志时,系统将使用 sa_sigaction 函数作为信号处理函数,否则使用 sa_handler 作为信号处理函数。接下来写一个简单的代码,来应用一下上面的几个函数。实现的需求就是创建一个子进程,父进程每隔一秒钟向子进程发送一个信号,子进程收到信号之后往一个txt文档中写入一句话。
#include#include#include#include #include #include #include #include void signalDeal(int sig,siginfo_t *info,void*t);int fg=0;int main(int argc,char* argv[]){ if(argc!=2){ printf("please input param\n"); return -1; } char writebuff[]="This is a test\n"; int count=0,seektemp=0,j=0; int fd=open(argv[1],O_RDWR | O_CREAT,S_IRWXU); //open a file pid_t pid=fork(); if(pid==0) //child process { struct sigaction act; act.sa_sigaction=signalDeal; sigemptyset(&act.sa_mask); act.sa_flags=SA_SIGINFO; sigaction(SIGUSR1,&act,NULL); while(1) { while(!fg); fg=0; if(count==0){ //first write int ret=write(fd,writebuff,strlen(writebuff)); if(ret==-1){ printf("write failed\n"); } seektemp=lseek(fd,0,SEEK_CUR); count++; } else{ //not first write j=strlen(writebuff)*count; //offset seektemp=lseek(fd,j,SEEK_SET); int ret=write(fd,writebuff,strlen(writebuff)); if(ret==-1){ printf("write failed\n"); } count++; } } } else { //parent process while(1) { sleep(1); int ret=kill(pid,SIGUSR1); if(ret==-1){ printf("send signal failed\n"); } else{ printf("send signal success\n"); } } }}void signalDeal(int sig,siginfo_t *info,void*t){ if(sig==SIGUSR1) { fg=1; }}
在命令行输入
gcc test.c./a.out a.txt
之后可以看到生成了一个a.txt文档,并且文档里有我们写入的内容