信号处理函数

1.设置信号处理函数

用户 可以提供自己的信号处理函数,然后使用signal函数将处理函数加载,函数原型

 

#include

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

 

signo表示信号值,func表示一个函数的指针,用来捕获指定的信号。func可去如下值之一:

SIG_IGN    忽略该信号

SIG_DFL   使用系统默认的方式处理

SIG_ERR 

 

signal函数的返回值也会死一个函数的指针,这个指针执行上一次的信号处理程序。如果出错,返回SIG_ERR

 

//sig.c

#include
#include
#include
#include
 
void handler(int signo)
{
    switch(signo){
    case SIGUSR1:
        printf("Parent : catch SIGUSR1/n");
        break;
    case SIGUSR2:
        printf("Child : catch SIGUSR2/n");
        break;
    default:  //因为本程序只使用SIGUSR1和SIGUSR2两个信号,所以绝对不会执行到这里
        printf("should not be here/n");
        break;
    }
 
    return ;
}
 
int main(void)
{
    pid_t ppid, cpid;

        //设置信号处理程序
    if(signal(SIGUSR1, handler) == SIG_ERR){
        perror("can¡¯t set handler for SIGUSR1");
        exit(1);
    }
 
    if(signal(SIGUSR2, handler) == SIG_ERR){
        perror("can¡¯t set handler for SIGUSR2");
        exit(1);
    }
 
    ppid = getpid();
 
    if((cpid = fork()) <0){
        perror("fail to fork");
        exit(1);
    }else if(cpid == 0){
        if(kill(ppid, SIGUSR1) == -1){  //向父进程发送SIGUSR1信号
            perror("fail to send signal");
            exit(1);
        }
 
        while(1);//死循环,等待父进程的信号
    }else{
        sleep(1);//休眠,保证子进程先运行
 
        if(kill(cpid, SIGUSR2) == -1){//向子进程发送SIGUSR2
            perror("fail to send signal");
            exit(1);
        }
 
        printf("kill child/n");
 
        if(kill(cpid, SIGKILL) == -1){//发送SIGKILL杀死子进程
            perror("fail to send signal");
            exit(1);
        }
 
        if(wait(NULL) == -1){//回收子进程,避免产生僵尸进程
            perror("fail to wait");
            exit(1);
        }
    }
 
    return 0;
}

 

2.发送信号

使用kill想进程或进程组发送信号,函数原型如下:

 

#include

int kill(pid_t pid,int signo);

 

                                                                 pid的取值和意义

pid的取值                                      所代表的意义

pid>0                                             将信号发送给进程id为pid的进程

pid==0                                           将此信号发送给进程组id和该进程相同的进程

pid<0                                              将此信号发送给进程组内的进程id为pid的进程

pid=-1                                             将此信号发给系统的所有进程

 

 

3.向进程本身发送信号

 

使用kill如下实现:

kill(getpid(),signo);

 

linux定义了实现此功能的函数,其函数原型如下:

 

#include

int raise(int signo);

 

成功返回0,失败返回-1。

 

 

4.设置linux定时器

函数原型:

 

#include

unsigned int alarm(unsigned int sec);

 

从设置定时器开始,如果系统时间超过该时间后就会向调用alarm函数的进程发送衣蛾SIGALRM信号,这个信号的默认动作是终止调用alarm函数的进程。

如果此前没有设置过定时器,或者设置过定时器但已经超时,alarm的返回值为0;如果此前设置的定时器没有超时,则返回该定时器剩余的秒数。参数为0表示取消一个定时器。

 

5.挂起进程

进程自愿进入阻塞态的情况称为进程挂起。

 

#include

int pause();

 

进入挂起状态后,直到一个信号到来,并且去处理了该信号并返回后,pause才返回,返回值是-1。


pause后不再相应SIGTERM等信号,唯一能保证杀死进程的方法是使用kill命令,发送SIGKILL信号。


6.进程休眠

 

#include

unsigned int sleep(unsigned int sec);

 

返回值有两种:一是挂起的时间超过了指定的时间,这是返回0,二是挂起期间被信号唤醒,此时返回挂起以来的时间。

 

//my_sleep.c

#include
#include
#include
#include
#include
#include
 
void sigalrm_handler(int signo)
{
 
}
 
void sigusr1_handler(int signo)
{
    printf("catch SIGUSR1/n");
}
 
unsigned int my_sleep(unsigned int nsec)
{
    void (*p)(int );

        //p用来保存原来的信号处理程序的指针
    if( (p = signal(SIGALRM, sigalrm_handler)) == SIG_ERR){ 
        perror("can¡¯t set handler for SIGALRM");
        exit(1);
    }
 
    alarm(nsec);//设置定时器
 
    pause(); //挂起程序,等待被定时器唤醒
     
    if(signal(SIGALRM, p) == SIG_ERR){//恢复SIGALRM的处理函数
        perror("can¡¯t rescue handler for SIGALRM");
        exit(1);
    }
     
    return alarm(0);
}
 
int main(void)
{
    struct timeval begintime, endtime;
        float elapsed;
    unsigned int rest;
       
         //设置SIGUSR1的信号处理函数,如果不设置的话,SIGUSR1信号不能从pause的函数中唤醒进程*/     
    if(signal(SIGUSR1, sigusr1_handler) == SIG_ERR){ 
        perror("can¡¯t set handler for SIGUSR1");
        exit(1);
    }
     
    printf("the first time/n");//第一次运行,让进程正常的被定时器唤醒
    printf("before sleeping/n");
 
    gettimeofday(&begintime, NULL);
 
    my_sleep(10);
 
    printf("after sleep/n");
     
    gettimeofday(&endtime, NULL);
     
    elapsed = 1000000 * (endtime.tv_sec - begintime.tv_sec) + 
        endtime.tv_usec - begintime.tv_usec; 
        elapsed /= 1000000;
     
    printf("elapsed time is %f/n", elapsed);
     
    printf("the second time/n");//第二次运行,让进程被信号唤醒
    printf("before sleeping/n");
 
    gettimeofday(&begintime, NULL);
     
    rest = my_sleep(20);
     
    printf("after sleeping/n");
    gettimeofday(&endtime, NULL);
 
    elapsed = 1000000 * (endtime.tv_sec - begintime.tv_sec) + 
        endtime.tv_usec - begintime.tv_usec; 
        elapsed /= 1000000;
     
    printf("actual sleeping-time is %f/n", elapsed);
    printf("the rest is %u/n", rest);
     
    return 0;
}

 

运行程序,输出:

the first time

before sleeping

10秒后,输出:

after sleep
elapsed time is 10.000083
the second time
before sleeping
这是查找该进程的pid,在另一个终端向该进程发送SIGUSR1信号

kill -USR1 xxx

输出:

catch SIGUSR1
after sleeping
actual sleeping-time is 6.852226
the rest is 13

你可能感兴趣的:(linux编程)