(P17)信号:信号分类、可靠信号与不可靠信号、信号发送、pause

1.信号分类

  • 可靠信号
    可靠信号都是实时信号;不可靠信号都是非实时信号
  • 不可靠信号
  • 实时信号
  • 非实时信号

2.可靠信号与不可靠信号

  • 不可靠信号:若一次性来了多个信号,这些信号是不会排队的,只会保留一个,后续的信号都被丢弃掉

  • linux信号机制基本上是从unix系统中继承过来的。早期unix系统中的信号机制比较简单和原始,后来在实践中暴露出一些问题:
    (1)进程每次处理信号后,就将对信号的响应设置为默认动作。在某些情况下,将导致对信号的错误处理;因此,用户如果不希望这样的操作,那么就要在信号处理函数结尾再一次调用signal(),重新安装该信号
    (2)早期unix的不可靠信号主要指的是进程可能对信号做出错误的反应以及信号可能丢失
    (3)linux支持不可靠信号,但是对不可靠信号机制做了改进:在调用完信号处理函数后,不必重新调用该信号的安装函数(信号安装函数是在可靠机制上的实现)。
    因此,linux下的不可靠信号问题主要指的是信号可能丢失

  • 可靠信号:可靠信号支持排队,不会丢失
    (1)随着时间的发展,实践证明了有必要对信号的原始机制加以改进和扩充。所以,后来出现的各种unix版本分别在这方面进行了研究,力求实现“可靠信号”。由于原来定义的信号已有许多应用,不好再改动,最终只好又新增加了一些信号,并在一开始就把它们定义为可靠信号,这些信号支持排队,不会丢失。
    (2)同时,信号的发送和安装也出现了新版本:信号发送函数sigqueue()以及信号安装函数sigaction()

  • 可靠信号:又称之为RT信号,实时信号

root@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
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX

  • 实时信号
    实时信号没有特殊的含义,它的含义由用户来决定
    man 7 signal
    (1)早期Unix系统只定义了32种信号,Red hat7.2支持64种信号,编号0-63(SIGRTMIN=31,SIGRTMAX=63),将来可能进一步增加。
    前32种信号已经有了预定义值,每个信号有了确定的用途以及含义,并且每种信号都有各自的缺省动作。eg:按键盘的ctrl c,会产生SIGINT信号,对该信号的默认反应就是进程终止。
    后32个信号表示实时信号,等同于前面阐述的是可靠信号。这保证了发送的多个实时信号都被接收。实时信号是POSIX标准的一部分,可用于应用进程。
    (2)非实时信号都不支持排队,都是不可靠信号;
    实时信号都支持排队,都是可靠信号;

3.信号发送

  • kill
kill -9 pid
9号信号不能被忽略,也不能被捕获
含义:向pid进程发送9号信号,
man 2 kill
kill(pid, sig);
pid>0,信号sig发送给进程号=pid的进程
pid=0,信号sig被发送给调用者进程所在组中的每一个进程
pid=-1,信号sig将被发送调用者进程有权限发送的每一个进程,除了1号进程之外
pid<-1,信号将发送给进程组=-pid中的每一个进程
  • raise
    man raise
    给自己发送信号。
raise(sig)等价于kill(getpid(),sig)
  • killpg
    给进程组发送信号。
killpg(pgrp, sig)等价于kill(-pgrp, sig)
  • sigqueue
    给进程发送信号,支持排队,可以附带信息

  • eg1:P17kill.c

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.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 handle(int sig);
int main(int argc, char *argv[])
{
    if (signal(SIGUSR1, handle) == SIG_ERR)
        ERR_EXIT("signal error");

    pid_t pid = fork();
    if (pid == 0)
        ERR_EXIT("fork error");
    if (pid == 0)
    {
        kill(getppid(), SIGUSR1);//向父进程发送SIGUSR1信号
        exit(EXIT_SUCCESS);
    }    
    //防止sleep被信号打断
    //5s的时间被信号中断了,留下的剩余时间4s,再睡眠4s即可
    int n = 5;
    do
    {
        n = sleep(n);
    } while (n > 0);
    
    // sleep(5);//父进程运行一段时间,直接使用sleep,会被信号打断 man 3 sleep
    return 0;
}

void handle(int sig)
{
    printf("recv a sig=%d\n", sig);
}

  • 测试:
    在这里插入图片描述

  • eg2:P17kill2.c

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.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 handle(int sig);
int main(int argc, char *argv[])
{
    if (signal(SIGUSR1, handle) == SIG_ERR)
        ERR_EXIT("signal error");

    pid_t pid = fork();
    if (pid == 0)
        ERR_EXIT("fork error");
    if (pid == 0)
    {
        pid = getpgrp();//获取进程组的号码
        kill(-pid, SIGUSR1);

        //上面2行等价于下面的1行代码
        // killpg(getpgrp(), SIGUSR1);
        exit(EXIT_SUCCESS);
    }    

    int n = 5;
    do
    {
        n = sleep(n);
    } while (n > 0);
    
    return 0;
}

void handle(int sig)
{
    printf("recv a sig=%d\n", sig);
}

  • 测试:
    因为signal安装信号的函数是在fork之前安装的,子进程也会继承SIGUSR1信号,向-pid发送信号的时候,相当于向进程组中的每一个进程都发送SIGUSR1信号,所以父子进程都会收到SIGUSR1信号,所以总共有2行,子进程收到信号后会退出,父进程收到信号后会sleep 5s
    在这里插入图片描述

4.pause

  • 让出CPU时间片,让别人运行

  • 将进程置为可中断睡眠状态。然后它调用scedule(),使linux进程调度器找到另一个进程来运行

  • pause使调用者进程挂起,直到一个进程被捕获

  • eg:P17pause.c

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.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 handle(int sig);
int main(int argc, char *argv[])
{
    //SIGINT对应键盘ctrl c
    //signal注册安装一个信号
    if (signal(SIGINT, handle) == SIG_ERR)
        ERR_EXIT("signal error");
    for(;;)
    {
        pause();//这样进程就处于暂停的状态
        printf("pause return \n");
    }
        
    return 0;
}

void handle(int sig)
{
    printf("recv a sig=%d\n", sig);
    sleep(1);
}

  • 测试:
    程序暂停在pause的位置
    在这里插入图片描述
    pause什么时候会返回?
    按下ctrl c,会产生SIGINIT信号,会转到信号处理程序来处理,当信号处理程序完了之后,pause程序就返回了,pause会被执行完的信号处理程序去唤醒
    在这里插入图片描述
    ctrl \表示退出
    在这里插入图片描述

  • Makefile

.PHONY:clean all
CC=gcc
CFLAGS=-Wall -g
BIN=01kill
all:$(BIN)
%.o:%.c
	$(CC) $(CFLAGS) -c $< -o $@
clean:
	rm -f *.o $(BIN)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

喜欢打篮球的普通人

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

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

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

打赏作者

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

抵扣说明:

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

余额充值