Linux进程入门学习(七)-信号通信

信号通信

什么是信号?

在操作系统中,当我们无正常结束一程序时,可以用任务管理器强行结束这个进程。在unix/linux 中,具体的实现过程是通过进程A 生成一个信号并发射出去,运行中的进程B捕获到这个信号然后根据这个信号的特定意义做出相应的操作。
信号是UNIX 和Linux 系统响应某些条件而产生的一个事件,接收到该信号的进程会相应地采取一些行动。信号的处理实质是能软件中断这样的机制来实现的。
通常信号是由一个错误产生的。但它们还可以作为进程间通信或修改行为的一种方式,明确地由一个进程发送给另一个进程。

信号的种类

在linux 系统中预定义了多种信号,主要分布在头文件signal.h 中,信号都以SIG 开头。
可以通过以下方式查看:#kill -l
kill-l
说明:
程序不可捕获、阻塞或忽略的信号有:SIGKILL,SIGSTOP
不能恢复至默认动作的信号有:SIGILL,SIGTRAP
默认会导致进程流产的信号有:SIGABRT,SIGBUS,SIGFPE,SIGILL,SIGIOT,
SIGQUIT,SIGSEGV,SIGTRAP,SIGXCPU,SIGXFSZ
默认会导致进程退出的信号有:SIGALRM,SIGHUP,SIGINT,SIGKILL,
SIGPIPE,SIGPOLL,SIGPROF,SIGSYS,SIGTERM,SIGUSR1,SIGUSR2,SIGVTALRM
默认会导致进程停止的信号有:SIGSTOP,SIGTSTP,SIGTTIN,SIGTTOU
默认进程忽略的信号有:SIGCHLD,SIGPWR,SIGURG,SIGWINCH

信号的工作原理

a.进程A 向内核设置进程B 信号
b.内核管理信号,并在信号成立时向进程B 发射信号
c.进程B 捕获到信号并执行响应
signal
整个通信过程中,不同阶段使用不同的接口函数
1)发送阶段

kill 函数
raise 函数
alarm 函数

2)处理阶段

signal 函数

3)接收阶段

while(1);
sleep();
pause();

下面分别介绍信号通信过程中,每个阶段所使用到的函数接口

信号的发送

kill 函数实现向进程发送信号
头文件

#include <sys/types.h>
#include <signal.h>

函数原型

    int kill(pid_t pid, int sig);

返回值

成功:0
失败:-1

参数列表

pid_t pid:要操作的进程PID
int sig : 发送的信号(信号序号或者信号名)

示例:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
int main()
{
    int pid,sig;
    while(1)
    {
        printf("please input process pid and sig:\n");
        scanf("%d%d", &pid,&sig);
        //发送指定信号给指定进程
        kill(pid,sig);
    }
    return 0;
}

raise 函数用于发送信号给自己
头文件

#include <signal.h>

函数原型

int raise(int sig);

返回值

成功返回0,失败返回非0

参数

Sig:发送的信号

示例:使用raise 实现自杀

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

int main()
{

    printf("raise before \n");
    raise(9);
    printf("raise after \n");

    return 0;
}

运行结果:

xie@ubuntu:/mnt/hgfs/share/ISP/pratice8.9$ vi raise.c 
xie@ubuntu:/mnt/hgfs/share/ISP/pratice8.9$ gcc raise.c -o  raise
xie@ubuntu:/mnt/hgfs/share/ISP/pratice8.9$ ./raise
raise before 
Killed
xie@ubuntu:/mnt/hgfs/share/ISP/pratice8.9$ 

以上代码中,执行完raise 函数后,并没有打印raise after,说明raise 设置的信号是立即有效的。

alarm 函数用于实现延时触发信号给自己。
函数说明
用来设置信号在经过参数seconds 指定的秒数后传送给目前的进程.
头文件

#include <unistd.h>

函数原型

unsigned int alarm(unsigned int seconds);

参数

seconds,定时时间,单位为秒

返回值

返回之前闹钟的剩余秒数, 如果之前未设闹钟则返回0

示例:

#include <stdio.h> 
#include <unistd.h>
#include <signal.h>
int main()
{
    int i=0;
    printf("alarm before \n");
    alarm(9);
    printf("raise after \n");
    while(i<15)
    {
        printf("cnt=%d\n",++i);
        sleep(1);
    }
    return 0;
}

运行结果:

alarm before 
raise after 
cnt=1
cnt=2
cnt=3
cnt=4
cnt=5
cnt=6
cnt=7
cnt=8
cnt=9
Alarm clock
xie@ubuntu:/mnt/hgfs/share/ISP/pratice8.9$

pause 函数用于实现进程的休眠
头文件

#include <unistd.h>

函数原型

int pause(void);

示例:

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

int main(int argc, char **argv)
{
    printf("pid = %d\n", getpid());
    pause();
    printf("pause done \n");

    return 0;
}

运行结果:

xie@ubuntu:/mnt/hgfs/share/ISP/pratice8.9$ ./pause
pid = 3125
^C          //ctrl+c
xie@ubuntu:/mnt/hgfs/share/ISP/pratice8.9$

以上代码中,进程调用pause 后会一直处理休眠态,这时可以按下ctrl + C 来结束进程。

信号的处理

通过前面的学习,我们已经知道,当内核接收到信号后,会在相应时间内对进程作出操作,而这样的操作都是信号的默认操作,如
alarm 函数实现的是配置14) SIGALRM 终止进程
ctrl+c 2) SIGINT 终止进程
kill 9) SIGKILL 终止进程
ctrl+z 20) SIGTSTP 暂停进程
可是,在很多实际情况下,当触发出信号后,我们并不打算让内核来中止或者暂停我们的进程,而是中断跳出处理其他事,完了再回来这时我们该怎么办? 可以采用signal 来处理

signal 函数用于获捕信号
头文件

#include <signal.h>

函数原型

typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

返回值

    sighandler_t 是一个函数指针变量,含有一个整形参数,即信号编号,返回值是void
如果返回负数,则出错。

参数列表

信号signum
函数指针sighandler_t handler,是一个系统的回调函数,可取以下三种情况
a.SIG_IGN 忽略本次触发的信号
b.SIG_DFL 采用系统默认的方式处理
c.自定义的信号处理函数指针

示例:请为SIGALRM 自定义一个对应的处理函数。

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

void alarm_handler(int signum)
{
    int i=0;
    while(i<10)
    {
        printf("alarm handler, signum = %d\n", signum);
        i++;
        sleep(1);
    }
    return ;
}

int main(int argc, char **argv)
{
    int i=0;
    //添加处理函数
    int ret;
    ret = (int)signal(SIGALRM, alarm_handler);
    if(ret < 0)
    {
        printf("fail to signal \n");
        return -1;
    }
    //延迟触发SIGALRM信号
    printf("alarm before \n");
    alarm(10);
    printf("alarm after \n");

    while(i<20)
    {
        printf("main handler, cnt = %d\n",++i);
        sleep(1);
    }
    while(1);
    return 0;
}

运行结果:

xie@ubuntu:/mnt/hgfs/share/ISP/pratice8.9$ ./signal
alarm before 
alarm after 
main handler, cnt = 1
main handler, cnt = 2
main handler, cnt = 3
main handler, cnt = 4
main handler, cnt = 5
main handler, cnt = 6
main handler, cnt = 7
main handler, cnt = 8
main handler, cnt = 9
main handler, cnt = 10
alarm handler, signum = 14
alarm handler, signum = 14
alarm handler, signum = 14
alarm handler, signum = 14
alarm handler, signum = 14
alarm handler, signum = 14
alarm handler, signum = 14
alarm handler, signum = 14
alarm handler, signum = 14
alarm handler, signum = 14
main handler, cnt = 11
main handler, cnt = 12
main handler, cnt = 13
main handler, cnt = 14
main handler, cnt = 15
main handler, cnt = 16
main handler, cnt = 17
main handler, cnt = 18
main handler, cnt = 19
main handler, cnt = 20

如果把ret = (int)signal(SIGALRM, alarm_handler);中的alarm_handler换成SIG_IGN(屏蔽该信号),结果将会是如下:

xie@ubuntu:/mnt/hgfs/share/ISP/pratice8.9$ ./signal
alarm before 
alarm after 
main handler, cnt = 1
main handler, cnt = 2
main handler, cnt = 3
main handler, cnt = 4
main handler, cnt = 5
main handler, cnt = 6
main handler, cnt = 7
main handler, cnt = 8
main handler, cnt = 9
main handler, cnt = 10
main handler, cnt = 11
main handler, cnt = 12
main handler, cnt = 13
main handler, cnt = 14
main handler, cnt = 15
main handler, cnt = 16
main handler, cnt = 17
main handler, cnt = 18
main handler, cnt = 19
main handler, cnt = 20

如果把ret = (int)signal(SIGALRM, alarm_handler);中的alarm_handler换成SIG_DFL(恢复默认信号处理方式),结果将会是如下:

xie@ubuntu:/mnt/hgfs/share/ISP/pratice8.9$ ./signal
alarm before 
alarm after 
main handler, cnt = 1
main handler, cnt = 2
main handler, cnt = 3
main handler, cnt = 4
main handler, cnt = 5
main handler, cnt = 6
main handler, cnt = 7
main handler, cnt = 8
main handler, cnt = 9
main handler, cnt = 10
Alarm clock
xie@ubuntu:/mnt/hgfs/share/ISP/pratice8.9$
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值