说明:
本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。
QQ 群 号:513683159 【相互学习】
内容来源:
《Unix环境高级编程》P257
目录:
一、信号集
(一)概念
信号集:一个能表示多个信号的数据类型:sigset_t
。
( 二)信号集相关函数【man sigemptyset】
函数 | 函数原型与功能描述 | 参数说明 | 头文件 | 返回值 | 注意 |
---|---|---|---|---|---|
sigemptyset() | int sigemptyset(sigset_t *set); 将一个信号集的内容清空 | set:信号集 signum:信号 | signal.h | 成功返回0,错误返回-1,设置errno。 sigismember()若sgum是set的成员返回1;若sgum不是成员返回0;若出错,则返回-1。 | |
sigfillset() | int sigfillset(sigset_t *set); 将一个信号集的置为全集 | ||||
sigaddset() | int sigaddset(sigset_t *set, int signum); 在某集合中添加某个信号 | ||||
sigdelset() | int sigdelset(sigset_t *set, int signum); 在某集合中删除某个信号 | ||||
sigismember() | int sigismember(const sigset_t *set, int signum); 在某集合中是否存在某个信号 |
二、信号屏蔽字
(一)概念
无法决定信号什么时候来,但可通过信号屏蔽字决定什么时候可以被响应。
即:一个进程的信号屏蔽字规定了当前阻塞而不能递送给该进程的信号集。
(二)信号屏蔽字函数
1️⃣sigprocmask()——检测或更改其信号屏蔽字
1.函数功能:检测或更改其信号屏蔽字或在一个步骤中同时执行这两个操作。
项目 | 说明 |
---|---|
函数原型 | int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); |
头文件 | signal.h |
参数说明 | how:做什么 SIG_BLOCK、SIG_UNBLOCK、SIG_SETMASK |
set:信号集 | |
oldset:之前信号机状态 | |
返回值 | 成功返回0 失败返回-1 |
注意 | ①若oldset非空指针,则进程当前信号屏蔽字通过oldset返回 ②若set是非空指针,则参数how指示如何修改信号屏蔽字。 ③若set是空指针,则不改变该进程的信号屏蔽字,how值无意义。 |
2.how可选用的值:
how | 说明 |
---|---|
SIG_BLOCK | 该进程新的信号屏蔽字是其当前信号屏蔽字和set指向信号集的并集。set包含了我们希望阻塞的附加信号 |
SIG_UNBLOCK | 该进程新的信号屏蔽字是其当前信号屏蔽字和set所指向信号集补集的交集。set包含了我们希望解除阻塞的信号 |
SIG_SETMASK | 该进程新的信号屏蔽字将被set指向的信号集的值代替 |
2️⃣sigpending()——取出pending集状态
1.函数功能:返回信号集,其中各个信号对调用进程是阻塞的而不是递送的,因而也一定是当前未决的。
项目 | 说明 |
---|---|
函数原型 | int sigpending(sigset_t *set); |
头文件 | signal.h |
参数说明 | set:信号集 |
返回值 | 成功返回0 失败返回-1 |
注意 |
三、其他函数
1️⃣sigaction() —— 检查或更改信号动作
1.函数功能:检查或修改与指定信号相关联的处理动作(或同时执行这两种操作)
项目 | 说明 |
---|---|
函数原型 | int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); |
头文件 | signal.h |
参数说明 | signum:信号编号 |
act:新行为 | |
oldact:旧行为 | |
返回值 | 成功返回0 失败返回-1 |
注意 | ①若act非空,则要修改其动作 ②若oldact非空则系统经由oldact返回信号的上一个动作 |
2.sigaction结构体:
struct sigaction
{
void (*sa_handler)(int); //信号捕捉函数的地址:SIG_IGN/SIG_DFL
void (*sa_sigaction)(int, siginfo_t *, void *); //备用捕捉函数的地址
sigset_t sa_mask; //信号集,调用信号捕捉函数前需将该信号集加入信号屏蔽字中。
int sa_flags; //信号选项
void (*sa_restorer)(void);
};
3.sa_flags选项:
sa_flags选项 | 说明 |
---|---|
SA_INTERRUPT | 由此信号中断的系统调用不会自动重启动 |
SA_NOCLDSTOP | 若signo是SIGCHLD,当子进程停止时(作业控制)、不产生此信号。当子进程终止时,仍旧产生此信号(但请参阅下面说明的SA_NOCLDWA工T选项)。若已设置此标志,则当停止的进程继续运行时,作为XSI扩展,不发送SIGCHLD信号 |
SA_NOCLDWAIT | 若signo是SIGCHLD,则当调用进程的子进程终止时,不创建僵死进程。若调用进程在后面调用wait,则调用进程阻塞。直到其所有子进程都终止,此时返回-1,并将errno设置为ECHILD |
SA_NODEFER | 当捕捉到此信号时,在执行其信号捕捉函数时,系统不自动阻塞此信号(除非sa_mask包括了此信号)。注意,此种类型的操作对应于早期的不可靠信号 |
SA_ONSTACK | 若用sigaltstack(2)声明了一替换栈,则将此信号递送给替换栈上的进程 |
SA__RESETHAND | 在此信号捕捉函数的人口处,将此信号的处理方式复位为SIG_DFL,并清除SA_SIGINFO标志。注意,此种类型的信号对应于早期的不可靠信号。但是。不能自动复位SIGILE,和SIGTRAP这两个信号的配置。设置此标志使sigaction的行为如同SA_NODEFER标志也设置了一样 |
SA_RESTART | 由此信号中断的系统调用会自动重启动 |
SA_SIGINFO | 此选项对信号处理程庠提供了附加信息:一个指向siginfo结构的指针以及一个指向进程上下文标识符的指针 |
2️⃣sigsetjmp()、siglongjmp()—— 信号处理程序中进行非局部转移
1.函数功能:
执行非本地转到,并提供可预测的处理过程信号罩。(不能从信号处理函数中往外跳)
若savesigs
非0,则sigsetjmp()
在env
中保存进程的当前信号屏蔽字。
调用siglongjmp()
时,若savesigs
非0的sigsetjmp()
已保存env
,则siglongjmp()
从其中恢复保存的信号屏蔽字。
函数 | 函数原型与功能描述 | 参数说明 | 头文件 | 返回值 | 注意 |
---|---|---|---|---|---|
sigsetjmp() | int sigsetjmp(sigjmp_buf env, int savesigs); | env: savesigs: | setjmp.h | 成功返回0,错误返回-1,设置errno。 | |
siglongjmp() | void siglongjmp(sigjmp_buf env, int val); |
3️⃣sigsuspend()——等待信号
1.函数功能:在一个原子操作中先恢复信号屏蔽字,然后使进程休眠。
项目 | 说明 |
---|---|
函数原型 | int sigsuspend(const sigset_t *mask); |
头文件 | signal.h |
参数说明 | mask: |
返回值 | 成功返回0 失败返回-1 |
注意 | 功能与pause()类似, |
示例:
示例一:
程序效果描述:一直打印5个*
换行,若Ctrl+C
进行中断,则无法中断,也不会阻塞,而是在下一行开始时进行响应。(多个信号只会响应一次),此时若想退出可使用Ctrl+\
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
static void int_handler(int s)
{
write(1,"!",1);
}
int main(int argc,char *argv[])
{
int i;
sigset_t set,oset,saveset; //声明信号集合
signal(SIGINT,int_handler); //若程序结束前,收到SIGINT信号时执行int_handler该行为
sigemptyset(&set); //将信号集清空
sigaddset(&set,SIGINT); //将SIGINT加入信号集中
sigprocmask(SIG_UNBLOCK,&set,&saveset); //用于恢复该模块使用之前的信号集
while(1)
{
sigprocmask(SIG_BLOCK,&set,&oset); //将对该set信号集进行阻塞,并将之前信号读入oset中
for(i = 0 ; i < 5; i++)
{
write(1,"*",1);
sleep(1);
}
write(1,"\n",1);
sigprocmask(SIG_UNBLOCK,&set,NULL); //恢复信号集不阻塞
//sigprocmask(SIG_SETMASK,&oset,NULL); 恢复信号集为原来的样子
}
sigprocmask(SIG_SETMASK,&saveset,NULL); //用于恢复该模块使用之前的信号集
return 0;
}
示例二:(sigsuspend()示例)
程序效果描述:打印一行5个*
换行后阻塞,若Ctrl+C
发送信号,则在下一行开始时进行响应。(多个信号只会响应一次),此时若想退出可使用Ctrl+\
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
static void int_handler(int s)
{
write(1,"!",1);
}
int main(int argc,char *argv[])
{
int i;
sigset_t set,oset,saveset; //声明信号集合
signal(SIGINT,int_handler); //若程序结束前,收到SIGINT信号时执行int_handler该行为
sigemptyset(&set); //将信号集清空
sigaddset(&set,SIGINT); //将SIGINT加入信号集中
sigprocmask(SIG_UNBLOCK,&set,&saveset); //解除set的阻塞,saveset恢复该模块使用之前的信号集
sigprocmask(SIG_BLOCK,&set,&oset); //阻塞set ,
while(1)
{
for(i = 0 ; i < 5; i++)
{
write(1,"*",1);
sleep(1);
}
write(1,"\n",1);
sigsuspend(&oset); //相当于下面三句的原子操作
/*
sigset_t tmpset;
sigprocmask(SIG_SETMASK,&oset,&tmpset);
pause();
sigprocmask(SIG_SETMASK,&tmpset,NULL);
*/
}
sigprocmask(SIG_SETMASK,&saveset,NULL); //用于恢复该模块使用之前的信号集
return 0;
}
示例三:(sigaction()示例)
原:守护进程程序
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <syslog.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#define FNAME "/tmp/out"
static int daemonize(void)
{
pid_t pid;
int fd;
pid = fork();
if(pid < 0)
return -1;
if(pid > 0) //parent
exit(0);
fd = open("/dev/null",O_RDWR);
if(fd < 0)
return -1;
dup2(fd,0);
dup2(fd,1);
dup2(fd,2);
if(fd > 2)
close(fd);
setsid();
chdir("/");
//umask(0);
return 0;
}
int main()
{
int i;
FILE *fp;
openlog("mydaemon",LOG_PID,LOG_DAEMON);
if(daemonize())
{
syslog(LOG_ERR,"daemonize() failed!");
exit(1);
}
else
{
syslog(LOG_INFO,"daemonize() successded!!");
}
fp = fopen(FNAME,"w");
if(fp == NULL)
{
syslog(LOG_ERR,"fopen():%s",strerror(errno));
exit(1);
}
syslog(LOG_INFO,"%s was opened.",FNAME);
for(i = 0;;i++)
{
fprintf(fp,"%d\n",i);
fflush(fp);
syslog(LOG_DEBUG,"%d is printef.",i);
sleep(1);
}
fclose(fp);
closelog();
exit(0);
}
修改后守护进程程序
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <syslog.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
static FILE *fp;
#define FNAME "/tmp/out"
static int daemonize(void)
{
pid_t pid;
int fd;
pid = fork();
if(pid < 0)
return -1;
if(pid > 0) //parent
exit(0);
fd = open("/dev/null",O_RDWR);
if(fd < 0)
return -1;
dup2(fd,0);
dup2(fd,1);
dup2(fd,2);
if(fd > 2)
close(fd);
setsid();
chdir("/");
//umask(0);
return 0;
}
static void daemon_exit(int s)
{
fclose(fp);
closelog();
exit(0);
}
int main()
{
int i;
struct sigaction sa;
sa.sa_handler = daemon_exit;
sigemptyset(&sa.sa_mask);
sigaddset(&sa.sa_mask,SIGQUIT);
sigaddset(&sa.sa_mask,SIGTERM);
sigaddset(&sa.sa_mask,SIGINT);
sa.sa_flags = 0;
sigaction(SIGINT,&sa,NULL);
sigaction(SIGQUIT,&sa,NULL);
sigaction(SIGTERM,&sa,NULL);
//signal(SIGINT,daemon_exit);
//signal(SIGQUIT,daemon_exit);
//signal(SIGTERM,daemon_exit);
openlog("mydaemon",LOG_PID,LOG_DAEMON);
if(daemonize())
{
syslog(LOG_ERR,"daemonize() failed!");
exit(1);
}
else
{
syslog(LOG_INFO,"daemonize() successded!!");
}
fp = fopen(FNAME,"w");
if(fp == NULL)
{
syslog(LOG_ERR,"fopen():%s",strerror(errno));
exit(1);
}
syslog(LOG_INFO,"%s was opened.",FNAME);
for(i = 0;;i++)
{
fprintf(fp,"%d\n",i);
fflush(fp);
syslog(LOG_DEBUG,"%d is printef.",i);
sleep(1);
}
exit(0);
}