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