文章目录
linux中signal函数详解
函数说明
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
函数参数说明
- signum要捕捉的信号
- handler要执行的捕捉函数指针,函数应用声明void func(int);
linux中sigaction函数详解
函数原型:sigaction函数的功能是检查或修改与指定信号相关联的处理动作(可同时两种操作)
int sigaction(int signum, const struct sigaction *act,
struct sigaction *oact);
signum参数指出要捕获的信号类型,act参数指定新的信号处理方式,oact参数输出先前信号的处理方式(如果不为NULL的话)。
struct sigaction结构体介绍
struct sigaction {
void (*sa_handler)(int); /* addr of signal handler, */
/* or SIG_IGN, or SIG_DFL */
sigset_t sa_mask; /* additional signals to block */
int sa_flags; /* signal options, Figure 10.16 */
/* alternate handler */
void (*sa_sigaction)(int, siginfo_t *, void *);
};
- sa_handler此参数和signal()的参数handler相同,代表新的信号处理函数
- sa_mask 用来设置在处理该信号时,暂时将sa_mask 指定的信号集搁置
- sa_flags 用来设置信号处理的其他相关操作,下列的数值可用。 (一般填0,SA_SIGINFO会使用第二个函数指针)
- SA_RESETHAND:当调用信号处理函数时,将信号的处理函数重置为缺省值SIG_DFL
- SA_RESTART:如果信号中断了进程的某个系统调用,则系统自动启动该系统调用
- SA_NODEFER :一般情况下, 当信号处理函数运行时,内核将阻塞该给定信号。但是如果设置了 - SA_NODEFER标记, 那么在该信号处理函数运行时,内核将不会阻塞该信号
例子1
通信sigaction捕捉setitimer产生的中断信号
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/time.h>
void catch_sig(int num){
printf("catch %d sig\n",num);
}
int main(int argc, char const *argv[])
{
//设置结构体
struct sigaction act;
act.sa_flags = 0;
act.sa_handler = catch_sig;
sigemptyset(&act.sa_mask);
//注册捕捉
sigaction(SIGALRM,&act,NULL);
//setitimer
struct itimerval myit = {{3,0},{5,0}};//间隔3秒,延迟5秒
setitimer(ITIMER_REAL,&myit,NULL);
while (1)
{
printf("who can kill me!\n");
sleep(1);
}
return 0;
}
输出
who can kill me!
who can kill me!
who can kill me!
who can kill me!
who can kill me!
catch 14 sig
who can kill me!
who can kill me!
who can kill me!
catch 14 sig
who can kill me!
who can kill me!
who can kill me!
catch 14 sig
who can kill me!
who can kill me!
who can kill me!
catch 14 sig
who can kill me!
who can kill me!
who can kill me!
catch 14 sig
who can kill me!
who can kill me!
who can kill me!
catch 14 sig
who can kill me!
...
例子2——临时屏蔽信号
信号捕捉特性
1.进程正常运行时,默认PCB中有一个信号屏蔽字,假定为☆,它决定了进程自动屏
蔽哪些信号。当注册了某个信号捕捉函数,捕捉到该信号以后,要调用该函数。而
该函数有可能执行很长时间,在这期间所屏蔽的信号不由☆来指定。而是用 sa mask
来指定。调用完信号处理函数,再恢复为☆。
2.XX信号捕捉函数执行期间;XX信它动被屏蔽。
3.阻塞的常规号不支持排队,产生多次只记录一次。(后32个实时信号支持排队)
练习
- 为某个信号设置捕捉函数
- 验证在信号处理函数执行期间,该信号多次递送,那么只在处理函数之行结束后,处理一次。
- 验证sa_mask在捕捉函数执行期间的屏蔽作用
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void catch_sig(int num){
printf("begin call %d sig\n",num);
sleep(5);//模拟捕获函数执行时间较长
printf("end call ,catch %d sig\n",num);
}
int main(int argc, char const *argv[])
{
struct sigaction act;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
sigaddset(&act.sa_mask,SIGQUIT);//临时屏蔽信号(ctrl + \)
act.sa_handler = catch_sig;
//注册捕捉
sigaction(SIGINT,&act,NULL);
while (1){
printf("who can kill me?\n");
sleep(1);
}
return 0;
}
输出
测试发送信号(短时间多次按下Ctrl + c)
$ ./a.out
who can kill me?
who can kill me?
who can kill me?
who can kill me?
^Cbegin call 2 sig
end call ,catch 2 sig
who can kill me?
who can kill me?
who can kill me?
who can kill me?
^Cbegin call 2 sig
^C^C^C^Cend call ,catch 2 sig
begin call 2 sig
end call ,catch 2 sig
who can kill me?
who can kill me?
who can kill me?
^Cbegin call 2 sig
^\^\^\^\^C^C^Cend call ,catch 2 sig
begin call 2 sig
end call ,catch 2 sig
退出
$
注意,上面的输出中,^C
是在终端按下Ctrl + c
键,^\
是按下Ctrl + \
键1,发出的信号2。
下面的输出(第20行)说明在信号执行期间临时屏蔽(退出信号和中断信号,实际是屏蔽了两个信号),信号执行完毕,执行了退出操作。
^\^\^\^\^C^C^Cend call ,catch 2 sig
例子3——借助SIGCHLD信号回收子进程
子进程结束运行,其父进程会收到SIGCHLD信号。该信号的默认处理动作是忽略。可以捕捉该信号 ,在捕捉函数中完成子进程状态的回收。
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
void catch_sig(int num){
pid_t wpid;
while ((wpid = waitpid(-1,NULL,WNOHANG))>0){
printf("wait child %d ok \n",wpid);
}
}
int main(int argc, char const *argv[])
{
int i = 0;
pid_t pid;
//在创建子进程之前屏蔽SIGCHLD信号
sigset_t myset,oldset;
sigemptyset(&myset);
sigaddset(&myset,SIGCHLD);
//oldset 保留现场,设置了SIGCHLD到阻塞信号集
sigprocmask(SIG_BLOCK,&myset,&oldset);
for(i = 0;i<10;i++){
pid = fork();
if(pid ==0){
break;
}
}
if( i==10){
//parent
struct sigaction act;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
act.sa_handler = catch_sig;
sigaction(SIGCHLD,&act,NULL);
//解除屏蔽现场
sigprocmask(SIG_SETMASK,&oldset,NULL);
while (1){
sleep(1);
}
}else if(i<10){
printf("I am %d child,pid = %d\n",i,getpid());
sleep(1);
}
return 0;
}
其他
内核捕捉信号过程
setitimer函数
释意
set value of an interval timer
代码
#include <sys/time.h>
int getitimer(int which, struct itimerval *curr_value);
int setitimer(int which, const struct itimerval *new_value,
struct itimerval *old_value);
参数1,整型which
ITIMER_REAL This timer counts down in real (i.e., wall clock) time. At each expiration, a SIGALRM signal is gener‐
ated.
ITIMER_VIRTUAL This timer counts down against the user-mode CPU time consumed by the process. (The measurement in‐
cludes CPU time consumed by all threads in the process.) At each expiration, a SIGVTALRM signal is gen‐
erated.
ITIMER_PROF This timer counts down against the total (i.e., both user and system) CPU time consumed by the process.
(The measurement includes CPU time consumed by all threads in the process.) At each expiration, a SIG‐
PROF signal is generated.
In conjunction with ITIMER_VIRTUAL, this timer can be used to profile user and system CPU time consumed
by the process.
参数2,参数3,结构体 itimerval
struct itimerval {
struct timeval it_interval; /* Interval for periodic timer */
struct timeval it_value; /* Time until next expiration */
};
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
sigemptyset函数
sigemptyset()
initializes the signal set given by set to empty, with all signals excluded from the set.
将set给定的信号集初始化为空,并将所有信号排除在集合之外
sigaddset和sigdelset函数
sigaddset() and sigdelset()
add and delete respectively signal signum from set.
分别从集合中添加和删除信号
sigprocmask函数
设置阻塞信号集
#include <signal.h>
/* Prototype for the glibc wrapper function */
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
参数how
SIG_BLOCK
The set of blocked signals is the union of the current set and the set argument.
SIG_UNBLOCK
The signals in set are removed from the current set of blocked signals. It is permissible to attempt to unblock a signal which is not blocked.
SIG_SETMASK
The set of blocked signals is set to the argument set.
waitpid函数
转自——waitpid()函数详解
函数原型
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *wstatus);
pid_t waitpid(pid_t pid, int *wstatus, int options);
int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
参数——pid
从参数的名字pid和类型pid_t中就可以看出,这里需要的是一个进程ID。但当pid取不同的值时,在这里有不同的意义。
- pid>0时,只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去。
- pid=-1时,等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样。
- pid=0时,等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬。
- pid<-1时,等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。
参数——options
options提供了一些额外的选项来控制waitpid,目前在Linux中只支持WNOHANG和WUNTRACED两个选项,这是两个常数,可以用"|"运算符把它们连接起来使用,比如:
ret=waitpid(-1,NULL,WNOHANG | WUNTRACED);
如果我们不想使用它们,也可以把options设为0,如:
ret=waitpid(-1,NULL,0);
如果使用了WNOHANG(wait no hung)参数调用waitpid,即使没有子进程退出,它也会立即返回,不会像wait那样永远等下去。
而WUNTRACED参数,由于涉及到一些跟踪调试方面的知识,加之极少用到,这里就不多费笔墨了,有兴趣的读者可以自行查阅相关材料。
wait不就是经过包装的waitpid吗?没错,察看<内核源码目录>/include/unistd.h文件349-352行就会发现以下程序段:
static inline pid_t wait(int * wait_stat) { return waitpid(-1,wait_stat,0); }
返回值和错误
返回值和错误
waitpid的返回值比wait稍微复杂一些,一共有3种情况:
- 当正常返回的时候,waitpid返回收集到的子进程的进程ID;
- 如果设置了选项
WNOHANG
,而调用中waitpid发现没有已退出的子进程可收集,则返回0; - 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
当pid所指示的子进程不存在,或此进程存在,但不是调用进程的子进程,waitpid就会出错返回,这时errno被设置为ECHILD;
信号表
man 7 signal查看信号
SIGHUP 1 Term Hangup detected on controlling terminal
or death of controlling process
SIGINT 2 Term Interrupt from keyboard (中断)
SIGQUIT 3 Core Quit from keyboard (退出)
SIGILL 4 Core Illegal Instruction
SIGABRT 6 Core Abort signal from abort(3)
SIGFPE 8 Core Floating-point exception
SIGKILL 9 Term Kill signal
SIGSEGV 11 Core Invalid memory reference
SIGPIPE 13 Term Broken pipe: write to pipe with no
readers; see pipe(7)
SIGALRM 14 Term Timer signal from alarm(2)
SIGTERM 15 Term Termination signal
SIGUSR1 30,10,16 Term User-defined signal 1
SIGUSR2 31,12,17 Term User-defined signal 2
SIGCHLD 20,17,18 Ign Child stopped or terminated
SIGCONT 19,18,25 Cont Continue if stopped
SIGSTOP 17,19,23 Stop Stop process
SIGTSTP 18,20,24 Stop Stop typed at terminal
SIGTTIN 21,21,26 Stop Terminal input for background process
SIGTTOU 22,22,27 Stop Terminal output for background process
SIGCHLD信号
SIGCHLD信号的产生条件
- 子进程终止时
- 子进程接收到SIGSTOP信号停止时
- 子进程处在停止态,接受到SIGCONT后唤醒时