进程间通信----信号篇

       

目录

        一丶信号的概念

二丶信号的分类

        三丶信号的处理方式

        四丶信号产生的方式

        五丶信号

六丶信号函数

1.发送信号

2.闹钟信号

3.定时器

4.信号处理函数

5.信号的处理过程


 一丶信号的概念

● 信号是在软件层次上对中断机制的一种模拟,是一种 异步通信方式

● 信号可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用它来通知用户空间进程发生了哪些系统事件。

● 如果该进程当前并未处于执行态,则该信号就由内核保存起来,直到该进程恢复执行再传递给它;如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞被取消时才被传递给进程。

二丶信号的分类

在Linux中,信号被分为不可靠信号和可靠信号,一共64种,可以通过kill -l命令来查看

● 不可靠信号:也称为非实时信号,不支持排队,信号可能会丢失,比如发送多次相同的信号,进程只能收到一次,信号值取值区间为1~31

● 可靠信号:也称为实时信号,支持排队,信号不会丢失,发多少次,就可以收到多少次,信号值取值区间为34~64

        三丶信号的处理方式

● 忽略信号:不做任何处理

● 捕捉信号:执行自定义的信号处理函数

● 执行(缺省)默认操作:Linux系统中对每种信号规定了默认操作,即执行信号默认的功

        四丶信号产生的方式

● 对于前台进程,用户可以输入特殊字符来发送,比如输入 Ctrl c

● 系统状态变化:比如alarm定时器到期时将引起SIGALRM信号

● 在终端运行kill命令或在程序中调用kill函数

        五丶信号

SIGKILL:结束进程,不能被忽略不能被捕捉 9

SIGSTOP:结束进程,不能被忽略不能被捕捉 19 停止信号

SIGCHLD:子进程状态改变时给父进程发的信号,不会结束进程 17

SIGINT:结束进程,对应的快捷方式 ctrl c 2

SIGTSTP:暂停信号,对应的快捷方式 ctrl z 20

SIGQUIT:退出信号,对应的快捷方式 ctrl \ 3

SIGALRM:闹钟信号,alarm函数设置定时,当到设定的时间时,内核会向进程发送此信号结束进程 14

SIGTERM:结束终端进程,kill使用时不加数字默认是此信号 15

六丶信号函数

1.发送信号

#include <signal.h>
int kill(pid_t pid, int sig);
功能:信号发送
参数:pid:指定进程
   sig:要发送的信号
返回值:成功 0     
       失败 -1

父子进程间进行通讯

2.闹钟信号

#include <unistd.h>
unsigned int alarm(unsigned int seconds)
功能:在进程中设置一个定时器
参数:seconds:定时时间,单位为秒
返回值:
    如果调用此alarm()前,进程中已经设置了闹钟时间,则返回上一个闹钟时间的剩余时间,否则返回0。
注意:一个进程只能有一个闹钟时间。如果在调用alarm时
	已设置过闹钟时间,则之前的闹钟时间被新值所代替
int pause(void);
功能:用于将调用进程挂起,直到收到信号为止。
#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
    //kill(getpid(), SIGKILL);  //给进程发送信号,此例子是给当前进程发送SIGKILL信号
    // raise(SIGKILL);  //给当前进程发送SIGKILL信号,等同于kill(getpid(), SIGKILL);  
    // while (1);

    pause();  //将进程挂起,作用类似死循环但是不占用CPU
    return 0;
}

3.定时器

unsigned int alarm(unsigned int seconds)
功能:在进程中设置一个定时器。当定时器指定的时间到了时,它就向进程发送SIGALARM信号。
参数:seconds:定时时间,单位为秒
返回值:如果调用此alarm()前,进程中已经设置了闹钟时间,则返回上一个闹钟时间的剩余时间,否则返回0。
注意:一个进程只能有一个闹钟时间。如果在调用alarm时已设置过闹钟时间,则之前的闹钟时间被新值所代替。
常用操作:取消定时器alarm(0),返回旧闹钟余下秒数。
        系统默认对SIGALRM(闹钟到点后内核发送的信号)信号的响应: 如果不对SIGALRM信号进行捕捉或采取措施,默认情况下,闹钟响铃时刻会退出进程。
#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
    printf("%d\n", alarm(10));  //设定闹钟5秒后发送闹钟信号
    sleep(1);                                       //睡眠1秒,此时闹钟还剩9秒
    printf("%d\n", alarm(3));    //打印剩余的9秒,设新闹钟3秒后发送闹钟信号
    pause(); //为了不让进程结束,等待SIGALRM信号产生,产生之后结束当前进程
    return 0;
}

4.信号处理函数

typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
功能:信号处理函数
参数:signum:要处理的信号
      handler:信号处理方式
          SIG_IGN:忽略信号  (忽略 ignore)
          SIG_DFL:执行默认操作 (默认 default)
          handler:捕捉信号 (handler为函数名,可以自定义)
     void handler(int sig){} //函数名可以自定义, 参数为要处理的信号
返回值:成功:设置之前的信号处理方式
      失败:-1
#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>

void handler(int sig) //参数sig代表要处理的信号
{
    if (sig == SIGINT)
        printf("ctrl C: %d\n", sig);
    else if (sig == SIGTSTP)
        printf("ctrl Z: %d\n", sig);
}

int main(int argc, char const *argv[])
{
    signal(SIGINT, SIG_IGN);  //对SIGINIT信号设置忽略方式处理
    //signal(SIGINT,SIG_DFL);  //对SIGINIT信号设置缺省方式处理,也就是默认操作
    signal(SIGINT, handler); //对SIGINIT信号设置捕捉方处理,也就是自定义处理方式
    signal(SIGTSTP, handler);

    //while (1); //为了让进程不要结束,因为等到信号真的来才能验证,不然进程就结束了
    pause();  //收到被捕获处理的信号时会结束挂起
    return 0;
}

5.信号的处理过程

        程序运行在用户空间时->进程由于系统调用或中断进入内核->转向用户空间执行信号处理函数->信号处理函数完毕后进入内核->返回用户空间继续执行程序
练习:

用信号的知识实现司机和售票员问题。
        1)售票员捕捉SIGINT(代表开车)信号,向司机发送SIGUSR1信号,司机打印(let's gogogo)
        2)售票员捕捉SIGQUIT(代表停车)信号,向司机发送SIGUSR2信号,司机打印(stop the bus)
        3)司机捕捉SIGTSTP(代表到达终点站)信号,向售票员发送SIGUSR1信号,售票员打印(please get off the bus)
        4)司机等待售票员下车,之后司机再下车。

分析:司机(父进程)、售票员(子进程)

司机:
        忽略的信号:SIGINT、SIGQUIT
        捕捉的信号:SIGUSR1、SIGUSR2、SIGTSTP

售票员:
        忽略的信号:SIGTSTP
        捕捉的信号:SIGINT、SIGQUIT、SIGUSR1

#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>

pid_t pid;
void saler(int sig)
{
    if (sig == SIGINT)
        kill(getppid(), SIGUSR1);
    if (sig == SIGQUIT)
        kill(getppid(), SIGUSR2);
    if (sig == SIGUSR1)
    {
        printf("pls get off the bus!\n");
        exit(0);
    }
}

void driver(int sig)
{
    if (sig == SIGUSR1)
        printf("let's gogogo!!!~~\n");
    if (sig == SIGUSR2)
        printf("stop the bus!\n");
    if (sig == SIGTSTP)
    {
        kill(pid, SIGUSR1);
        wait(NULL);
        exit(0); //司机等待售票员下车之后再下车
    }
}

int main(int argc, char const *argv[])
{
    pid = fork();
    if (pid < 0)
    {
        perror("fork err");
        return -1;
    }
    else if (pid == 0)
    {
        printf("i am saler!\n");
        signal(SIGINT, saler);
        signal(SIGQUIT, saler);
        signal(SIGUSR1, saler);
        signal(SIGTSTP, SIG_IGN);
    }
    else
    {
        printf("i am driver!\n");
        signal(SIGUSR1, driver);
        signal(SIGUSR2, driver);
        signal(SIGTSTP, driver);
        signal(SIGINT, SIG_IGN);
        signal(SIGQUIT, SIG_IGN);
    }

    while (1)
        pause(); //不能发送一个信号就结束进程,所以可以循环挂起不占用CPU

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值