kill函数
- 函数原型:
Int kill(pid_t pid, int siq)
- 功能:既可以向自身发送信号,也可以向其他进程发送信号;
- 参数:
- pid>0 将信号sig发给pid进程
- pid=0 将信号sig发给同组进程
- pid=-1 将信号sig发送给所有进程,调用者进程有权限发送的每一个进程(除了1号进程之外,还有它自身)
- pid<-1 将信号sig发送给进程组是pid(绝对值)的每一个进程
注意,如果在fork之前安装信号,则子进程可以继承信号。
补充:getpgrp()函数获取进程组pid
sleep函数
函数原型:unsigned int sleep(unsigned int seconds);
功能:让进程睡眠。原理和wait类似,都有可能被其他信号打断!
注意:
1)能被信号打断,然后处理信号函数以后,就不再睡眠了。直接向下执行代码
2)sleep函数的返回值,是剩余的秒数
- 示例代码:
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
void myhandle(int num)
{
if (num == SIGINT)
{
printf("recv signal SIGINT \n");
}
else if (num == SIGUSR1)
{
printf("recv signal SIGUSR1 \n");
}
else
{
printf("recv signal id num : %d \n", num);
}
}
int main(void)
{
pid_t pid;
printf("main ....begin\n");
if (signal(SIGINT, myhandle) == SIG_ERR)
{
perror("func signal err\n");
return 0;
}
if (signal(SIGUSR1, myhandle) == SIG_ERR)
{
perror("func signal err\n");
return 0;
}
pid = fork();
if (pid == -1)
{
printf("fork err....\n");
return 0;
}
//子进程向父进程发送信号
//子进程向同组进程发送信号
/*
if (pid == 0)
{
//pid = getpgrp();
pid = getppid();
//kill(pid, SIGUSR1); //向老爹发信号
kill(0, SIGUSR1); //向进程组发信号
//killpg(pid, SIGUSR1);
exit(0);
}
*/
if (pid == 0)
{
pid = getpgrp();
killpg(pid, SIGUSR1);
exit(0);
}
int n = 3;
do
{
printf("父进程开始睡眠\n");
n = sleep(n);
printf("父进程开始唤醒\n");
} while (n > 0);
//sleep(n);
printf("sleep 函数执行完毕以后返回...\n");
return 0;
}
raise函数
函数原型: int raise(int sig);
功能:给自己发送信号。raise(sig)等价于kill(getpid(), sig);
kill可以发送信号给其他进程
killpg函数
函数原型:int killpg(int pgrp, int sig);
功能:给进程组发送信号。killpg(pgrp, sig)等价于kill(-pgrp, sig);
sigqueue函数
函数原型:int sigqueue(pid_t pid, int sig, const union sigval value);
函数功能: 给进程发送信号,支持排队,可以附带信息
pause函数
- 将进程置为可中断睡眠状态。然后它调用内核函数schedule(),使linux进程调度器找到另一个进程来运行。
- pause使调用者进程挂起,直到一个信号被捕获就返回,不再阻塞调用者进程。
alarm函数
功能:设置一个闹钟延迟发送信号。告诉linux内核n秒以后,发送SIGALRM信号;
函数原型:unsigned int alarm(unsigned int seconds);
注意:该闹钟只是一次有效,如果要重复有效,则需要在信号处理函数里面再次调用alarm函数设置闹钟。
void myhandle(int num)
{
printf("recv signal id num : %d \n", num);
alarm(1);
}
int main(void)
{
printf("main ....begin\n");
//注册信号处理函数
if (signal(SIGALRM, myhandle) == SIG_ERR)
{
perror("func signal err\n");
return 0;
}
alarm(1);
while(1)
{
pause();
printf("pause return\n");
}
return 0;
}
信号处理函数遇上可重入和不可重入函数
所谓可重入函数是指一个可以被多个任务调用的过程,任务在调用时不必担心数据是否会出错。因为进程在收到信号后,就将跳转到信号处理函数去接着执行。--也就是说可重入函数可以被多个进程或者函数同时调用,但不必关心内部数据和全局数据是否会被修改。
如果信号处理函数中使用了不可重入函数,那么信号处理函数可能会修改原来进程中不应该被修改的数据,这样进程从信号处理函数中返回接着执行时,可能会出现不可预料的后果。不可重入函数在信号处理函数中被视为不安全函数。
满足下列条件的函数多数是不可再入的:
(1)使用静态的数据结构,如getlogin(),gmtime(),getgrgid(),getgrnam(),getpwuid()
以及getpwnam()
等等;
(2)函数实现时,调用了malloc()
或者free()
函数
(3)实现时使用了标准I/O函数的
结论:在信号处理函数中,尽量不使用含有全局变量和静态变量的函数。特别是这个变量在程序中随时可能读写。
man 7 signal 查找可重入函数和不可重入函数。
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
typedef struct _Teacher
{
int age;
int num;
}Teacher;
Teacher g_t;
char *p = NULL:
void printfGlobTeacher()
{
printf("g_t.age:%d \n", g_t.age);//含有全局变量--不可重入--gt一直在改变
printf("g_t.num:%d \n", g_t.num);
//p = malloc(100);//容易引起不可重入
}
void myhandle(int num)
{
printf("recv signal id num : %d \n", num);
printfGlobTeacher();
alarm(1);
}
int main(void)
{
Teacher t1, t2;
t1.age = 30;
t1.num = 30;
t2.age = 40;
t2.num = 40;
printf("main ....begin\n");
//注册信号处理函数
if (signal(SIGALRM, myhandle) == SIG_ERR)
{
perror("func signal err\n");
return 0;
}
//间接递归
//myhandle----->alarm=====>myhandle
alarm(1);
while(1)
{
g_t = t1;
g_t = t2;
}
return 0;
}