Linux 使用之信号相关函数

信号发送

	int kill(pid_t pid, int signum) //kill 命令产生信号
	参数:pid: 	
				> 0:发送信号给指定进程
				= 0:发送信号给跟调用kill函数的那个进程处于同一进程组的进程。
				< -1: 取绝对值,发送信号给该绝对值所对应的进程组的所有组员。如kill -9 -10698 杀死进程组id为10698的所有进程 kill -9 -组id
				= -1:发送信号给,有权限发送的所有进程。
		signum:待发送的信号
	返回值:
		成功: 0
		失败: -1 errno

软件条件产生信号

alarm 函数:使用自然计时法。
	定时发送SIGALRM给当前进程。
	unsigned int alarm(unsigned int seconds); 	//seconds:定时秒数 返回值:上次定时剩余时间。 
	alarm(0); 取消闹钟。
	time 命令 : 查看程序执行时间。   实际时间 = 用户时间 + 内核时间 + 等待时间。  --> 优化瓶颈 IO

setitimer函数:
	int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
	参数:
		which:	ITIMER_REAL: 采用自然计时。 ——> SIGALRM
				ITIMER_VIRTUAL: 采用用户空间计时  ---> SIGVTALRM
				ITIMER_PROF: 采用内核+用户空间计时 ---> SIGPROF
		new_value:定时秒数
		           类型:struct itimerval {
               				struct timeval {
               					time_t      tv_sec;         /* seconds */
               					suseconds_t tv_usec;        /* microseconds */
           				}it_interval;---> 周期定时秒数
               				 struct timeval {
               					time_t      tv_sec;         
               					suseconds_t tv_usec;        
           				}it_value;  ---> 第一次定时秒数  
           			 };
		old_value:传出参数,上次定时剩余时间。
		e.g.
			struct itimerval new_t;	
			struct itimerval old_t;	

			new_t.it_interval.tv_sec = 0;
			new_t.it_interval.tv_usec = 0;
			new_t.it_value.tv_sec = 1;
			new_t.it_value.tv_usec = 0;

			int ret = setitimer(&new_t, &old_t);  定时1秒

	返回值:
		成功: 0
		失败: -1 errno

其他几个发信号函数:
	int raise(int sig);
	void abort(void);

信号集操作函数

sigset_t set; 	// typedef unsigned long sigset_t; 
int sigemptyset(sigset_t *set); 	//将某个信号集清 0 成功:0;失败:-1
int sigfillset(sigset_t *set); 	//将某个信号集置 1 成功:0;失败:-1
int sigaddset(sigset_t *set, int signum); 	//将某个信号加入信号集 成功:0;失败:-1
int sigdelset(sigset_t *set, int signum); 	//将某个信号清出信号集 成功:0;失败:-1
int sigismember(const sigset_t *set, int signum);	//判断某个信号是否在信号集中 返回值:在集合:1;不在:0;出错:-1 
int sigpending(sigset_t *set); 	//读取当前进程的未决信号集   set 传出参数。 返回值:成功:0;失败:-1,设置 errno
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); 	//成功:0;失败:-1,设置 errno
	用来屏蔽信号、解除屏蔽也使用该函数。其本质,读取或修改进程的信号屏蔽字(PCB 中)
	严格注意,屏蔽信号:只是将信号处理延后执行(延至解除屏蔽);而忽略表示将信号丢处理。
参数:
	set:传入参数,是一个位图,set 中哪位置 1,就表示当前进程屏蔽哪个信号。
	oldset:传出参数,保存旧的信号屏蔽集。
	how 参数取值: 假设当前的信号屏蔽字为 mask
	1. SIG_BLOCK: 当 how 设置为此值,set 表示需要屏蔽的信号。相当于 mask = mask|set
	2. SIG_UNBLOCK: 当 how 设置为此,set 表示需要解除屏蔽的信号。相当于 mask = mask & ~set
	3. SIG_SETMASK: 当 how 设置为此,set 表示用于替代原始屏蔽及的新屏蔽集。相当于 mask = set
	若,调用 sigprocmask 解除了对当前若干个信号的阻塞,则在 sigprocmask 返回前,至少将其中一
	个信号递达。
sigset_t 类型的本质是位图。但不应该直接使用位操作,而应该使用上述函数,保证跨系统操作有效。

使用

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>

void sys_err(const char *str)
{
    perror(str);
    exit(1);
}

void print_set(sigset_t *set)   //判断信号集内容
{
    int i;
    for (i = 1; i<32; i++) {
        if (sigismember(set, i)) 
            putchar('1');
        else 
            putchar('0');
    }
    printf("\n");
}
int main(int argc, char *argv[])
{
    sigset_t set, oldset, pedset;
    int ret = 0;

    sigemptyset(&set);    
    sigaddset(&set, SIGINT);
    sigaddset(&set, SIGQUIT);
    sigaddset(&set, SIGBUS);
    sigaddset(&set, SIGKILL);

    ret = sigprocmask(SIG_BLOCK, &set, &oldset);   //屏蔽信号
    if (ret == -1)
        sys_err("sigprocmask error");

    while (1) {
        ret = sigpending(&pedset);
        print_set(&pedset);
        sleep(1);
    }

    return 0;
}

信号捕捉函数signal

typedef void (*sighandler_t)(int);   //处理动作 回调函数
sighandler_t signal(int signum, sighandler_t handler);  //捕捉函数siganl

使用

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>

void sys_err(const char *str)
{
	perror(str);
	exit(1);
}

void sig_catch(int signo)
{
    printf("catch you!! %d", signo);

    return ;
}

int main(int argc, char *argv[])
{
    signal(SIGINT, sig_catch);

    while (1);

	return 0;
}

信号捕捉函数sigaction

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); 
		成功:0;失败:-1,设置 errno
参数:
		act:传入参数,新的处理方式。
		oldact:传出参数,旧的处理方式。 

struct sigaction 结构体

	struct sigaction {
		void (*sa_handler)(int);
		void (*sa_sigaction)(int, siginfo_t *, void *);
		sigset_t sa_mask; 
		int sa_flags; 
		void (*sa_restorer)(void);
	};
	sa_restorer:该元素是过时的,不应该使用,POSIX.1 标准将不指定该元素。(弃用)
	sa_sigaction:当 sa_flags 被指定为 SA_SIGINFO 标志时,使用该信号处理程序。(很少使用) 
	重点掌握:
	① sa_handler:指定信号捕捉后的处理函数名(即注册函数)。也可赋值为 SIG_IGN 表忽略 或 SIG_DFL 表执行默认动作
	② sa_mask: 调用信号处理函数时,所要屏蔽的信号集合(信号屏蔽字)。注意:仅在处理函数被调用期间屏蔽生效,是临时性设置。
	③ sa_flags:通常设置为 0,表使用默认属性。(设置被信号中断后系统调用是否重启。SA_INTERRURT 不重启。 SA_RESTART 重启)

sigaction使用

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>

void sys_err(const char *str)
{
	perror(str);
	exit(1);
}

void sig_catch(int signo)                   // 回调函数
{
    if (signo == SIGINT) {
        printf("catch you!! %d\n", signo);
        sleep(10);          //模拟信号处理函数执行很长时间
    }
    /*
    else if (signo == SIGQUIT)                     
        printf("-----------catch you!! %d\n", signo);
    */
    return ;
}

int main(int argc, char *argv[])
{
    struct sigaction act, oldact;

    act.sa_handler = sig_catch;         //设置回调函数
    sigemptyset(&(act.sa_mask));        //清空sa_mask屏蔽字, 只在sig_catch工作时有效
    sigaddset(&act.sa_mask, SIGQUIT);   //将SIGQUIT加入信号屏蔽集,这就导致,在调用信号处理函数期间
	 /*不仅不响应SIGINT信号本身,还不响应SIGQUIT*/
	 
    act.sa_flags = 0;                   //默认值
    
    int ret = sigaction(SIGINT, &act, &oldact);     //注册信号捕捉函数
    if (ret == -1)
        sys_err("sigaction error");
//  ret = sigaction(SIGQUIT, &act, &oldact);     //注册信号捕捉函数 可以捕捉不同信号使用同一个动作,只需动作里识别信号

    while (1);
	return 0;
}

信号捕捉特性

  • 进程正常运行时,默认 PCB中有一个信号屏蔽字,假定为☆,它决定了进程自动屏蔽哪些信号。当注册了某个信号捕捉函数,捕捉到该信号以后,要调用该函数。而该函数有可能执行很长时间,在这期间所屏蔽的信号不由☆来指定。而是用sa_mask 来指定。调用完信号处理函数,再恢复为☆。
  • XXX 信号捕捉函数执行期间,XXX 信号自动被屏蔽。
  • 阻塞的常规信号不支持排队,产生多次只记录一次。(后 32 个实时信号支持排队)

使用SIGCHLD 信号完成子进程回收

SIGCHLD 的产生条件

  • 子进程终止时
  • 子进程接收到 SIGSTOP 信号停止时
  • 子进程处在停止态,接受到 SIGCONT 后唤醒时

SIGCHLD 信号注意问题

  1. 子进程继承父进程的信号屏蔽字和信号处理动作,但子进程没有继承未决信号集 spending。
  2. 注意注册信号捕捉函数的位置。
  3. 应该在 fork 之前,阻塞 SIGCHLD 信号。注册完捕捉函数后解除阻塞。

SIGCHLD 信号使用

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <errno.h>
#include <pthread.h>

void sys_err(const char *str)
{
    perror(str);
    exit(1);
}

void catch_child(int signo)
{
    pid_t wpid;
    int status;

    while((wpid = waitpid(-1, &status, 0)) != -1) {         // 循环回收,防止僵尸进程出现.
        if (WIFEXITED(status))
            printf("catch child id %d, ret=%d\n", wpid, WEXITSTATUS(status));
    }
    return ;
}

int main(int argc, char *argv[])
{
    pid_t pid;

    sigset_t set, oldset, pedset;  //fork之前,阻塞SIGCHLD信号
    int ret = 0;
    sigemptyset(&set);    
    sigaddset(&set, SIGCHLD);
    ret = sigprocmask(SIG_BLOCK, &set, &oldset);
    if (ret == -1)
        sys_err("sigprocmask BLOCK error");

    int i; 
    for (i = 0; i < 15; i++)
        if ((pid = fork()) == 0)                // 创建多个子进程
            break;

    if (15 == i) {								//父进程
        struct sigaction act;
        act.sa_handler = catch_child;           // 设置回调函数
        sigemptyset(&act.sa_mask);              // 设置捕捉函数执行期间屏蔽字
        act.sa_flags = 0;                       // 设置默认属性, 本信号自动屏蔽
        sigaction(SIGCHLD, &act, NULL);         // 注册信号捕捉函数

		ret = sigprocmask(SIG_UNBLOCK, &set, &oldset);   //开始解除SIGCHLD信号阻塞
	    if (ret == -1)
	        sys_err("sigprocmask UNBLOCK error");

        printf("I'm parent, pid = %d\n", getpid());
        while (1);
    } else {
        printf("I'm child pid = %d\n", getpid());
        return i;
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值