首页 > shell教程 阅读:0

关于Linux Shell中进程、信号和捕获的总结

信号是学习 linux 时必须要熟悉的一部分,没有了信号 Linux 系统就没办法正常的工作。而我们作为 Linux 程序员,也要用到信号来进行程序的运行,没有了信号,我们的工作将会变得一团糟。这几天,我把刚刚学的关于信号的知识点总结了一下,分享给大家,希望可以对大家有所帮助。

信号的概念

信号是 linux 系统为了响应某些状况而产生的事件。进程收到信号后应该采取相应的动作

哪些情况会引发信号

  1. 键盘事件   ctrl +c  ctrl +\

  2. 非法内存   如果内存管理出错,系统就会发送一个信号进行处理

  3. 硬件故障   同样的,硬件出现故障系统也会产生一个信号

  4. 环境切换   比如说从用户态切换到其他态,状态的改变也会发送一个信号,这个信号会告知给系统

怎样查看信号呢?

kill -l

这个命令就可以查看所有的信号啦,现在信号已经增加到 65 个了,但是在这里我要提一下,从 33-64 这些信号一般不会采用,这是为了区分可靠信号和不可靠信号而新增加的 32 个信号。关于可靠信号和不可靠信号,我会在下面加以说明滴。

下面我把最常用到的信号给大家解释一下

#define	SIGHUP	1	/* hangup */#define	SIGINT	2	/* interrupt */#define	SIGQUIT	3	/* quit */#define	SIGILL	4	/* illegal instruction (not reset when caught) */#define	SIGTRAP	5	/* trace trap (not reset when caught) */#define	SIGABRT	6	/* abort() */#if  (defined(_POSIX_C_SOURCE) && !defined(_DARWIN_C_SOURCE))#define	SIGPOLL	7	/* pollable event ([XSR] generated, not supported) */#else	/* (!_POSIX_C_SOURCE || _DARWIN_C_SOURCE) */#define	SIGIOT	SIGABRT	/* compatibility */#define	SIGEMT	7	/* EMT instruction */#endif	/* (!_POSIX_C_SOURCE || _DARWIN_C_SOURCE) */#define	SIGFPE	8	/* floating point exception */#define	SIGKILL	9	/* kill (cannot be caught or ignored) */#define	SIGBUS	10	/* bus error */#define	SIGSEGV	11	/* segmentation violation */#define	SIGSYS	12	/* bad argument to system call */#define	SIGPIPE	13	/* write on a pipe with no one to read it */#define	SIGALRM	14	/* alarm clock */#define	SIGTERM	15	/* software termination signal from kill */#define	SIGURG	16	/* urgent condition on IO channel */#define	SIGSTOP	17	/* sendable stop signal not from tty */#define	SIGTSTP	18	/* stop signal from tty */#define	SIGCONT	19	/* continue a stopped process */#define	SIGCHLD	20	/* to parent on child stop or exit */#define	SIGTTIN	21	/* to readers pgrp upon background tty read */#define	SIGTTOU	22	/* like TTIN for output if (tp->t_local&LTOSTOP) */#if  (!defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE))#define	SIGIO	23	/* input/output possible signal */#endif#define	SIGXCPU	24	/* exceeded CPU time limit */#define	SIGXFSZ	25	/* exceeded file size limit */#define	SIGVTALRM 26	/* virtual time alarm */#define	SIGPROF	27	/* profiling time alarm */#if  (!defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE))#define SIGWINCH 28	/* window size changes */#define SIGINFO	29	/* information request */#endif#define SIGUSR1 30	/* user defined signal 1 */#define SIGUSR2 31	/* user defined signal 2 */
信号量值名称发送方式说明
(2)SIGINTctrl +c终止信号
(3)SIGQUITctrl  + \暂停信号,放入后台
(4)SIGILL
非法指令
(5)SIGTRAPabort(3)进程异常终止
(7)SIGBUS
(虚实关系建立)       总线错误(从写的位置到物理内存,操作系统没有将磁盘的开始位置到物理内存之间建立      联系    mmap(把虚拟内存和磁盘文件的关系映射起来,如果磁盘大小大于 0,就建立这种关系
(9)SIGKILLkill - 9 pid杀死进程
(11)SIGSEGV
段错误
(13)SIGPIPE
管道破裂
(14)SIGALRM
闹钟
(15)SIGTERM
缺省终止某个进程,终止掉
(17)SIGSTOP
子进程死的时候会给父进程发送这个信号
(19)SIGCONT
进程暂停
(23)SIGURG
紧急数据
(29)SIGINFO
异步 IO

信号的默认处理方式:

这个就要使用   man 手册自己去查找啦,有好多呢,记住常用的就可以啦  
man 7 signal  在第七页,所以直接用 man 7

进程收到信号的三种处理方式

  1. 默认:如果是系统默认的话,那就会终止这个进程

  2. 忽略 :信号来了我们不处理,装作没看到    SIGKILL  SIGSTOP 不能忽略

  3. 捕获并处理 :当信号来了,执行我们自己写的代码(捕获信号这个动作是需要我们完成的)  SIGKILL SIGSTOP 不能捕获

注册信号

typedef void  (\* sighandler_t) (int);sighandler_t signal (int signum   //要注册的信号,sighandler_t handler);    //信号执行函数 1.自己定义函数2.SIG_IGN  #define SIG_IGN  (sighandler_t(1))把 1 强制转化为函数指针类型3.SIG_DFL  #define SIG_IGN  (sighandler_t(0))把 0 强制转化为函数指针类型

返回错误 SIG_ERR#define SIG_ERR

信号是异步事件,当信号到达时,保存当前的执行环境,转去执行信号处理函数,当信号处理函数完毕,恢复现场,继续执行

这里我们看一下可靠信号和不可靠信号的区别吧

不可靠信号

Linux 的信号继承自早期的 Unix 信号,Unix 信号的缺陷 1.信号处理函数执行完毕,信号恢复成默认处理方式(Linux 已经改进) 2.会出现信号丢失,信号不排队
1-31  都是不可靠的,会出现信号丢失现象

可靠信号

34-64 重新设计的一套信号集合
不会出现信号丢失,支持排队,信号处理函数执行完毕,不会恢复成缺省处理方式
实时信号 : 就是可靠信号
非实时信号:不可靠信号

发送信号:

kill  -信号值 pid
int kill(int pid,int signum)
   pid > 0 :发送给 pid 进程
   pid = 0 :调用者所在进程组的任一进程
   pid = -1:  有权发送的任何一个进程,除了 1
   pid < -1 |pid|进程组所有的进程

进程组:进程组中有若干个进程

用管道连接的进程, fork 创建的父子进程都属于同一个进程组
sleep 返回值 > 0 表示还剩多少秒没睡就被信号打断

给自己发信号

raise(  int signum  )
kill(getpid() ,signum)

给进程组发信号
int killpg(int gid,int signum);
暂停进程,直到被信号打断
int  pause( void )把当前进程变成就绪态,让出 CPU  calling process
SIGALRM
int alarm(int sec)
当 sec 规定的时间到了,触发 SIGALRM 信号
如果 sec 是 0,表示清除信号

下面是两个 SIGALRM 信号的使用范例,希望通过这两个实例可以加深你们对于闹钟信号的理解啊

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <signal.h>void handler(int s) //信号到来,则执行这个函数,输出超时{
    printf("超时\n");
    exit(1);}int main(){
    char buf[100] = {};
    printf("输入名字");

    signal(SIGALRM, handler); //定义一个信号函数,当 SIGALRM 信号发过来时,执行 handler 函数
    alarm(5);                 //设置五秒的时钟,五秒内如果没有执行输入操作就会发送信号

    scanf("%s", buf);
    alarm(0); //如果五秒内执行了操作,那就清空闹钟

    printf("名字为:%s\n", buf);

    for (;;) //验证闹钟时间已经清空
    {
        fflush(stdout);
        printf(".");
        sleep(1);
    }}

执行结果

基于闹钟原理,我们再来实现一个更加复杂的小型考试系统

代码如下

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <signal.h>int count = 0; //初始化int wrong = 0; //初始化void handler(int s) //当闹钟信号发过来,执行这个函数,输出对错的个数{
    printf("time out \n");
    printf("right = %d,wrong = %d\n ", count, wrong);
    exit(1);}int main(){
    int i = 0;
    signal(SIGALRM, handler); //定义信号函数接收信号

    alarm(30);               //设置闹钟时间为 30 秒
    srand(getpid());         //用子进程 id 作为随机种子数
    for (i = 0; i < 10; i++) //循环十次,输出随机数相加结果
    {
        int left = rand() % 10;
        int right = rand() % 10;
        printf("%d + %d=", left, right);
        int ret;

        while (getchar() != '\n')
            ; //清空输入缓冲区,防止异常输入使得程序崩溃
        scanf("%d", &ret);
        if (left + right == ret) //判断对错
        {
            count++;
        }
        else
        {
            wrong++;
        }
    }
    printf("做完了\n");
    printf("right =%d,wrong = %d \n", count, wrong);}

程序结果如下

从这两个实例,我们已经可以知道时钟的简单用法啦,那么说了这么多,我觉得你们也可以自己上手去做一个以闹钟为中心的小程序啦。再另一篇博客里面我会讲一讲时钟的用法,希望可以帮助到你们哦。
注:我也是个初学者啦,如果哪里又纰漏之处,欢迎指正啊,也可以私信我,我很喜欢交流的。

beylze编程学院,一个分享编程知识和seo优化知识的网站。跟着beylze一起学习,每天都有进步。

通俗易懂,深入浅出,一篇文章只讲一个知识点。

文章不深奥,不需要钻研,在公交、在地铁、在厕所都可以阅读,随时随地涨姿势。

文章不涉及代码,不烧脑细胞,人人都可以学习。

当你决定关注beylze(公众号:beylze),你已然超越了90%的其他从业者!

优秀教程