欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

进程间通信——信号

程序员文章站 2022-03-19 13:15:32
...
信号
        软中断信号(signal,又简称为信号)用来通知进程发生了异步事件。在软件层次上是对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是进程间通信机制中唯一的异步通信机制一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。进程之间可以互相通过系统调用kill发送软中断信号。内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件。信号机制除了基本通知功能外,还可以传递附加信息。

信号的分类

收到信号的进程对各种信号有不同的处理方法。处理方法可以分为三类:

第一种是类似中断的处理程序,对于需要处理的信号,进程可以指定处理函数,由该函数来处理。

第二种方法是,忽略某个信号,对该信号不做任何处理,就象未发生过一样。

第三种方法是,对该信号的处理保留系统的默认值,这种缺省操作,对大部分的信号的缺省操作是使得进程终止。进程通过系统调用signal来指定进程对某个信号的处理行为。


可靠信号与不可靠信号

信号: man 7 signal  -》信号的英文帮助文档,信号在实现时,通常是一个整数。

Standard signals:

Signal     Value     Action   Comment

       ────────────────────────────────────────

       SIGHUP        1       Term    控制终端检测到挂起操作或控制进程死亡产生该信号

       SIGINT         2       Term    CTRL+C

       SIGQUIT       3       Core    CTRL+Z

       SIGILL          4       Core    遇到非法指令时产生,Illegal Instruction

       SIGABRT      6       Core    调用abort函数产生,Abort signal from abort(3)

       SIGFPE         8       Core    浮点运算出错,Floating point exception

       SIGKILL        9       Term    Kill signal  *不可忽略

       SIGSEGV     11       Core    内存非法引用时产生,Invalid memory reference

       SIGPIPE       13       Term    当pipe是非阻塞方式打开时,如果往管道里写数据时,没有进程读时,产生该信号。

       SIGALRM     14       Term    Timer signal from alarm(2)

       SIGTERM     15       Term    终止信号。Termination signal

       SIGUSR1   30,10,16    Term    User-defined signal 1

       SIGUSR2   31,12,17    Term    User-defined signal 2

       SIGCHLD   20,17,18    Ign(忽略)     子进程停止或终止运行时产生。

       SIGCONT   19,18,25    Cont    在停止状态继续运行时产生该信号Continue if stopped

       SIGSTOP   17,19,23    Stop    Stop process  *不可忽略

       SIGTSTP   18,20,24    Stop    Stop typed at terminal

       SIGTTIN   21,21,26    Stop    Terminal input for background process

       SIGTTOU   22,22,27    Stop    Terminal output for background process

       The signals SIGKILL and SIGSTOP cannot be caught, blocked,

       or ignored. 

-----------------------------------------------------------------------------------------------------------------

命令kill的使用

kill  信号发送命令

kill -数字  +PID   --->向某个进程发送一个信号

kill -s  信号名称  +PID ---》向某个进程发送一个信号


可以通过 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     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

信号值位于SIGRTMIN和SIGRTMAX之间的信号都是可靠信号,可靠信号克服了信号可能丢失的问题。Linux在支持新版本的信号安装函数sigation()以及信号发送函数sigqueue()的同时,仍然支持早期的signal()信号安装函数,支持信号发送函数kill()。

对于目前linux的两个信号安装函数:signal()及sigaction()来说,它们都不能把SIGRTMIN以前的信号变成可靠信号(都不支持排队,仍有可能丢失,仍然是不可靠信号),而且对SIGRTMIN以后的信号都支持排队。这两个函数的最大区别在于,经过sigaction安装的信号都能传递信息给信号处理函数,而经过signal安装的信号不能向信号处理函数传递信息。对于信号发送函数来说也是一样的。


信号的实现步骤

1.创建信号

2.信号在进程中注册

3.信号的执行

4.信号注销


信号相关函数

1.signal() ---》信号捕抓函数

头文件:

  #include <signal.h>

函数原型:

  void(*sighandler_t)(int);-》指针   ---》 void func(int arg) ->信号处理函数,该函数必须为 void func(int arg)类型

  sighandler_t signal(int signum, sighandler_t handler);

参数一:捕抓的信号值

参数二:信号的处理方式

 SIG_IGN,   -》忽略改信号

 SIG_DFL    -》使用默认方式处理

 自定义处理

返回值:失败返回SIG_ERR ,成功返回信号值

 阻塞该信号(需要使用第三方函数处理) 


练习:signal函数的使用

#include <stdio.h>

#include <signal.h>

#include <unistd.h>

#include <sys/types.h>

//信号处理函数

 void func(int arg) 

 {

 

printf("I GET signal arg=%d\n",arg);

 

 }

int main()

{

//捕抓信号   

//signal(SIGINT,SIG_DFL); //使用默认方式处理

//signal(SIGINT,SIG_IGN);   //忽略该信号

signal(SIGTERM,func);     //使用自定义方式处理


printf("wati signal %d\n",getpid());

int i=0;

while(1)

{

if(i==5)

{

//发送信号

 //kill(getpid(),SIGTERM);

 raise(SIGTERM);

 break;

}

printf("i=%d\n",i++);

sleep(1);

}

//pause();

printf("signal run\n");

return 0;

}



进程间通信——信号

-----------------------------------------------------------------------------------------------------------------

2. pause()---》等待信号函数

头文件:

   #include <unistd.h>

函数原型:

    int pause(void);

返回值:失败返回-1;

------------------------------------------------------------------------------------------------------------------

3. kill()---》信号发送

头文件:

      #include <sys/types.h>

       #include <signal.h>

函数原型:

       int kill(pid_t pid, int sig);

参数一:指定信号的接受者

        pid > 0   接受者的PID号

        pid == 0  发给  与进程同组的所有进程

 pid == -1 发给  拥有该进程权限的 所有进程

pid <  0  发给  与组id 和pid绝对值相同的进程

参数二:信号类型

返回值:成功返回0,失败返回-1

----------------------------------------------------------------------------------

4. raise()---》给自己发送信号

头文件:

        #include <signal.h>

函数原型:

       int raise(int sig);  《=》kill(getpid(),int sig);

----------------------------------------------------------------------------------

5. alarm()---》时钟信号

头文件:

      #include <unistd.h>

函数原型:

       unsigned int alarm(unsigned int seconds);

       设置:一个时钟,以秒为单位,当时钟来时,会触发SIGALRM信号


练习:alarm函数的使用

#include <stdio.h>

#include <unistd.h>

#include <signal.h>

int i=0;

void func(int arg)

{



printf("sig=%d,%d\n",arg,i++);



} 

 

int main()

{



    //产生一个时钟


while(1)

{

alarm(2);

//捕抓时钟信号

signal(SIGALRM,func);

pause();

}

return 0;

}


进程间通信——信号

运行后每隔一秒时钟信号触发

------------------------------------------------------------------------------

6. sigprocmask()---》信号阻塞

头文件:

         #include <signal.h>

函数原型:

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

返回值:成功返回0,失败返回-1

参数一:

       SIG_BLOCK     阻塞

       SIG_UNBLOCK   不阻塞

       SIG_SETMASK   阻塞

参数二:阻塞集合 -》该集合中存放要阻塞的信号   ***重点

参数三:原来的阻塞集合

注意:阻塞集合会把所有信号都保存起来,当解除阻塞后才去执行相应的信号操作。

进程间通信——信号

练习:信号阻塞

#include <stdio.h>

#include <unistd.h>

#include <signal.h>

#include <sys/types.h>

#include <unistd.h>



void func(int arg)

{



printf("sig****************=%d\n",arg);



} 



int main()

{



    //阻塞信号集 


//1.设置信号集中要阻塞的信号

    sigset_t set;//定义一个信号集

    sigemptyset(&set); //清空信号集

sigaddset(&set,SIGALRM);//添加要阻塞的信号

    //开启阻塞集合

sigprocmask(SIG_BLOCK ,&set,NULL);


    //捕抓一个信号

signal(SIGALRM,func);

int i = 0;

for(i=0;i<10;i++)

{

printf("i=%d,pid=%d\n",i,getpid());

sleep(1);

}

 //关闭阻塞集合

sigprocmask(SIG_UNBLOCK,&set,NULL);

pause();

return 0;

}


进程间通信——信号

运行程序,10s后阻塞等待14号信号,在另一个终端用14号信号杀掉进程。

---------------------------------------------------------------------------

7. sigaction()---》信号传参:

头文件:

  #include <signal.h>

函数原型:

  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);//不常用

           };

参数三:原有的信号处理参数

处理函数中的结构体:

siginfo_t {

  int      si_int;      /* POSIX.1b signal */

         void    *si_ptr;      /* POSIX.1b signal */

        ......................

}

进程间通信——信号

练习:sigaction函数的使用

#include <signal.h>

#include <stdio.h>

#include <string.h>

#include <sys/types.h>

#include <unistd.h>



void  func(int arg, siginfo_t *info, void *arc)

{

printf("GET SIGNAL info%d\n",info->si_int);

}



int main()

{

//捕抓信号

struct sigaction act;

bzero(&act,sizeof(act));

act.sa_sigaction = func;

act.sa_flags = SA_SIGINFO;//把原来的信号处理函数替换掉

    sigaction(SIGTERM,&act,NULL);

//signal(SIGTERM,func);

//等待信号

printf("getpid=%d\n",getpid());

pause();

}


进程间通信——信号

运行程序后,在另一个终端kill掉这个线程,可以捕获该信号。

---------------------------------------------------------------------------

8. sigqueue()---》带参的发送函数:

头文件:

        #include <signal.h>

函数原型:

         int sigqueue(pid_t pid, int sig, const union sigval value);

 参数一:要发送的PID号 

 参数二:要发送的信号

 参数三:要传输的数据

        union sigval {

               int   sival_int;

               void *sival_ptr;

           };

练习:sigqueue函数的使用

#include <signal.h>

#include <stdio.h>

#include <stdlib.h>



int main(int argc,char *argv[])

{

 union sigval value;

value.sival_int = 2222;

//发送带参信号

sigqueue(atoi(argv[1]),SIGTERM,value);

} 

--------------------------------------------------------------------------


相关标签: 信号