Linux进程间通信:信号讲解

信号发送
        信号是 Linux 系统响应某些条件而产生的一个事件, 接收到该信号的进程会执行相应的操作。
信号的产生有三种方式:
1)由硬件产生, 如从键盘输入 Ctrl+C 可以终止当前进程
2)由其他进程发送, 如可在 shell 进程下, 使用命令 kill -信号标号 PID, 向指定进程发送信号。
3)异常, 进程异常时会发送信号
        本篇只关注在应用层对信号的处理。 在 Ubuntu 终端输入 kill -l, 查看所有的信号。
1) SIGHUP  2) SIGINT  3) SIGQUIT  4) SIGILL  5) SIGTRAP
6) SIGABRT  7) SIGBUS  8) SIGFPE  9) SIGKILL  10) SIGUSR1
11) SIGSEGV  12) SIGUSR2  13) SIGPIPE  14) SIGALRM  15) SIGTERM
16) SIGSTKFLT  17) SIGCHLD  18) SIGCONT  19) SIGSTOP  20) SIGTSTP
21) SIGTTIN  22) SIGTTOU  23) SIGURG  24) SIGXCPU  25) SIGXFSZ
26) SIGVTALRM  27) SIGPROF  28) SIGWINCH  29) SIGIO  30) SIGPWR
31) SIGSYS  34) SIGRTMIN  35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3

信号说明默认操作
SIGABRT由 abort()发送终止且进行内存转储
SIGALRM由 alarm()发送终止
SIGBUS硬件或对齐错误终止且进行内存转储
SIGCHLD子进程终止忽略
SIGCONT进程停止后继续执行忽略
SIGFPE算术异常终止且进行内存转储
SIGHUP进程的控制终端关闭(最常见的是
用户登出)
终止
SIGILL进程试图执行非法指令终止且进行内存转储
SIGINT用户产生中断符(Ctrl-C)终止
SIGIO异步 IO 事件(Ctrl-C)终止(a)
SIGKILL不能被捕获的进程终止信号终止
SIGPIPE向无读取进程的管道写入终止
SIGPROF向无读取进程的管道写入终止
SIGPWR断电终止
SIGQUIT用户产生退出符(Ctrl-\)终止且进行内存转储
SIGSEGV无效内存访问终止且进行内存转储
SIGSTKFLT协处理器栈错误终止(b)
SIGSTOP挂起进程停止
SIGSYS进程试图执行无效系统调用终止且进行内存转储
SIGTERM可以捕获的进程终止信号终止
SIGTRAP进入断点终止且进行内存转储
SIGSTP用户生成挂起操作符(Ctrl-Z)停止
SIGTTIN后台进程从控制终端读停止
SIGTTOU后台进程向控制终端写停止
SIGURG紧急 I/O 未处理忽略
SIGUSR1进程自定义的信号终止
SIGUSR2进程自定义的信号终止
SIGVTALRM用 ITIMER_VIRTUAL 为参数调
用 setitimer()时产生
终止
SIGWINCH控制终端窗口大小改变忽略
SIGXCPU进程资源超过限制终止且进行内存转储
SIGXFSZ文件资源超过限制终止且进行内存转储

下面是几个常用的函数:

函数int kill(pid_t pid, int sig)
头文件#include <sys/types.h>
#include <signal.h>
参数 pid大于 0, 时为向 PID 为 pid 的进程发送信号
等于 0, 向同一个进程组的进程发送信号;
等于-1, 除发送进程自身外, 向所有进程 ID 大于 1 的进程发送信号。
小于-1, 向组 ID 等于该 pid 绝对值的进程组内所有进程发送信号。
参数 sig设置发送的信号; 等于 0 时为空信号, 无信号发送。 常用来进行错误检
查。
返回值执行成功时, 返回值为 0; 错误时, 返回-1, 并设置相应的错误代码 errno
功能用于向任何进程组或进程发送信号。
函数int raise(int sig)
头文件#include <signal.h>
参数 sig信号
功能相当于 kill(getpid(),sig), 向进程自身发送信号
函数unsigned int alarm(unsigned int seconds)
头文件#include <unistd.h>
参数设定的时间
功能设定的时间超过后产生 SIGALARM 信号, 默认动作是终止进程。
注意每个进程只能有一个 alarm()函数, 时间到后要想再次使用要重新注册。

使用规则:
实验 1 代码: 在程序中实现: 自己给自己发送信号。
raise.c:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <stdlib.h>
int main(void)
{
    printf("raise before\n");
    raise(9);
    printf("raise after\n");
    return 0;
}

编译运行, 如下图所示:

实验 2 代码 kill.c 发送信号。
kill.c

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{
    pid_t pid;
    int sig;
    if(argc < 3){
        printf("Usage:%s <pid_t> <signal>\n",argv[0]);
        return -1;
    }
     sig = atoi(argv[2]);
     pid = atoi(argv[1]);
     kill(pid,sig);
     return 0;
}

 test.c

#include <stdio.h>
#include <unistd.h>
void main(void)
{
    while(1){
       sleep(1);
       printf("hello world\n");
    }
}

编译运行 test, 如下图所示, 进程会循环打印 hello world。

重新打开另一个窗口, 编译 kill.c, 然后查看 test 进程的 pid 号, 运行测试如下图所示;

与此同时, 显示 test 的窗口显示, test 进程被杀死, 如下图所示:

实验 3 代码 alarml.c

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{
    int i;
    alarm(3);
    while(1){
       sleep(1);
       i++;
       printf("i = %d\n",i);
    } 
    return 0;
}

 编译 alarm.c,并运行。 如下图所示, 设定的时间(3 秒) 超过后产生 SIGALARM 信号, 默认动作是终止进程。

 信号接收

接收信号: 如果要让我们接收信号的进程可以接收到信号, 那么这个进程就不能停止。 让进程不停止有三种方法:
 while
 sleep
 pause
方法一;
while.c

#include <stdio.h>
#include <unistd.h>
void main(void)
{
    while(1){
       sleep(1);
       printf("hello world\n");
    }
}

编译运行结果如下所示, 按 ctrl+C 会发送 SIGINT 信号:

方法二;
sleep.c

#include <stdio.h>
#include <unistd.h>
void main(void)
{
    sleep(60);
}

 编译运行, 如下图所示, 会休眠 60s.

方法三
使用 pause()函数, 函数详解如下:

函数int pause(void)
头文件#include <unistd.h>
返回值进程被信号中断后一直返回-1
功能将进程挂起, 等待信号

pause.c

#include <stdio.h>
#include <unistd.h>
void main(void)
{
    printf("pause before\n");
    pause();
    printf("pause after\n");
}

 编译程序并运行, 如下图所示:

输入以下命令查看进程, 如下图所示:


按 ctrl+C 键, pause 进程终止, 再次查看 pause 的进程, 如下图所示:
 

信号处理
 信号是由操作系统来处理的, 说明信号的处理在内核态。 信号不一定会立即被处理, 此时会储存在信号的信号表中。处理过程示意图:

 

由上图中可看出信号有三种处理方式:
1.默认方式(通常是终止进程) ,
2.忽略, 不进行任何操作。
3.捕捉并处理调用信号处理器(回调函数形式) 。

函数sighandler_t signal(int signum, sighandler_t handler);
可以简化成 signal(参数 1, 参数 2);
头文件#include <unistd.h>
typedef void (*sighandler_t)(int);
参数 1我们要进行处理的信号, 系统的信号我们可以在终端键入 kill -l 查看。
参数 2处理的方式(是系统默认还是忽略还是捕获)
忽略该信号, 填写“SIG_IGN” ;
采用系统默认方式处理该信号, 填写“SIG_DFL” ;
捕获到信号后执行此函数内容,
定义格式为“typedef void (*sighandler_t)(int)” , sighandler_t 代表一个函数指针。
返回值调用成功返回最后一次注册信号调用 signal()时的 handler 值; 失败返回 SIG_ERR。
功能改变收到信号后的动作。

实验 1 代码实现信号忽略

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
int main(void)
{
    signal(SIGINT,SIG_IGN);
    while(1){
       printf("wait signal\n");
       sleep(1);
    } 
    return 0;
}

 编译运行程序, 如下图所示, 当我们按下 ctrl+C 键的时候, 信号被忽略。

实验 2: 代码实现采用系统默认方式处理该信号

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
int main(void)
{
    signal(SIGINT,SIG_DFL);
    while(1){
       printf("wait signal\n");
       sleep(1);
    } 
    return 0;
}

 编译运行程序, 如下图所示, 按 ctrl+c 可以终止程序。

实验 3 代码实现捕获到信号后执行此函数内容

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void myfun(int sig)
{
    if(sig == SIGINT){
        printf("get sigint\n");
    }
}

int main(void)
{
    signal(SIGINT,myfun);
    while(1){
        sleep(1);
        printf("wait signal\n");
    } 
    return 0;
}

 编译运行程序如下图所示,当我们按下 ctrl+c 时, 显示 myfun 函数里面的打印信息。

我们再打开另一个终端, 输入如下图所示的命令也可以实现同样的效果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木士易

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值