目录
signal函数
signal函数说明
#include <signal.h>
typedef void (*sighandler_t)(int); //句柄函数,返回值为空,参数为整型的函数。
sighandler_t signal(int signum, sighandler_t handler);//信号捕捉
参数:
signum :待捕捉信号
handler:捕捉信号后的操纵函数
返回值:signal() returns the previous value of the signal handler, or SIG_ERR on error. In the event of an error,errno is set to indicate the cause.
signal.c测试
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <pthread.h>
#include <sys/mman.h>
#include <errno.h>
#include <signal.h>
void sys_error(const char* string1){
perror(string1);
exit(1);
}
void print_nihao(int signalNo){//这里的signalNo表示信号的位图位置
printf("nihao\n");
}
int main(int argc,char* argv[]){
//sleep(3);
signal(SIGINT,print_nihao);//注册一个信号捕捉函数
while(1){
}
return 0;
}
测试结果
haitu@ubuntu:/opt/modbus_test/linux_test/signal/signal2$ ./signal
^Cnihao
^Cnihao
^Cnihao
^\退出 (核心已转储)
sigaction函数
说明:注册一个信号捕捉函数
sigaction, rt_sigaction - examine and change a signal action
函数体:
#include <signal.h>
int sigaction(int signum, const struct sigaction *act,
struct sigaction *oldact);
参数:
signum:捕捉信号编号
struct sigaction {
void (*sa_handler)(int);//捕捉函数
void (*sa_sigaction)(int, siginfo_t *, void *);//一般不使用
sigset_t sa_mask;//屏蔽字,只工作在信号捕捉函数期间。一般为空的
int sa_flags;//一般为0
void (*sa_restorer)(void);//已经弃用
};
act:新的处理动作
oldact:旧的处理动作,传出参数
返回值:
sigaction() returns 0 on success; on error, -1 is returned, and errno is set to indicate the error.
sigaction函数测试
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <pthread.h>
#include <sys/mman.h>
#include <errno.h>
#include <signal.h>
void sys_error(const char* string1){
perror(string1);
exit(1);
}
void print_nihao(int signalNo){
printf("nihao\n");
}
int main(int argc,char* argv[]){
struct sigaction act,oldact;
act.sa_handler = print_nihao;
sigemptyset(&(act.sa_mask));//清空sa_mask,一般清空就可以
//sigaddset(&(act.sa_mask),SIGINT)
act.sa_flags = 0;//默认一般sa_flags=0,本信号屏蔽。
int ret = sigaction(SIGINT,&act,&oldact);
if(ret == -1){
sys_error("sigaction error")
}
while(1){
}
return 0;
}
测试结果
haitu@ubuntu:/opt/modbus_test/linux_test/signal/signal2$ ./sigaction
^Cnihao
^Cnihao
^Cnihao
^Cnihao
|^H^\退出 (核心已转储)
信号捕捉的特性
信号捕捉特性:
1. 捕捉函数执行期间,信号屏蔽字 由 mask --> sa_mask , 捕捉函数执行结束。 恢复回 mask
2. 捕捉函数执行期间,本信号自动被屏蔽(sa_flgs = 0).
3. 捕捉函数执行期间,被屏蔽信号多次发送,解除屏蔽后只处理一次!
利用SIGCHLD信号回收子进程
SIGCHLD信号参数条件
子进程终止时
子进程接收到SIGSTOP信号停止时
子进程处在停止态,接受到SIGCONT后唤醒时
总结:只要子进程的状态发生变化,那么就会产生SIGCHLD信号。
测试程序
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <pthread.h>
#include <sys/mman.h>
#include <errno.h>
#include <sys/wait.h>
void sys_error(const char* string1){
perror(string1);
exit(1);
}
void do_sig_child(int signalNo){//信号捕捉函数
pid_t wpid;
// int status;//用于获取子进程返回的值
//while((wpid = waitpid(0,&status,WNOHANG)) > 0){//非阻塞回收子进程
// if (WIFEXITED(status))
// //returns true if the child terminated normally, that is, by calling exit(3)
// //or _exit(2), or by returning from main()
// printf("child %d exit %d\n", wpid, WEXITSTATUS(status));
// else if (WIFSIGNALED(status))
// //returns true if the child process was terminated by a signal
// printf("child %d cancel signal %d\n", wpid, WTERMSIG(status));
// }
// printf("catch pid --------------%d\n",wpid);
//}
//信号是不排队的,当捕捉时,一下子两个子进程死亡
//循环是因为,防止出现子进程同时结束,没有被回收
//回收多个子进程就用循环
while((wpid = wait(NULL)) != -1){//回收子进程
printf("catch pid --------------%d\n",wpid);
}
return;
}
int main(int argc,char* argv[]){
pid_t pid;
//阻塞 (解决信号捕捉还没有注册完,子进程就已经执行完毕了)
//sigset_t set,oldset;//创建的信号集
//sigemptyset(&set);//清空信号集
//sigaddset(&set, SIGCHLD);//添加各种信号
//设置信号屏蔽字
//int ret = sigprocmask(SIG_BLOCK,&set,&oldset);//将自我创建的信号集进行阻塞。
//if(ret == -1){
// sys_error("sigprocmask error");
//}
int i;
for(i = 0;i<5;i++){
pid = fork();//循环创建子进程
if(pid == 0){
break;
}else if(pid < 0){
sys_error("fork error");
}
}
if(pid > 0){//父进程
struct sigaction act;
act.sa_handler = do_sig_child;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGCHLD, &act, NULL);//设置捕捉SIGCHLD的函数
//解除阻塞
//ret = sigprocmask(SIG_UNBLOCK,&set,&oldset);//将自我创建的信号集进行阻塞。
//if(ret == -1){
// sys_error("sigprocmask error");
//}
printf("i am parent ,pid == %d\n",getpid());
while(1);//让父进程不死亡
}else if(pid == 0){//子进程
printf("i am child ,pid == %d,parent pid == %d\n",getpid(),getppid());
return i++;
}
return 0;
}
//附录:
/*pid_t waitpid(pid_t pid, int *status, int options)
options:
WNOHANG
没有子进程结束,立即返回
WUNTRACED
如果子进程由于被停止产生的SIGCHLD,waitpid则立即返回
WCONTINUED
如果子进程由于被SIGCONT唤醒而产生的SIGCHLD,waitpid则立即返回
status:
WIFEXITED(status)
子进程正常exit终止,返回真
WEXITSTATUS(status)
返回子进程正常 退出值
WIFSIGNALED(status)
子进程被信号终止,返回真
WTERMSIG(status)
返回终止子进程的信号值
WIFSTOPPED(status)
子进程被停止,返回真
WSTOPSIG(status)
返回停止子进程的信号值
WIFCONTINUED(status)
*/
测试结果
haitu@ubuntu:/opt/modbus_test/linux_test/signal/signal2$ ./wait_sigchld
i am child ,pid == 12925,parent pid == 12924
i am parent ,pid == 12924
i am child ,pid == 12929,parent pid == 12924
i am child ,pid == 12926,parent pid == 12924
catch pid --------------12929
catch pid --------------12925
i am child ,pid == 12927,parent pid == 12924
catch pid --------------12926
i am child ,pid == 12928,parent pid == 12924
catch pid --------------12927
catch pid --------------12928
^C
pause函数
pause - wait for signal 使得进程休眠(挂起),返回值总是-1
#include <unistd.h>
int pause(void);
使调用进程(线程)进入休眠状态(就是挂起);直到接收到信号且信号函数成功返回 pause函数才会返回
pause测试
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
void sys_error(const char* string1){
perror(string1);
exit(1);
}
void do_signal(int signal){
printf("signalNo = %d\n",signal);
}
int main(int argc,char* argv[]){
signal(SIGINT,do_signal);
/*这里也可以使用sigaction函数
struct sigaction act;
act.sa_handler = do_signal;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT, &act, NULL);//设置捕捉SIGCHLD的函数
*/
printf("pause before\n");
pause();
printf("pause after\n");
return 0;
}
/*运行结果
haitu@ubuntu:/opt/modbus_test/linux_test/signal/signal2$ ./pause
pause before
^CsignalNo = 2
pause after
*/
中断系统调用
系统调用可分为两类:慢速系统调用和其他系统调用。
1.慢速系统调用:可能会使进程永远阻塞的一类。如果在阻塞期间收到一个信号,该系统调用就被中断,不再继续执行(早 期);也可以设定系统调用是否重启。如,read、write、pause、wait...
2.其他系统调用:getpid、getppid、fork...
慢速系统调用被中断的相关行为,实际上就是pause的行为: 如,read
① 想中断pause,信号不能被屏蔽。
② 信号的处理方式必须是捕捉 (默认、忽略都不可以)
③ 中断后返回-1, 设置errno为EINTR(表“被信号中断”)
可修改sa_flags参数来设置被信号中断后系统调用是否重启。SA_INTERRURT不重启。 SA_RESTART重启。
扩展了解:
sa_flags还有很多可选参数,适用于不同情况。如:捕捉到信号后,在执行捕捉函数期间,不希望自动阻塞该信号,可将sa_flags设置为SA_NODEFER,除非sa_mask中包含该信号。