linux 信号系统调用

1、signal 系统调用 
系统调用signal用来设定某个信号的处理方法。该调用声明的格式如下: 
void (signal(int signum, void (handler)(int)))(int); 
在使用该调用的进程中加入以下头文件: 
#include 



上述声明格式比较复杂,如果不清楚如何使用,也可以通过下面这种类型定义的格式来使用(POSIX的定义): 



typedef void (*sighandler_t)(int); 
sighandler_t signal(int signum, sighandler_t handler); 
但这种格式在不同的系统中有不同的类型定义,所以要使用这种格式,最好还是参考一下联机手册。



在调用中,参数signum指出要设置处理方法的信号。第二个参数handler是一个处理函数,或者是 
SIG_IGN:忽略参数signum所指的信号。 
SIG_DFL:恢复参数signum所指信号的处理方法为默认值。 



传递给信号处理例程的整数参数是信号值,这样可以使得一个信号处理例程处理多个信号。系统调用signal返回值是指定信号signum前一次的处理例程或者错误时返回错误代码SIG_ERR。下面来看一个简单的例子: 



#include
#include
#include
void sigroutine(int dunno) { /* 信号处理例程,其中dunno将会得到信号的值 */
switch (dunno) {
case 1:
printf(“Get a signal – SIGHUP “);
brea在使用这两个调用的进程中加入以下头文件:
#include 在使用这两个调用的进程中加入以下头文件:
#include k;
case 2:
printf(“Get a signal – SIGINT “);
break;
case 3:
printf(“Get a signal – SIGQUIT “);
break;
}
return;
}



int main() {
printf(“process id is %d “,getpid());
signal(SIGHUP, sigroutine); //* 下面设置三个信号的处理方法
signal(SIGINT, sigroutine);
signal(SIGQUIT, sigroutine);
for (;;) ;
}



2、kill 系统调用 
系统调用kill用来向进程发送一个信号。该调用声明的格式如下: 
int kill(pid_t pid, int sig); 
该 系统调用可以用来向任何进程或进程组发送任何信号。如果参数pid是正数,那么该调用将信号sig发送到进程号为pid的进程。如果pid等于0,那么信 号sig将发送给当前进程所属进程组里的所有进程。如果参数pid等于-1,信号sig将发送给除了进程1和自身以外的所有进程。如果参数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,即该信号发送给一个组,则该错误 表示组中有成员进程不能接收该信号。



#include <sys/types.h>



#include



int kill(pid_t pid, int sig);



参数:
pid:可能选择有以下四种




  1. pid大于零时,pid是信号欲送往的进程的标识。

  2. pid等于零时,信号将送往所有与调用kill()的那个进程属同一个使用组的进程。

  3. pid等于-1时,信号将送往所有调用进程有权给其发送信号的进程,除了进程1(init)。

  4. pid小于-1时,信号将送往以-pid为组标识的进程。



sig:准备发送的信号代码,假如其值为零则没有任何信号送出,但是系统会执行错误检查,通常会利用sig值为零来检验某个进程是否仍在执行。



返回值说明: 成功执行时,返回0。失败返回-1,errno被设为以下的某个值 EINVAL:指定的信号码无效(参数 sig 不合法) EPERM;权限不够无法传送信号给指定进程 ESRCH:参数 pid 所指定的进程或进程组不存在
#include <sys/wait.h>
#include <sys/types.h>
#include
#include
#include



int main( void )
{
pid_t childpid;
int status;
int retval;



 childpid = fork();
if ( -1 == childpid )
{
perror( "fork()" );
exit( EXIT_FAILURE );
}
else if ( 0 == childpid )
{
puts( "In child process" );
sleep( 100 );//让子进程睡眠,看看父进程的行为
exit(EXIT_SUCCESS);
}
else
{
if ( 0 == (waitpid( childpid, &status, WNOHANG )))
{
retval = kill( childpid,SIGKILL );

if ( retval )
{
puts( "kill failed." );
perror( "kill" );
waitpid( childpid, &status, 0 );
}
else
{
printf( "%d killed\n", childpid );
}

}
}

exit(EXIT_SUCCESS); }


3、pause系统调用
系统调用pause的作用是等待一个信号。该调用的声明格式如下:
int pause(void);
该调用使得发出调用的进程进入睡眠,直到接收到一个信号为止。该调用总是返回-1,并设置错误代码为EINTR(接收到一个信号)。下面是一个简单的范例: 



#include
#include
#include
void sigroutine(int unused) {
printf(“Catch a signal SIGINT “);
}



int main() {
signal(SIGINT, sigroutine);
pause();
printf(“receive a signal “);
}



4、alarm和 setitimer系统调用
系统调用alarm的功能是设置一个定时器,当定时器计时到达时,将发出一个信号给进程。该调用的声明格式如下:
unsigned int alarm(unsigned int seconds);



系 统调用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);




该系统调用给进程提供了三个定时器,它们各自有其独有的计时域,当其中任何一个到达,就发送一个相应的信号给进程,并使得计时器重新开始。三个计时器由参数which指定,如下所示: 
TIMER_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
#include
#include
#include
int sec;



void sigroutine(int signo) {
switch (signo) {
case SIGALRM:
printf(“Catch a signal – SIGALRM “);
break;
case SIGVTALRM:
printf(“Catch a signal – SIGVTALRM “);
break;
}
return;
}



int main() {
struct itimerval value,ovalue,value2;
sec = 5;



printf("process id is %d ",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 (;;) ; }


信号发送函数sigqueue和信号安装函数sigaction
sigaction函数用于改变进程接收到特定信号后的行为。



sigqueue()是比较新的发送信号系统调用,主要是针对实时信号提出的(当然也支持前32种),支持信号带有参数,与函数sigaction()配合使用。



sigqueue的第一个参数是指定接收信号的进程ID,第二个参数确定即将发送的信号,第三个参数是一个联合数据结构union sigval,指定了信号传递的参数,即通常所说的4字节值。



一,sigaction()
#include int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact));



sigaction函数用于改变进程接收到特定信号后的行为。该函数的第一个参数为信号的值,可以为除SIGKILL及SIGSTOP外的任何一个特定有效的信号(为这两个信号定义自己的处理函数,将导致信号安装错误)。第二个参数是指向结构sigaction的一个实例的指针,在结构sigaction的实例中,指定了对特定信号的处理,可以为空,进程会以缺省方式对信号处理;第三个参数oldact指向的对象用来保存原来对相应信号的处理,可指定oldact为NULL。如果把第二、第三个参数都设为NULL,那么该函数可用于检查信号的有效性。



第二个参数最为重要,其中包含了对指定信号的处理、信号所传递的信息、信号处理函数执行过程中应屏蔽掉哪些函数等等。



Category linux