UNIX信号(下)

UNIX信号(下)
2011-11-28 11:14

3     有关信号的系统调用

        前面两节已经介绍了有关信号的大部分知识。这一节我们来了解一下这些系统调用。其中,系统调用signal是进程用来设定某个信号的处理方法,系统调用 kill是用来发送信号给指定进程的。这两个调用可以形成信号的基本操作。后两个调用pause和alarm是通过信号实现的进程暂停和定时器,调用 alarm是通过信号通知进程定时器到时。所以在这里,我们还要介绍这两个调用。

3.1    signal 系统调用

       系统调用signal用来设定某个信号的处理方法。该调用声明的格式如下:
       void (*signal(int signum, void (*handler)(int)))(int);
       在使用该调用的进程中加入以下头文件:
       #include <signal.h>上述声明格式比较复杂,如果不清楚如何使用,也可以通过下面这种类型定义的格式来使用(POSIX的定义):
       typedef void (*sighandler_t)(int);
       sighandler_t signal(int signum, sighandler_t handler);
       但这种格式在不同的系统中有不同的类型定义,比如在BSD 4.4系统中是通过下面这种类型定义使用的:
       typedef void (*sig_t) (int);
       sig_t signal(int signum, sig_t handler);
       所以要使用这种格式,最好还是参考一下联机手册。在调用中,参数signum指出要设置处理方法的信号。第二个参数handler是一个处理函数,或者是
       + SIG_IGN:忽略参数signum所指的信号。
       + SIG_DFL:恢复参数signum所指信号的处理方法为默认值。传递给信号处理例程的整数参数是信号值,这样可以使得一个信号处理例程处理多个信号。 系统调用signal返回值是指定信号signum前一次的处理例程或者错误时返回错误代码SIG_ERR。注意,根据 POSIX(B.3.3.1.3),进程不能将SIGCHLD设置为SIG_IGN,这一点与BSD系统不同。下面来看一个简单的例子:     #include <signal.h>
    #include <unistd.h>
    #include <stdio.h>
  
    void sigroutine(int dunno)
    {
    /* 信号处理例程,其中dunno将会得到信号的值 */  
    switch (dunno)
    {    
    case 1:      
    printf("Get a signal -- SIGHUP \n");      
    break;    
    case 2:      
    printf("Get a signal -- SIGINT \n");      
    break;    
    case 3:      
    printf("Get a signal -- SIGQUIT \n");      
    break;  
    }  
    return;
    }
    int main()
    {  
    printf("process id is %d\n",getpid());
    signal(SIGHUP, sigroutine);
    //* 下面设置三个信号的处理方法  
    signal(SIGINT, sigroutine);
    signal(SIGQUIT, sigroutine);  
    for (;;);
    }
    其中信号SIGINT由按下Ctrl-C发出,信号SIGQUIT由按下Ctrl-\发出。该程序执行的结果如下:
    localhost:~$ ./sig_test
    process id is 463
    Get a signal -- SIGINT       //按下Ctrl-C得到的结果
    Get a signal -- SIGQUIT     //按下Ctrl-\得到的结果                      
                                            //按下Ctrl-z将进程置于后台
    [1]+    Stopped                   ./sig_test
    localhost:~$ bg
    [1]+ ./sig_test &
    localhost:~$ kill -1 463      //向进程发送SIGHUP信号
    localhost:~$ Get a signal -- SIGHUP
    kill -9 463                //向进程发送SIGKILL信号,终止进程
    localhost:~$

3.2   kill 系统调用

系统调用kill用来向进程发送一个信号。该调用声明的格式如下:
   int kill(pid_t pid, int sig);
   在使用该调用的进程中加入以下头文件:
   #include <sys/types.h>
   #include <signal.h>
   该系统调用可以用来向任何进程或进程组发送任何信号。如果参数pid是正数,那么该调用将信号sig发送到进程号为pid的进程。如果pid等于0,那么 信号sig将发送给当前进程所属进程组里的所有进程。如果参数pid等于-1,且发送进程有超级用户的权力时,信号sig将发送给除了进程1和自身以外的 所有进程,若发送进程没有超级用户的权力,信号sig将发送给所有uid等于发送信号进程的euid的进程。如果参数pid小于-1,信号sig将发送给 属于进程组-pid的所有进程。如果参数sig为0,将不发送信号。该调用执行成功时,返回值为0;错误时,返回-1,并设置相应的错误代码errno。 下面是一些可能返回的错误代码:
   + EINVAL:指定的信号sig无效。
   + ESRCH:参数pid指定的进程或进程组不存在。注意,在进程表项中存在的进程,可能是一个还没有被wait收回,但已经终止执行的僵死进程。
   + EPERM:进程没有权力将这个信号发送到指定接收信号的进程。因为,一个进程被允许将信号发送到进程pid时,必须拥有root权力,或者是发出调用的 进程的UID或EUID与指定接收的进程的UID或保存用户ID(savedset-user-ID)相同。如果参数pid小于-1,即该信号发送给一个 组,则该错误表示组中有成员进程不能接收该信号。

3.3   pause系统调用

系统调用pause的作用是等待一个信号。该调用的声明格式如下:
   int pause(void);
   在使用该调用的进程中加入以下头文件:
   #include <unistd.h>
   该调用使得发出调用的进程进入睡眠,直到接收到一个信号为止。该调用总是返回-1,并设置错误代码为EINTR(接收到一个信号)。下面是一个简单的范例:
   #include <unistd.h>
   #include <stdio.h>
   #include <signal.h>
  
   void sigroutine(int unused)
   {  
      printf("Catch a signal SIGINT\n");
   }
   int main()
   {  
     signal(SIGINT, sigroutine);  
     pause();  
     printf("receive a signal\n");
   }
   在这个例子中,程序开始执行,就象进入了死循环一样,这是因为进程正在等待信号,当我们按下Ctrl-C时,信号被捕捉,并且使得pause退出等待状态。

3.4   alarm和 setitimer系统调用

系统调用alarm的功能是设置一个定时器,当定时器计时到达时,将发出一个信号给进程。该调用的声明格式如下:
   unsigned int alarm(unsigned int seconds);
   在使用该调用的进程中加入以下头文件:
   #include <unistd.h>
   系统调用alarm安排核心为调用进程在指定的seconds秒后发出一个SIGALRM的信号。如果指定的参数seconds为0,则不再发送 SIGALRM信号。后一次设定将取消前一次的设定。该调用返回值为上次定时调用到发送之间剩余的时间,或者因为没有前一次定时调用而返回0。注意,在使 用时,alarm只设定为发送一次信号,如果要多次发送,就要多次使用alarm调用。
   对于alarm,这里不再举例。现在的系统中很多程序不再使用alarm调用,而是使用setitimer调用来设置定时器,用getitimer来得到定时器的状态,这两个调用的声明格式如下:
   int getitimer(int which, struct itimerval *value);
   int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue);
   在使用这两个调用的进程中加入以下头文件:       
   #include <sys/time.h>
   该系统调用给进程提供了三个定时器,它们各自有其独有的计时域,当其中任何一个到达,就发送一个相应的信号给进程,并使得计时器重新开始。三个计时器由参数which指定,如下所示:
   +   ITIMER_REAL:按实际时间计时,计时到达将给进程发送SIGALRM信号。
   +   ITIMER_VIRTUAL:仅当进程执行时才进行计时。计时到达将发送SIGVTALRM信号给进程。
   + ITIMER_PROF:当进程执行时和系统为该进程执行动作时都计时。与ITIMER_VIR-TUAL是一对,该定时器经常用来统计进程在用户态和核心态花费的时间。计时到达将发送SIGPROF信号给进程。

定时器中的参数value用来指明定时器的时间,其结构如下:
   struct itimerval
   {     
     struct timeval it_interval; /* 下一次的取值 */     
     struct timeval it_value;     /* 本次的设定值 */
   };
   该结构中timeval结构定义如下:
   struct timeval
   {     
     long tv_sec;                 /* 秒 */     
     long tv_usec;                /* 微秒,1秒 = 1000000 微秒*/
   };
   在setitimer调用中,参数ovalue如果不为空,则其中保留的是上次调用设定的值。定时器将it_value递减到0时,产生一个信号,并将 it_value的值设定为it_interval的值,然后重新开始计时,如此往复。当it_value设定为0时,计时器停止,或者当它计时到期,而 it_interval为0时停止。调用成功时,返回0;错误时,返回-1,并设置相应的错误代码errno:
   +   EFAULT:参数value或ovalue是无效的指针。
   +   EINVAL:参数which不是ITIMER_REAL、ITIMER_VIRT或ITIMER_PROF中的一个。
   下面是关于setitimer调用的一个简单示范,在该例子中,每隔一秒发出一个SIGALRM,每隔0.5秒发出一个SIGVTALRM信号:

   #include <signal.h>
   #include <unistd.h>
   #include <stdio.h>
   #include <sys/time.h>
   int sec;
  
   void sigroutine(int signo)
   {  
     switch (signo)
     {    
       case SIGALRM:      
       printf("Catch a signal -- SIGALRM\n");      
       break;    
       case SIGVTALRM:      
       printf("Catch a signal -- SIGVTALRM\n");      
       break;  
     }  
     return;
   }
  
   int main()
   {
     struct itimerval value,ovalue,value2;
     sec = 5;  
     printf("process id is %d\n",getpid());  
     signal(SIGALRM, sigroutine);  
     signal(SIGVTALRM, sigroutine);  
     value.it_value.tv_sec = 1;  
     value.it_value.tv_usec = 0;
     value.it_interval.tv_sec = 1;  
     value.it_interval.tv_usec = 0;  
     setitimer(ITIMER_REAL, &value, &ovalue);
     value2.it_value.tv_sec = 0;  
     value2.it_value.tv_usec = 500000;  
     value2.it_interval.tv_sec = 0;  
     value2.it_interval.tv_usec = 500000;  
     setitimer(ITIMER_VIRTUAL, &value2, &ovalue);  
     for (;;) ;
   }
   该例子的屏幕拷贝如下:
   localhost:~$ ./timer_testprocess id is 579
   Catch a signal -- SIGVTALRM
   Catch a signal -- SIGALRM
   Catch a signal -- SIGVTALRM
   Catch a signal -- SIGVTALRM
   Catch a signal -- SIGALRM
   Catch a signal -- SIGVTALRM
   本章不再介绍其他的调用,如果读者希望了解其他调用,请参考联机手册或其他文档。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值