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;
}