Linux C编程之信号介绍

这几天写了CSAPP的实验,用C语言实现一个简单的SHELL,为了做这个实验,把APUE信号相关的内容看了一遍,这里跟大家分享一下。
Unix信号使用总结:

信号的原理:

信号是一种进程通信的方法,他应用于异步事件的处理。信号的实现是一种软中断。它被发送为一个正在运行的进程,已告知进程某个事件发生了。

1.1 信号的创建:

用信号处理来模拟操作系统的中断功能。要想使用信号处理功能,你要做的就是填写一个信号处理函数即可。

(1)调用signal函数

函数原型如下:void signal(int signo,void * handler); signo是信号类型,后面是信号处理函数。

(2)调用kill函数

可以封装一个发送信号的函数:
int send_signal(pid_t pid, int sig) {
    if (kill(pid, sig) < 0) {
        /* fail silently if pid doesn't exist -- this keeps handlers that
         are called too close together from complaining */
        if (errno != ESRCH)
            unix_error("send_signal: kill failed");
    }
    return 0;
}


通过kill函数,对pid进程发送信号

(3)使用sigaction函数封装signal

sigaction的结构如下:

struct sigaction

{

     void(*sa_handler)(int);

     void(*sa_sigaction)(int,siginfo_t *,void *);

     sigset_tsa_mask;

     intsa_flags;

}



可以通过设置sigaction中的参数值获得对sinal的封装。

1.2信号集操作
(1)信号集的概念
在实际的应用中一个应用程序需要对多个信号进行处理,为了方便,linux系统引进了信号集的概念。信号集用多个信号组成的数据类型sigset_t.可用以下的系统调用设置信号集中所包含的数据。在系统中是这样定义的:


typedef struct { unsignedlong sig[_NSIG_WORDS];} sigset_t;

(2)信号集的操作

信号集的操作

有以下几种:

int sigemptyset(sigset_t *set);

int sigfillset(sigset_t *set);

int sigadd(sigset_t *set,int setnumber);

int sigdelset(sigset_t *set,int setnumber);

int sigismember(sigset_t *set,int setnumber);


使用方法:
通常使用sigemptyset函数初始化信号集,然后调用sigadd添加需要处理的信号,这样才能对信号进行阻塞等操作。

(3)信号的阻塞

有时候不希望在接到信号时就立即停止当前执行,去处理信号,同时也不希望忽略该信号,而是延时一段时间去调用信号处理函数。这种情况是通过阻塞信号实现的。阻塞的

概念和忽略信号是不同的。操作系统在信号被进程解除阻塞之前不会讲信号传递出去,被阻塞的信号也不会影响进程的行为,信号只是暂时被阻止传递。当进程忽略一个信号

时,信号会被传递出去但进程会将信号丢弃。需要注意当信号被阻塞的时候,如果这种信号发生了多次,在阻塞完成之后,只调用一次这种信号,在阻塞过程中,这种信号是

未决的。
信号阻塞处理涉及到以下几个函数:
1.sigprocmask函数
int sigprocmask(ubt how,const sigset_t*set,sigset_t *oldset);

sigprocmask设定对信号屏蔽集内的信号的处理方式(阻塞或不阻塞)。

参数:

how:用于指定信号修改的方式,可能选择有三种

SIG_BLOCK//将set所指向的信号集中包含的信号加到当前的信号掩码中。即信号掩码和set信号集进行或操作。

SIG_UNBLOCK//将set所指向的信号集中包含的信号从当前的信号掩码中删除。即信号掩码和set进行与操作。

SIG_SETMASK //将set的值设定为新的进程信号掩码。即set对信号掩码进行了赋值操作。

set:为指向信号集的指针,在此专指新设的信号集,如果仅想读取现在的屏蔽值,可将其置为NULL。

oldset:也是指向信号集的指针,在此存放原来的信号集。可用来检测信号掩码中存在什么信号。

返回说明:

成功执行时,返回0。失败返回-1,errno被设为EINVAL。

2.sigsuspend函数

int sigsuspend(const sigset_t *sigmask);

此函数用于进程的挂起,sigmask指向一个信号集。当此函数被调用时,sigmask所指向的信号集中的信号将赋值给信号掩码。之后进程挂起。直到进程捕捉到信号,

并调用处理函数返回时,函数sigsuspend返回。信号掩码恢复为信号调用前的值,同时将errno设为EINTR。进程结束信号可将其立即停止。

3.sigpending函数

次函数是判断当前信号是否处于未决状态

1.3 wait()和waitpid()
wait和waitpid是处理僵死进程的函数,一般通过在while循环中使用这两个函数来处理僵死进程。

实例:

前面介绍了这么多关于信号的东西,下面看一个实例:

#include "stdio.h"
#include "stdlib.h"
#include "unistd.h"
#include "signal.h"

char *id = "terminal\n";
int count = 0;

static void do_termal(int sig) {
    ++count;
    //write(1,id,strlen(id));
    printf("aaaaa\n");
}

static void do_stop(int sig) {
    ++count;
    //write(1,id,strlen(id));
    printf("stop\n");
}
int main() {
    struct sigaction sa_oact;
    struct sigaction sa_nact;
    sigset_t pendmask;

    sa_nact.sa_handler = do_termal;
    sa_nact.sa_flags = 0;
    sigemptyset(&sa_nact.sa_mask);
    sigaddset(&sa_nact.sa_mask, SIGTSTP);
    sigprocmask(SIG_BLOCK, &sa_nact.sa_mask, &sa_nact.sa_mask);
    sigaction(SIGINT, &sa_nact, &sa_oact);
    sa_nact.sa_handler = do_stop;
    sa_nact.sa_flags = 0;
    sigaction(SIGTSTP, &sa_nact, &sa_oact);
    puts("sig!");
    do {
        if (count == 2) {
            sigprocmask(SIG_UNBLOCK, &sa_nact.sa_mask, &sa_nact.sa_mask);
            printf("un_block ctrl_z\n");
        }
        sigpending(&pendmask);
        if (sigismember(&pendmask, SIGTSTP)) {//SIGINT是悬而未决的。所谓悬而未决,是指SIGQUIT被阻塞还没有被处理
            printf("SIGTSTP pending\n");
        }
        sleep(2);
    } while (count < 5);
    puts("end");
    return 0;
}




代码中注册了两个信号处理函数,为ctrl_z,和ctrl_c处理,首先初始化sigaction结构中的各个成员,然后调用

sigemptyset(&sa_nact.sa_mask);
sigaddset(&sa_nact.sa_mask, SIGTSTP);

初始化信号集,之后在count<=2的时候对ctrl_z block,当count>2解除block,程序运行中,如果在<2时,多次按ctrl_z,在unblock之后,只会执行一次ctrl_z处理
函数,其余的信号则被抛弃。




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值