<Unix环境高级编程>--信号

   这篇是关于在读<Unix环境高级编程>信号一章时做的一些笔记,特记录在此。
   关于多线程,多进程这几章的笔记,写得太乱,正在尝试更正。

以下开始正文
-----------------------------------------------------------------------------------------

1.信号和pause()函数
   调用pause()函数可以让进程的状态变为休眠,休眠到什么时候呢?答案是:当进程中一
个信号产生时。
   当进程调用pause()函数休眠的时候,信号来临,此时,内核将自动将进程唤醒。
   以下的代码可以使用pause让进程中休眠一段时间后,继续执行。

   signal(SIG_ALRM,slg_alrm);
   alarm(3);
   while(need_to_do==0)
        pause();

   void slg_alrm()
    {
        need_to_do=1;
    }

   第一步,进程调用alarm函数设定一个超时器,3秒后SIG_ALRM进程将会准时到达。
   第二步,进程在pause()程序语句中转入休眠状态。
   第三步,SIG_ALRM到达后,sig_alrm被处理,将变量need_to_do设置为1。
   第四步,内核将此进程唤醒,此进程将从休眠状态转为可调度状态。此进程被分配到CPU后,进程将从pause()指令的下一条指令开始执行。
   第五步,此时need_to_do已经变更为1,所以,pause函数将不会被执行了。这样,就模
拟了一个让进程休眠三秒的动作。



2.中断系统调用和自动重启动
  在Linux系统中,总有某些函数是属于低速系统调用,当调用低速系统调用函数时,进程会被阻塞,哪些是低速系统调用呢?
  比如,当需要进行I/O函数操作时(read/write函数),因为需要访问I/O设备,其肯定属于低速系统调用,在访问该I/O设备时,进程可能会被阻塞一段时间,又或者,当进程需要>从键盘读入字符(read(STDIN))时,进程肯定会被阻塞,直到用户在键盘中输入完毕,函数才能返回。
    那么,当以上低速系统调用期间,如果进程产生信号,怎么处理?
    如:
      if((n=read(STDIN,buf,BUFSIZE))<0)
        {
            //...
        }
    以上代码,如果进程在执行read指令期间发生中断(信号产生),处理完中断后,进程该从哪儿开始执行呢?重新执行read吗?还是read的下一条指令?还是产生异常退出?
    在早期的UNIX系统中,有一个特性就是:如果进程在执行一个低速系统调用而阻塞间捕捉到一个信号,则该系统调用就被中断不再继续执行,系统调用返回出错,其error值被置为EINTR.  早期对于低速系统调用的处理大多都是这样的:
        agin:
           if((n=read(STDIN,buf,BUFSIZE))<0)  //对于中断,系统将会返回出错
           {
              if(errno==EINTR)
                goto agin;   //重新执行read
              else
                //其它错误。
           }
           else
              //Success

    为了帮助应用程序不必处理被中断的系统调用,就引入了自动重启动机制。自动重启动的函数包括:ioctl,read,readv,write,writev,wait,waitpid.
    对于以上函数,在执行期间,如果被信号中断,则该函数可以再次被调用。
    以上,wait,waitpid并不是针对低速设备调用阻塞而使用的函数,把它们加入到自动重启动是因为:这两个函数碰到信号总是会被中断!!
    对于以上函数默认的自动重启动,有时会产生一些问题,所以,现在允许进程基于每个信号禁用自动重启动的功能。

3.可重入函数
    在讨论可重和函数之前,先来看一下以下的代码:

    int gobal_var = 0;

    signal(SIG_TERM,sig_term);

    void sig_term
    {
        gobal_var = 0;
        //do other
    }

    void func()
    {
        gobal_var = 1; //设置全局变量为1
        if(gobal_var)
            // do other code.
    }

  以上,func函数设置全局变为1,继续下面的指令,如果在if(gobal_var)这条指令执行前,发生了SIG_TERM中断,则全局变为gobal_var被置为0,返回后,if语句下面的指令将不会被执行。
  以上有一个特征,就是在发生中断时,进程的全局数据空间导致了变更,调用func函数时,如果有中断或没有中断产生,函数产生的结果可能不一样。
  另外一个例子,假设gobal_var是在func中保存了进程的pid号,我们将func作为一个函数库发布,在多进程环境中,每个进程调用func产生的结果也可能不一样。
  在<unix环境高级编程>中也有一个例子,就是进程正在执行malloc,此时捕捉到信号,执行信号处理函数,其中又会调用malloc函数,因为对于进程来说,malloc函数为进程所分配的存储区维护一张链表,执行信号处理程序时,进程可能正在更改此链表,如果继续执行一次malloc,则此链表可能遭受破坏。

  现在我们引入可重入函数:什么是可重入函数?
    可重入(reentrant)函数可以由多于一个任务并发使用,而不必担心数据错误,也可以理解为,每个任务得到的数据都是相同的。另外一个特征:可重入函数可以在任意时刻被中断,稍后继续运行,不会丢失数据。
    所以,可重入函数内部大部分都会使用本地变量,或者,如果要使用全局变量进,会保护自己的数据。

  当一个函数中使用以下几种实现时,他们很可能就不是可重入的函数:
    (a): 使用静态的数据结构或者全局变量。
    (b): 调用malloc或者free函数.
    (c): 调用了其它不可重函数
    (d): 调用了I/O库。
  总体来说的话,一个函数在重入条件下(可以看成再次调用,或者从中断返回)条件下使用了未受保护的共享资源,则它是不可重入的.

posted @ 2008-06-11 23:41  shipfi  阅读(682)  评论(0编辑  收藏  举报