Linux信号学习3 (signal、sigaction、利用捕捉回收子进程、pause、中断系统调用)

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中包含该信号。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值