zoukankan      html  css  js  c++  java
  • 初识信号---进程间的交流

    一、信号机制

      正如我们标题所说,信号就是进程间的对话,A进程想要告诉B进程一些事。比如子进程在结束之前就可以给父进程发这样一条“短信”:“嘿,我要结束了,为了避免让我成为僵死进程,快来读取我的信息吧!”这个时候父进程收到了子进程的短信,于是停下手里的工作,花少量时间处理好子进程,再继续进行自己的工作,这样,父进程再也不用傻傻的等子进程结束之后再执行,同样,多个子进程也可以通过这样的“发短信”的方式,告知父进程。这样就做到了异步处理僵死进程,使得子进程和父进程不再串行进行,父进程也不用再默默的等待子进程结束啦,从而节省了大量的时间。而“短信”,就是信号。这里我们要研究的就是,进程是怎样收发信号的,发送的信号到底是怎样内容?

      其实信号的实质,就是由系统预先定义好的某些特定的事件。在《unix环境高级编程》里有写,信号其实就是一些定义在头文件里的int型正整数。

      所以我们刚刚所说的子进程给父进程发信号,并不像我写的那么废话连篇,就只是一个简单的、冷冰冰的、机械的、准确的整型数字。信号可以接受,可以发送,但是都是指在进程之间进行。

      总结起来就是:

      在Linux中,信号是进程间通讯的一种方式,它采用的是异步机制。当信号发送到某个进程中时,操作系统会中断该进程的正常流程,并进入相应的信号处理函数执行操作,完成后再回到中断的地方继续执行。

      需要说明的是,信号只是用于通知进程发生了某个事件,除了信号本身的信息之外,并不具备传递用户数据的功能。


      很多条件下都会产生信号:

      ·当用户按下某些终端按键时,引发终端产生信号。在终端上按Ctrl+C键则产生中断信号SIGINT。这是停止一个已失去控制的程序的方法。

      ·引荐一场产生信号:除数为0、无效的内存引用等等,这些条件通常由引荐检测到,并将其通知内核。然后内核为该条件发生正在运行的进程产生适当的信号。例如,对执行一个无效内存引用的进程产生SIGSEGV信号。

      ·进程调用kill函数可将信号发送给另一个进程或进化成呢个组。自然面对此有所限制:接收信号进程和发送信号进程的所有者必须相同,或者发送信号进程的所有者必须时超级用户。

      ·用户课用kill命令将信号发送给其他进程。此命令只是kill函数的接口。常用此命令终止哟个时空的后台进程。

      ·当检测到某种软件条件已经发生,并应将其通知有关进程也产生信号。这里值得不是硬件产生打得条件(如初一0),而是软件条件。例如SIGGURG(在网络连接山川来带外数据时长生)、SIGPIPE(在管道的读进程已终止后,一个进程写此管道时产生)、,以及SIGALRM(进程所设置的闹钟始终超时时产生)。

      信号是异步处理事件的经典实例。产生信号的时间对进程而言是随机出现的。进程不能简单地测试一个变量来判断是否出现了一个信号,而是必须告诉内核“在次信号出现时,请执行下列操作”。

      但是内核在某个信号出现时也可以按照下列三种方式之一进行处理,我们称之为信号的处理与信号的相关动作。

      1、忽略此信号。大多数信号都可以使用这种中方式进行处理,但有两种信号却绝不能贝忽略。他们是SIGKILL和SIGSTOP。

      2、捕捉信号。要做到这一点,需要通知内核在魔偶中信号发生时调用一个用户函数。再次函数中,可以执行用户希望堆这种事件进行的处理。

      3、执行系统默认操作。针对大多数信号的系统默认动作是终止进程。


    二、signal函数

    void (*signal(int signo, void (*func)(int))) (int);

    返回值:成功,返回以前的信号处理函数;失败,返回SIG_ERR

    说明:

    signo为信号名。

    func为常量值SIG_IGN(忽略此信号)/SIG_DFL(执行系统默认动作)/(接收此信号要调用的函数地址)。

    signal函数的返回值是一个函数地址,指向在此之前的信号处理函数,而func指向新的信号处理函数。

     

    由于函数原型太过复杂,也可使用下面的定义方式:

    typedef void Sigfunc(int);

    Sigfunc* signal(int, Sigfunc*);

    看一个实例:

     1 #include <stdio.h>
     2 #include <signal.h>
     3 
     4 void fun()
     5 {
     6     int i = 0;
     7     for(; i < 3; i++)
     8     {
     9         printf("Hello World
    ");//1秒打印一次,执行3次
    10         sleep(1);
    11     }
    12 }
    13 
    14 void main()
    15 {
    16     signal(SIGINT,fun);//信号:当用户按下Ctrl+C时,执行fun函数
    17     while(1)
    18     {
    19         sleep(2);
    20         printf("Running
    ");//每2秒打印一次
    21     }
    22 }

     执行结果:

      可以看到,每次按下ctrl+c,都会执行一次打印三次Hello World,也就是说,每次按下Ctrl+C时,都会发送一个信号,让程序去执行调用并执行fun函数。

      同样,我做一个小小的改动,就能实现让第一次SIGINT信号成为执行fun的命令,第二次按下时直接程序结束。我把SIGINT信号修改为默认方式接收的signal函数在fun中,为什么不放在主函数main中呢?因为我把放在signal(SIGINT,fun)之前会导致该信号又被修改为去执行fun了,而其放在之后,又直接为为默认方式,而不能同时使用两个信号。当我把signal(SIGINT,SIG_DFL)放在fun函数里时就不会发生这些事了,因为只有当第一个信号起作用时才会进入fun函数,而在fun函数里,无论我把第二个信号放在哪儿都是能实现的。

     1 #include <stdio.h>
     2 #include <signal.h>
     3 
     4 void fun()
     5 {
     6     int i = 0;
     7     for(; i < 3; i++)
     8     {
     9         printf("Hello World
    ");//秒打印一次,执行3次
    10         sleep(1);
    11         signal(SIGINT,SIG_DFL);//以默认方式接收信号
    12     }
    13 }
    14 
    15 void main()
    16 {
    17     signal(SIGINT,fun);//信号:当用户按下Ctrl+C时,执行fun函数
    18     while(1)
    19     {
    20         sleep(2);
    21         printf("Running
    ");
    22     }
    23 }

      结果如下:

      如图所示,当第二次按下Ctrl+C时,信号又变成了默认操作,直接结束程序。

      还是这段代码,当我按下Ctrl+C的时机是在执行fun时,程序会在执行完fun之后直接结束。

  • 相关阅读:
    保持URL不变和数字验证
    centOS ftp key?
    本地环境测试二级域名
    linux 解决You don't have permission to access 问题
    php smarty section loop
    php header Cannot modify header information headers already sent by ... 解决办法
    linux部分命令
    Linux 里面的文件操作权限说明
    用IT网络和安全专业人士视角来裁剪云的定义
    SQL Server 2008 R2炫酷报表"智"作有方
  • 原文地址:https://www.cnblogs.com/jian-99/p/7739687.html
Copyright © 2011-2022 走看看