您的位置:首页 > 运维架构 > Linux

Linux系统编程-信号

2016-08-06 17:17 375 查看
    0x00
    信号和中断类似,中断是硬件发出,而信号由软件发出。
    信号常用于进程间通信,一个信号常见的处理如下:
    1、设置对应信号的信号处理函数。
    2、当信号来临时,打断正常执行的程序(本质上是在系统调用前检查是否有信号的到来),去执行信号处理函数。
    3、信号处理函数执行完,继续执行原程序。

    0x01
    我们先来看一下linux中一共支持多少种信号。使用命令kill -l,可以列出linux支持的所有信号。
    


   
    每个信号都有对应的默认信号处理函数,使用命令man 7 signal,可以查看到。
    


    如果信号没有定义对应的信号处理函数,并且没有忽略信号,那么则采用默认的信号处理函数。
    这里要注意,信号SIGKILL和SIGSTOP既不能被捕捉,也不能被忽略。

    0x02
    我们看一个自定义信号处理函数的例子,来学习如何自定义信号处理,并且学习如何发送信号。
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>

#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)

void handler(int sig);
int main(int argc, char *argv[])
{
if (signal(SIGINT, handler) == SIG_ERR)
ERR_EXIT("signal error");
for (;;)
pause();
return 0;
}

void handler(int sig)
{
printf("recv a sig=%d\n", sig);
}    这个程序中我们自定义了信号处理函数,对应的信号是SIGINT,那么什么时候会发送这个这个信号呢?答案是当用户按下Ctrl + C时,我们看控制台输出的结果。
    


    对照信号表,SIGINT是2号信号。在程序中pause,可以用来等待下一次信号的来临。
    在这个例子中发送信号是通过键盘按Ctrl + C。
    这里顺便说一下三号信号SIGQUIT,发送该信号,按键盘Ctrl + \。
  
    0x03
    我们接着讲另一种发送信号的方式,通过kill,列出代码:
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>

#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)

void handler(int sig);
int main(int argc, char *argv[])
{
if (signal(SIGUSR1, handler) == SIG_ERR)
ERR_EXIT("signal error");
pid_t pid = fork();
if (pid == -1)
ERR_EXIT("fork error");

if (pid == 0) //子进程
{
kill(getppid(), SIGUSR1);
exit(EXIT_SUCCESS);
}

int n = 5; //父进程
do
{
n = sleep(n);
} while (n > 0);
return 0;
}

void handler(int sig)
{
printf("recv a sig=%d\n", sig);
}    这个程序中使用了SIGUSR1这个用户自定义信号。使用kill向对应的父进程发送信号,父进程sleep一旦接收到信号,会立刻退出,所以就不会睡眠5秒钟,那么如何让父进程睡眠5秒钟呢?我们采用了循环的方式,即使被子进程发来的信号打断,也可以继续睡眠剩余的时间。
    这个例子中发送信号,我们采用kill的方式。

    0x04
    下面再看一个通过alarm发送信号的例子:
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>

#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)

void handler(int sig);
int main(int argc, char *argv[])
{
if (signal(SIGALRM, handler) == SIG_ERR)
ERR_EXIT("signal error");

alarm(1);
for (;;)
pause();
return 0;
}

void handler(int sig)
{
printf("recv a sig=%d\n", sig);
alarm(1);
}    通过alarm函数向自身进程发送SIGALRM的信号,在信号处理函数中再一次发送ALRM信号,起到了一个定时器的作用。

    0x05
    接下来的例子,我们讲解下结束进程的几种方式,结合waitpid函数,进一步理解waitpid函数。
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>

#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)

int main(int argc, char *argv[])
{
pid_t pid;
pid = fork();
if (pid == -1)
ERR_EXIT("fork error");

if (pid == 0) //子进程
{
sleep(3);
printf("this is child\n");
exit(100);
//return 0;
//abort();
//raise(SIGKILL);
}

int ret;
printf("this is parent\n");
int status;
//ret = wait(&status);
//ret = waitpid(-1, &status, 0);
ret = waitpid(pid, &status, 0); //父进程
printf("ret = %d pid = %d\n", ret, pid);
if (WIFEXITED(status))
printf("child exited normal exit status=%d\n", WEXITSTATUS(status));
//else
// printf("child exited abnormal\n");

else if (WIFSIGNALED(status))
printf("child exited abnormal signal number=%d\n", WTERMSIG(status));
else if (WIFSTOPPED(status))
printf("child stoped signal number=%d\n", WSTOPSIG(status));
return 0;
}
    这个例子说明了结束一个进程的几种方式:
    1、exit(100) ---> 控制台输出:child exited normal exit status=100 
    2、return 0 ---> 控制台输出:child exited normal exit status=0
    3、abort(),向当前进程发送SIGABRT,SIGABRT信号的默认处理函数为终止进程 ---> 控制台输出:child exited abnormal signal number=6 (SIGABRT是6号信号)
    4、raise(SIGKILL),向当前进程发送SIGKILL信号,SIGKILL信号的默认处理函数是终止进程 --> 控制台输出:child exited abnormal signal number=9 (SIGKILL是9号信号)

    0x06
    总结:
    信号既可以被捕获(自定义信号处理函数),也可以忽略,也可以走默认信号处理函数。
    发送信号有以下几种方式:
    1、键盘操作,Ctrl + C,Ctrl + \
    2、kill
    3、alarm
    4、abort
    5、raise
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: