栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

Linux 信号简述

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Linux 信号简述

信号是什么

信号是系统响应某些条件所产生的一个事件,接收到该信号的进程会进行相应的操作

信号也可以由一个进程发送给另一个进程

信号的种类 常用信号
SIGALRM由 alarm 函数设置的定时器产生
SIGHUP有一个处于非连接状态的终端发送给控制进程,或者由控制进程在自身结束时发送给每个前台进程
SIGINT一般由终端敲入 Ctrl + C 组合键或预先设置好的中断字符产生
SIGKILL该信号不能被捕获或忽略,一般在 shell 中用来终止异常进程
SIGPIPE如果向管道写数据时没有与之对应的读进程,就会产生该信号
SIGTERM作为一个请求被发送,要求进程结束运行。 UNIX 在关机时用该信号要求系统服务停止运行。它是 kill 命令默认发送的信号
SIGUSR1, SIGUSR2进程之间可以用这个信号进行通信,例如报告状态信息等
SIGABORT进程异常终止
SIGQUIT终端退出
SIGSEGV无效内存段访问
SIGILL非法指令

如果进程接收到上面的信号,而事先有没有安排捕获,那么进程会终止

信号的处理 signal 函数
 #include 

typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);

 示例:

#include 
#include 
#include 

void fun(int sig)
{
    printf("nGot signal %dn", sig);

    (void) signal(SIGINT, SIG_DFL); // 中断处理函数中再次修改 SIGINT 为默认处理行为(结束进程)
}

int main()
{
    (void) signal(SIGINT, fun);    // 修改默认 SIGINT 处理行为,调用中断处理函数
    while(1)
    {
        printf("Hellon");
        sleep(1);
    }

    return 0;
}

 

增强版 sigaction 函数
#include 

int sigaction(int signum, const struct sigaction *act, 
            struct sigaction *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);
};

对于信号的关联,用到了两个同样的结构体变量:act,oldact 分别用于设置指定信号的动作,以及保存原先对该信号的动作(如果 oldact 不为空)

结构体提供了更细致的控制,常用的栏位:

void (*sa_handler)(int):信号处理函数

sigset_t  sa_mask:信号集,在调用信号处理函数之前,该信号集将被加入到进程的信号屏蔽字中,相当于这组信号不被当前进程所接收

int sa_flags:信号处理方式

SA_NOCLDSTOP子进程停止时不产生 SIGCHILD 信号
SA_RESETHAND将对此信号的处理方式在信号处理函数入口处重置为 SIG_DFL
SA_RESTART重启可中断的函数而不是给出 EINTR 错误
SA_NODEFER捕获到信号时不将它添加到信号屏蔽字中

示例: 

#include 
#include 
#include 

void fun(int sig)
{
    printf("nGot signal %dn", sig);
}

int main()
{
    struct sigaction act;
    act.sa_handler = fun;

    sigemptyset(&act.sa_mask);    // 不屏蔽任何信号

    act.sa_flags = SA_RESETHAND;    // 重置为默认行为, 将在信号处理函数入口处进行

    sigaction(SIGINT, &act, 0);

    while(1)
    {
        printf("Hellon");
        sleep(1);
    }

    return 0;
}

 默认情况下,sigaction 是不被重置的,如果要想重置,则 sa_flags = SA_RESETHAND

信号函数集

sigaction 的 sa_mask 信号集使用下面一组函数进行设定

  • int sigemptyset(sigset_t *set);
    将信号集初始化为空
  • int sigfillset(sigset_t *set);
    将信号集初始化为包含所有已定义的信号
  • int sigaddset(sigset_t *set, int signum);
    将信号 signum 添加到信号集 set 中
  • int sigdelset(sigset_t *set, int signum);
    将信号 signum 从信号集 set 中删除
  • int sigismember(const sigset_t *set, int signum);
    判断给定的信号 signum 是否是信号集 set 中的一个成员
  • int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
    set : 新的屏蔽字,非空 how 才有意义
    how:
    SIG_BLOCK把参数 set 中的信号添加到信号屏蔽字中
    SIG_UNBLOCK从信号屏蔽字中删除参数 set 中的信号
    SIG_SETMASK把信号屏蔽字设置为 set 中的信号

    oldset:原来的屏蔽字,如果非空保存当前信号屏蔽字
    注意:调用这个函数才能改变进程的屏蔽字,其余函数只是改变变量的值而已
     
  • int sigpending(sigset_t *set);
    将阻塞的信号中停留在待处理状态的一组信号写到 set 指向的信号集中
     
  • int sigsuspend(const sigset_t *mask);
    将进程的屏蔽字替换为由参数 mask 给出的信号集,然后挂起进程的执行
    注意:先替换再挂起,程序将在信号处理函数执行完成后再继续执行,如果收到信号终止了程序,那么该函数不会返回

    如果一个信号被进程阻塞,它就不会传递给进程,但是会停留在待处理的状态。
    当进程解除对待处理信号的阻塞时,待处理信号就会立刻被处理

示例:

#include 
#include 
#include 

void handler(int sig)
{
    printf("Handle the signal %dn", sig);
}

int main(int argc, char **argv)
{
    sigset_t sigset;    // 用于记录屏蔽字
    sigset_t ign;       // 用于记录被阻塞(屏蔽)的信号集
    struct sigaction act;

    // 清空信号集
    sigemptyset(&sigset);
    sigemptyset(&ign);

    // 向信号集中添加 SIGINT
    sigaddset(&sigset, SIGINT);

    // 设置处理函数 和 信号集
    act.sa_handler = handler;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    sigaction(SIGINT, &act, 0);

    printf("Wait the signal SIGNAL...n");
    pause();

    // 设置进程屏蔽字, 在本例中为屏蔽 SIGINT
    sigprocmask(SIG_SETMASK, &sigset, 0);
    printf("Please press Ctrl + C in 10 seconds...n");
    sleep(10);

    // 测试 SIGINT 是否被屏蔽
    sigpending(&ign);    // 将阻塞的信号中停留在待处理状态的一组信号写到 set 指向的信号集中
    if (sigismember(&ign, SIGINT))
    {
        printf("The SIGINT signal has ignoredn");
    }

    // 从信号集中删除信号 SIGINT
    sigdelset(&sigset, SIGINT);
    printf("Wait the signal SIGINT...n");

    // 将进程的屏蔽字重新设置, 即取消对 SIGINT 的屏蔽
    // 并挂起进程
    sigsuspend(&sigset);

    printf("The app will exit in 5 secondes!n");
    sleep(5);

    return 0;
}

 

 

问题

Q:在使用 signal 或者 sigaction 来指定信号处理函数时,如果在建立这个关联前就接收到了要处理的信号,进程会有怎样的反应?

A:不会调用信号处理函数

sam_mask 制定了一个信号集,在调用信号处理函数前,该信号集就被加入到进程的信号屏蔽字中,设置信号屏蔽字可以防止信号在它的信号处理函数还未运行结束时就被接收到的情况

信号的发送  kill 函数
#include 
#include 

int kill(pid_t pid, int sig);

失败原因:

  • EINVAL An invalid signal was specified.        无效信号
  • EPERM  The process does not have permission to send the signal to any of the target processes.        发送权限不够
  • ESRCH  The  pid  or  process  group  does  not exist.  Note that an existing process might be a zombie, a  process which already committed termination, but has not yet been wait(2)ed for.        目标进程不存在
alarm 函数

常用的定时器,时间到了发送 SIGARM 信号

#include 

unsigned int alarm(unsigned int seconds);

示例:

#include 
#include 
#include 
#include 
#include 

static int isalarm = 0;

void fun(int sig)
{
    isalarm = 1;
}

int main()
{
    pid_t pid;
    pid = fork();
    switch(pid)
    {
    case -1:
        perror("fork failedn");
        exit(1);
    case 0:
        // 子进程
        sleep(5);

        // 向父进程发送信号
        kill(getppid(), SIGALRM);
        exit(0);
    default:;
    }

     // 设置信号处理函数
    signal(SIGALRM, fun);
    while(!isalarm)
    {
        printf("Hellon");
        sleep(1);
    }

    if(isalarm)
        printf("nGot a signal %dn", SIGALRM);

    exit(0);
}

子进程休息 5s 后向父进程发送 alarm 信号,期间父进程完成了信号处理函数的关联,并持续打印,直到收到信号进入处理函数

为了避免父进程的持续等待信号,可以使用 pause 函数替代while 打印部分

pause 函数用于挂起进程,直到接收到信号

 

信号处理函数的可重入性

信号处理函数可以在其执行期间被中断并被再次调用

这就要求我们的信号处理函数是可重入的,那么内部调用的函数也必须是可重入的

特别容易出错的是,printf 来进行打印,print 函数是不可重入的

可重入函数表:

accessfpathconfrenamesysconf
aio_returnfstatrmdirtcdrain
aio_suspendfsyncsem_posttcflow
alarmgetegidsetgidtcflush
cfgetispeedgeteuidsetpgidtcgetattr
cfgetospeedgetgidsetsidtcgetpgrp
cfsetispeedgetgroupssetuidtcsendbreak
cfsetospeedgetpgrpsigactiontcsetattr
chidirgetpidsigaddsettcsetpgrp
chmodgetppidsigdelsettime
chowngetuidsigemptysettimer_getoverrun
clock_gettimekillsigfillsettimer_gettime
closelinksigismembertimer_settime
createlseeksignaltimes
dupmkdirsigpauseumask
dup2mkfifosigpendinguname
execleopensigprocmaskunlink
execvepathconfsigqueueutime
_exitpausesigsetwait
fcntlpipesigsuspendwaitpid
fdatasyncraisesleepwrite
forkreadstat
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/827928.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号