Linux signal events Learning note

Linux signal events Learning note

reference document:

sigemptyset sigfillset sigprocmask 博文

1.信号的处理

信号的产生,使用kill -l命令查看有哪些信号,具体使用man 7 signal查看详情页。

信息的发生有两个来源:硬件产生和软件产生。硬件顾名思义就是例如键盘比如平常用的组合键(ctrl+c ctrl+z等);软件则是使用系统函数或者命令发出信号。

系统为我们提供的信号发生函数有很多,但是其中最常用的是kill、raise、alarm和 setitimert(定时器吧)。

提供前三个函数的声明:

#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
int kill(pid_t pid,int sig);
int raise(int sig);
unisigned int alarm(unsigned int seconds);

kill函数中sig 为向进程发送的触发信号(pmst:kill的原因有很多,当然要区分。个人理解。),pid取值有三种情况:

  • 正数。那么sig自然会发送给pid号进程喽
  • 0。那么信号信号sig被发送到所有和pid进程同一个进程组的进程(pmst:一个应用不知一个进程吧)
  • -1。信号发给所有进程表中的进程,除了最大的那个进程号。

TODO:以上理解有偏差,还需更正。

raise系统调用向自己发送一个sig信号,我们可以用上面那个函数来实现这个功能。
alarm函数和时间有点关系,允许在seconds秒后发送一个SIGALRM(14)信号。当然也可以使用setitimert+signal来实现。

测试了下alarm()函数

#include <unistd.h>
#include <stdio.h>

main()
{
unsigned int i;
alarm(1);
for(i=0;1;i++)
printf("I=%d",i);
}

执行以上函数输出不定(pmst:虚拟机CentOS 512M,输出8127)。
本来alarm是在设定时间后自行相应的操作,但是缺省会默认执行结束进程操作。

2.信号的操作

信号操作 有时候我们希望进程正确的执行,而不想进程受到信号的影响,比如我
们希望上面那个程序在1秒钟之后不结束.这个时候我们就要进行信号的操作了.
信号操作最常用的方法是信号屏蔽.信号屏蔽要用到下面的几个函数.

以下是几个信号操作的函数声明:

#include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set,int signo);
int sigdelset(sigset_t *set,int signo);
int sigismember(sigset_t *set,int signo);
int sigprocmask(int how,const sigset_t *set,sigset_t *oset);
  • sigemptyset函数初始化信号集合set,将set设置为空(pmst:我认为就是kill -l输出的那些就是信号集合了,这里选择都清除光)
  • sigfillset也初始化信号集合,只是将信号集合设置为所有信号的集合(pmst:这里则是设置所有信号的集合)
  • sigaddset将信号signo加入到信号集合之中(signo 自然就是信号number)
  • sigdelset将信号从信号集合中删除,同上
  • sigismember查询信号是否在信号集合之中.其实就是sig is member of process的缩写吧
  • sigprocmask是最为关键的一个函数。使用执行sigprocmask函数前,要先设置好信号集合set.这个函数的作用是将指定的信号集合set加入到进程的信号阻塞集合之中去,如果提供了oset那么当前的进程信号阻塞集合将会保存在oset里面.参数how决定函数的操作方式.
    • SIG_BLOCK:增加一个信号集合到当前进程的阻塞集合之中.
    • SIG_UNBLOCK:从当前的阻塞集合之中删除一个信号集合.
    • SIG_SETMASK:将当前的信号集合设置为信号阻塞集合.

注意: 信号的阻塞就是让系统暂时保留信号留待以后发送。(注意:不是不发送,而是延迟发送)一般情况下信号的阻塞只是暂时的,只是为了防止信号打断敏感的操作。

可参考:信号的阻塞

测试用例:

#include <signal.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
int main(int argc,char **argv)
{
double y;
sigset_t intmask;
int i,repeat_factor;
if(argc!=2)
{
fprintf(stderr,"Usage:%s repeat_factor/n/a",argv[0]);
exit(1);
}
if((repeat_factor=atoi(argv[1]))<1)repeat_factor=10;
sigemptyset(&intmask); /* 将信号集合设置为空 */
sigaddset(&intmask,SIGINT); /* 加入中断 Ctrl+C 信号*/
while(1)
{
/*阻塞信号,我们不希望保存原来的集合所以参数为NULL*/
sigprocmask(SIG_BLOCK,&intmask,NULL);
fprintf(stderr,"SIGINT signal blocked/n");
for(i=0;i<repeat_factor;i++)y=sin((double)i);
fprintf(stderr,"Blocked calculation is finished/n");
/* 取消阻塞 */
sigprocmask(SIG_UNBLOCK,&intmask,NULL);
fprintf(stderr,"SIGINT signal unblocked/n");
for(i=0;i<repeat_factor;i++)y=sin((double)i);
fprintf(stderr,"Unblocked calculation is finished/n");
}
exit(0);
}

我们先使用一个自定义的signal集合,然后将SIGINT加入到该信号集合中,即只要ctrl+c就会中断程序。在while(1)里我们使用sigprocmask函数使用SIG_BLOCK指定当SIGINT信号触发时放到信号阻塞集合中,马上执行for(i=0;i<repeat_factor;i++)y=sin((double)i);这条语句,设定repeat_factor 值大一些,所以在这一步中操作时间比较长,此时状态时阻塞的,因此按下ctrl+c就把这个信号放入到阻塞集合中,并非立马触发,只有当unblock后会立马触发;当然倘若直接SIG_UNBLOCK屏蔽信号取消了,这个信号就会发生作用。

此时我们希望对信号做出及时的反应,比如当用户按下Ctrl+C时,我们希望做一些事情,例如告知用户你的这个操作不好吗,请不要重试,而不是什么都告知。此时我们需要用到sigaction函数。

先了解下函数的声明:

#include <signal.h>

/* 函数 */     
int sigaction(int signo,const struct sigaction *act,
struct sigaction *oact);

/* 结构体 */
struct sigaction {
    void (*sa_handler)(int signo);// 函数指针 用于信号处理
    void (*sa_sigaction)(int siginfo_t *info,void *act);//函数指针
    sigset_t sa_mask;   //信号集合
    int sa_flags;       //标志位
    void (*sa_restore)(void);//函数指针 用于重置
}

函数的三个传入参数解释:

  • signo很简单就是我们要处理的信号了,可以是任何的合法的信号.有两个信号不能够使用(SIGKILL和SIGSTOP).
  • act包含我们要对这个信号进行如何处理的信息.具体看下面结构体。
  • oact更简单了就是以前对这个函数的处理信息了,主要用来保存信息的,一般用NULL就OK了

结构体解释:

  • sa_handler是一个函数型指针,这个指针指向一个函数,这个函数有一个参数.这个函数就
    是我们要进行的信号操作的函数.
  • sa_sigaction,sa_restoresa_handler差不多的,只是参数不同罢了.这两个元素我们很少使用,就不管了.
  • sa_flags用来设置信号操作的各个情况.一般设置为0好了.
  • sa_mask 屏蔽信号集合??? 就是加入这个集合的信号都会被屏蔽掉。

在使用的时候我们用sa_handler指向我们的一个信号操作函数,就可以了.sa_handler有两个特殊的值:SIG_DELSIG_IGN.SIG_DEL是使用缺省的信号操作函数,而SIG_IGN是使用忽略该信号的操作函数.

sigaction 测试用例:

#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#define PROMPT "你想终止程序吗?"
char *prompt=PROMPT;
void ctrl_c_op(int signo)
{
write(STDERR_FILENO,prompt,strlen(prompt));
}
int main()
{
struct sigaction act;
act.sa_handler=ctrl_c_op;
sigemptyset(&act.sa_mask);
act.sa_flags=0;
if(sigaction(SIGINT,&act,NULL)<0)
{
fprintf(stderr,"Install Signal Action Error:%s/n/a",strerror(errno));
exit(1);
}
while(1);
}

非常简单,就是当SIGINT触发时调用 sigaction 中的handler方法处理。但是如果我们在处理信号的时候又有信号到来,这就有点棘手了。因此我们要使用sigaction 中的sa_mask屏蔽信号集合了!当我们进入handler处理事务时,不想被其他消息干扰,那么就把那些消息加入到sa_mask屏蔽集合中,处理完后 在unblock下。以上是pmst的个人见解。

其他信号函数

先看下函数声明:

pause 和 sigsuspend
#include <unistd.h>
#include <signal.h>
int pause(void);
int sigsuspend(const sigset_t *sigmask);
  • pause函数很简单,就是挂起进程直到一个信号发生了.
  • sigsuspend也是挂起进程只是在调用的时候用sigmask取代当前的信号阻塞集合.
sigsetjmp 和siglongjmp
#include <sigsetjmp>
int sigsetjmp(sigjmp_buf env,int val);
void siglongjmp(sigjmp_buf env,int val);

这两个信号跳转函数也可以实现程序的跳转让我们可以从函数之中跳转到我们需要的地方.

一个实例

#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <pwd.h>
#include <sys/types.h>
#include <sys/stat.h>
/* Linux 的默任个人的邮箱地址是 /var/spool/mail/ */
#define MAIL_DIR "/var/spool/mail/"
/* 睡眠10秒钟 */
#define SLEEP_TIME 10
#define MAX_FILENAME 255
unsigned char notifyflag=1;
long get_file_size(const char *filename)
{
struct stat buf;
if(stat(filename,&;buf)==-1)
{
if(errno==ENOENT)return 0;
else return -1;
}
return (long)buf.st_size;
}
void send_mail_notify(void)
{
fprintf(stderr,"New mail has arrived/007/n");
}
void turn_on_notify(int signo)
{
notifyflag=1;
}
void turn_off_notify(int signo)
{
notifyflag=0;
}
int check_mail(const char *filename)
{
long old_mail_size,new_mail_size;
sigset_t blockset,emptyset;
sigemptyset(&;blockset);
sigemptyset(&;emptyset);
sigaddset(&;blockset,SIGUSR1);
sigaddset(&;blockset,SIGUSR2);
old_mail_size=get_file_size(filename);
if(old_mail_size<0)return 1;
if(old_mail_size>0) send_mail_notify();
sleep(SLEEP_TIME);
while(1)
{
if(sigprocmask(SIG_BLOCK,&;blockset,NULL)<0) return 1;
while(notifyflag==0)sigsuspend(&;emptyset);
if(sigprocmask(SIG_SETMASK,&;emptyset,NULL)<0) return 1;
new_mail_size=get_file_size(filename);
if(new_mail_size>old_mail_size)send_mail_notify;
old_mail_size=new_mail_size;
sleep(SLEEP_TIME);
}
}
int main(void)
{
char mailfile[MAX_FILENAME];
struct sigaction newact;
struct passwd *pw;
if((pw=getpwuid(getuid()))==NULL)
{
fprintf(stderr,"Get Login Name Error:%s/n/a",strerror(errno));
exit(1);
}
strcpy(mailfile,MAIL_DIR);
strcat(mailfile,pw->pw_name);
newact.sa_handler=turn_on_notify;
newact.sa_flags=0;
sigemptyset(&;newact.sa_mask);
sigaddset(&;newact.sa_mask,SIGUSR1);
sigaddset(&;newact.sa_mask,SIGUSR2);
if(sigaction(SIGUSR1,&;newact,NULL)<0)
fprintf(stderr,"Turn On Error:%s/n/a",strerror(errno));
newact.sa_handler=turn_off_notify;
if(sigaction(SIGUSR1,&;newact,NULL)<0)
fprintf(stderr,"Turn Off Error:%s/n/a",strerror(errno));
check_mail(mailfile);
exit(0);
}

关于 sigempty()

简述

sigempty() 是众多函数家庭众多的一员,可用于手工设置信号集合。Signal Set是一些数据对象,允许让一个线程持续管理一个信号组。例如,一个线程或许创建一个信号集合用于记录那些被block的信号,也可以记录那些挂起的信号。

我们可以通过sigprocmask()来管理这些信号集合,传入SIG_BLCOKSIG_UNBLOCKSIG_SETMASK;或者examine signal sets returned by other functions(such as sigpending())

正如sigempty名字所说,它初始化一个特定的集合为空集合。换句话说,所有支持的信号都被排除在外了。

参数

IN:
*set
(Input) A pointer to a signal set.

Return Value
0 sigemptyset() wassuccessful.

Related Information
The

demo
#include <stdio.h>
#include <unistd.h>
#include <signal.h>

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

struct sigaction sigact;
sigset_t sigset;

sigemptyset( &sigact.sa_mask );
sigact.sa_flags = 0;
sigact.sa_handler = SIG_IGN;
sigaction( SIGUSR2, &sigact, NULL );

/*
 * Unblocking all signals ensures that the signal
 * handling action will be taken when the signal
 * is generated.
 */

sigemptyset( &sigset );
sigprocmask( SIG_SETMASK, &sigset, NULL );

printf( "before kill()\n" );
kill( getpid(), SIGUSR2 );
printf( "after kill()\n" );

return( 0 );
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值