Day 53 Linux setitimer函数 信号集操作函数 信号捕捉 SIGCHLD信号

1. setitimer函数

设置定时器(闹钟),可以代替alarm函数,精度为微秒us,可以实现周期定时

int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
返回值:
    成功:0
    失败:-1,设置error

参数:
(1).which 指定定时方式
    自然定时:ITIMER_REAL                            计算自然时间
    虚拟空间计时器(用户空间):ITIMER_VIRTUAL        只计算进程占CPU的时间
    运行时计时(用户+内核):ITIMER_PROF              计算占用CPU以及系统调用的时间
(2).new_value:定时秒数
struct itimerval
{

    struct timeval
    {
        time_t tv_sec;        //秒
        suseconds_t tv_usec;  //微秒   
    }it_interval;
    
    struct timeval
    {
        time_t tv_sec;
        suseconds_t tv_usec;
    }it_value;
}
初始化eg:
    new_t.it_interval.tv_sec=1;
    new_t.it_interval.tv_user=1;
    new_t.it_value.tv_sec=0;
    new_t.it_value.tv_usec=0;
(3).old_value:传出参数,上次定时剩余时间
    old_value参数,它是用来存储上一次setitimer调用时设置的new_value值
    通常来说我们用不上,直接设置为NULL

提示:

it interval:用来设定两次定时任务之间的间隔时间

it value:用于设定延时时间

setitimer的工作机制是:先对it_value倒计时,当it_value倒计时为0的时候触发信号,然后触发it_interval倒计时,倒计时为0时,触发信号,处理信号;同时it_interval再次进行倒计时,一直这样循环下去

两个参数都设置为0,则为清零操作 

练习:用setitimer函数实现alarm函数,重复计算机1秒数数程序

#include<stdio.h>
#include<signal.h>
#include<sys/time.h>
#include<stdlib.h>
#include<unistd.h>

void myfunc(int signo)
{
	printf("hello world\n");
	exit(1);
}

int main()
{
	struct itimerval newit;
	signal(SIGALRM,myfunc);//注册SIGALRM信号的捕捉处理函数
	newit.it_value.tv_sec = 5;
	newit.it_value.tv_usec = 0;
	newit.it_interval.tv_sec = 3;
	newit.it_interval.tv_usec = 0;

	if(setitimer(ITIMER_REAL, &newit, NULL) == -1)
	{
		perror("setitimerError");
		return -1;
	}
	int i = 0;
	while(1)
	{
		i++;
		printf("i = %d\n", i);
		sleep(1);
	}
	return 0;
}

延时5s启动输出helloworld,间隔2s重复输出

#include<stdio.h>
#include<signal.h>
#include<sys/time.h>

void myfunc(int signo)
{
	printf("hello world\n");
}
int main()
{
	struct itimerval newit = {{2, 0}, {5, 0}};
	signal(SIGALRM, myfunc);
	if(setitimer(ITIMER_REAL, &newit, NULL) == -1)
	{
		perror("setitimerError");
	}
	while(1);
	return 0;
}

 

5s后开始输出第一个helloworld 之后每两秒输出一个helloworld 

2. 信号集操作函数

2.1 信号集设定

2.1.1 信号处理控制原理框图

        内核通过读取未决信号来判断信号是否应被处理,信号屏蔽字mask可以影响未决信号机,而我们可以在应用程序中自定义set来改变mask值,已达到屏蔽指定信号的目的。

2.1.2 信号集操作函数

//清空set
int sigemptyset(sigset_t *set);
//置1 set
int sigfillset(sigset_t *set);
//把某个信号加入到 set
int sigaddset(sigset_t *set, int signum);
//把某个信号从set中移除
int sigdelset(sigset_t *set, int signum);
//查看某个信号,是否在set中
int sigismember(const sigset_t *set, int signum);

2.2 sigprocmask函数

        用来设置屏蔽信号,解除屏蔽,它的本质是读取或修改进程信号屏蔽字(PCB中)

        屏蔽信号,只是将信号处理延后执行(延后至解除屏蔽),而忽略表示将信号丢失

int sigprocmask(int how, const sigset_t *set, iigset_t *oldset);
返回值:
    成功0
    失败-1,设置error

参数:
(1).how: SIG_BLOCK(设置阻塞),SIG_UNBLOCK(取消阻塞), SIG_SETMASK(自定义set)
(2).set: 自定义set
(3).oldset:旧有的mask

2.3  sigpending函数

读取当前进程的未决信号集

int sigpending(sigset_t *set);
返回值:
    成功0
    失败-1.设置error
参数:set传出未决信号集

例:使用自定义set,屏蔽ctrl+c 2号信号,读取当前进程未决信号集打印纸屏幕

 

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

void printSet(sigset_t *set)
{
	for(int i = 0; i < 32; i++)
	{
		if(sigismember(set, i))
		{
			putchar('1');
		}
		else
		{
			putchar('0');
		}
	}
	printf("\n");
}

int main()
{
	sigset_t set, oldset, pendset;
	sigemptyset(&set);//清空set
	sigaddset(&set, SIGINT);//屏蔽ctrl+c 2号信号
	int ret = sigprocmask(SIG_BLOCK, &set, &oldset);
	if(ret == -1)
	{
		perror("Error");
	}
	while(1)
	{
		ret = sigpending(&pendset);//读取信号集
		if(ret == -1)
		{
			perror("Error");
		}
		printSet(&pendset);
		sleep(1);
	}
	return 0;
}

3. 信号捕捉

3.1 signal函数

注册一个信号捕捉函数 

typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

该函数由ANSI定义,由于历史原因在不同班的Unix和不同版本的Linux中可能有不同的行为,因此应该尽量避免使用它,取而代之使用的是sigaction函数

例:捕捉信号ctrl+c

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

void sig_cath(int signum)
{
	printf("catch you! %d\n", signum);
}

int main()
{
	signal(SIGINT, sig_cath);
	while(1);
	return 0;
}

 

3.2 sigaction函数

修改信号处理动作(通常在Linux用来注册一个信号捕捉函数)

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

 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_restroer:该元素是过时的,不应该使用,POSIX.1 标准将不指定该元素(弃用)

sa_sigaction:当sa_flags被指定SA_SIGINFO标志的时候,使用该信号处理程序(很少使用)

重点掌握:

sa_handler:指定信号捕捉后的处理函数名(注册函数),也可以赋值为SIG_IGN表示忽略或SIG_DFL表示执行默认动作

sa_mask:调用信号处理函数,所要屏蔽的信号集合(信号屏蔽子)。注意,仅在处理函数被调用期间屏蔽生效,是临时设置的

sa_flags:通常设置为0,表示使用默认属性,捕捉函数执行期间,屏蔽同一信号再次过来调用捕捉

例子:捕捉信号ctrl+c

 

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

void sig_cath(int signum)
{
	printf("catch you ! %d\n", signum);
}

int main()
{
	struct sigaction act, oldact;
	act.sa_handler = sig_cath;//设置回调信号捕捉函数
	sigemptyset(&(act.sa_mask));//情况sa_mask屏蔽字,只在捕捉函数执行期间有效
	act.sa_flags = 0;//使用默认值
	int ret = sigaction(SIGINT, &act, &oldact);
	if(ret == -1)
	{
		perror("Error");
	}
	while(1);
	return 0;
}

 

信号捕捉特性:

1.进程正常运行时, 默认PCB中有一个信号屏蔽字,假定为※,它决定了进程自动屏蔽哪些信号。当注册了某个信号捕捉函数,捕捉到该信号以后,要调用该函数。而该函数有可能执行很长时间,在这期间所屏蔽的信号不由※来指定。而是用sa_mask来指定。调用完信号处理函数,再恢复为※

2.XXX信号捕捉函数执行期间,XXX信号自动被屏蔽(sa_flags=0)。

3.阻塞的常规信号不支持排队, 产生多次只记录一次。(后32个实时信号支持排队)。

练习:

1. 为某个信号设置捕捉函数,捕捉函数输出信息

#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<sys/time.h>

void catch_sig(int num)
{
	printf("抓住%d信号了!\n", num);
}

int main()
{
	//注册一下捕捉函数
	struct sigaction act;
	act.sa_flags = 0;//默认值
	act.sa_handler = catch_sig;
	sigemptyset(&act.sa_mask);

	sigaction(SIGALRM, &act, NULL);

	//setitimer
	struct itimerval mytime = {{3, 0}, {5, 0}};
	setitimer(ITIMER_REAL, &mytime, NULL);
	while(1)
	{
		printf("欸嘿~\n");
		sleep(1);
	}
	return 0;
}

 3.3 内核实现信号捕捉过程

4. SIGCHLD信号

4.1 SIGCHLD的产生条件

子进程终止时

紫禁城接收到SIGSTOP信号停止时

子进程处在停止态,接收到SIGCONT后唤醒

4.2 借助SIGCHLD信号回收子进程

 子进程结束运行,其父进程会收到SIGCHLD信号。该信号的默认处理动作是忽略。可以捕捉该信号,在捕捉函数中完成子进程状态的回收。

例:创建10个子进程,利用SIGCHLD信号进行回收

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

void catch_child(int signum)
{
	pid_t wpid;
	while((wpid = wait(NULL)) != -1)
	{
		printf("-----------catch child id %d\n", wpid);
	}
}

int main()
{
	pid_t pid;
	int i;
	for(i = 0; i < 15; i++)
	{
		if((pid == fork()) == 0)
			break;
	}

	if(i == 15)
	{
		struct sigaction act;
		act.sa_handler = catch_child;
		sigemptyset(&act.sa_mask);
		act.sa_flags = 0;

		sigaction(SIGCHLD, &act, NULL);

		printf("I am parent process! pid = %d\n", getpid());
		while(1)
		{
			;
		}
	}
	else
	{
		printf("I am %dth child process! pid = %d\n", i+1, getpid());
		sleep(1);
	}
	return 0;
}

 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值