zoukankan      html  css  js  c++  java
  • linux第2天 信号 wait

    孤儿进程和僵尸进程

     如果父进程先退出,子进程还没退出那么子进程的父进程将变为init进程。(注:任何一个进程都必须有父进程)

    如果子进程先退出,父进程还没退出,那么子进程必须等到父进程捕获到了子进程的退出状态才真正结束,否则这个时候子进程就成为僵进程。

    孤儿进程

    如果父亲进程先结束,子进程会托孤给1号进程

    父进程注册SIGCHLD信号,然后在回调函数里while (waitpid(0, NULL, WNOHANG) > 0); 注意这个分号不能少.

    避免僵尸进程的另一种方法

    #include <signal.h>

    signal(SIGCHLD, SIG_IGN); 告诉内核,本进程要忽略SIGCHLD,请内核大哥帮忙处理.

    在SIGCHLD信号被显式忽略的情况下,2.6内核子进程将直接退出并释放资源,等父进程调用waitpid时,发现对应的pid进程已不存在,因而返回-1错误,errno为10(No child processes)。
    从某位高手的博客里看到了这个解释。大概就是因为显示忽略SIGCHLD信号时,内核会处理子进程的退出状态,再调用waitpid的时候,就找不到子进程了。不显示忽略的时候,内核不会对子进程的退出做处理,所以还是能找到的。至于设置了信号处理函数之后为什么还是能正常返回?我推测应该是进程同步问题。信号处理函数对这个信号的处理是异步的,所以等到空闲时,父进程才会调用这个函数来处理信号,在这之前,waitpid就已经发挥作用,所以正常返回。由于初学,所以也不太懂,还请高手指正阿!

    1、wait和waitpid出现的原因

    SIGCHLD

      当子进程退出的时候,内核会向父进程发送SIGCHLD信号,子进程的退出是个异步事件(子进程可以在父进程运行的任何时刻终止)

      子进程退出时,内核将子进程置为僵尸状态,这个进程称为僵尸进程,它只保留最小的一些内核数据结构,以便父进程查询子进程的退出状态。

    父进程查询子进程的退出状态可以用wait/waitpid函数

    Wait获取status后检测处理

    宏定义     描述

    WIFEXITED(status)  如果子进程正常结束,返回一个非零值

      WEXITSTATUS(status)      如果WIFEXITED非零,返回子进程退出码

    WIFSIGNALED(status)     子进程因为捕获信号而终止,返回非零值

      WTERMSIG(status) 如果WIFSIGNALED非零,返回信号代码

    WIFSTOPPED(status)       如果子进程被暂停,返回一个非零值

      WSTOPSIG(status)   如果WIFSTOPPED非零,返回一个信号代码

    用来等待某个特定进程的结束

    waitpid(pid_t pid, int *status,int options)

    对于waitpid的p I d参数的解释与其值有关:

     pid == -1 等待任一子进程。于是在这一功能方面waitpid与wait等效。

     pid > 0 等待其进程I D与p I d相等的子进程。

     pid == 0 等待其组I D等于调用进程的组I D的任一子进程。换句话说是与调用者进程同在一个组的进程。

     pid < -1 等待其组I D等于p I d的绝对值的任一子进程。

    Wait和waitpid区别和联系

      在一个子进程终止前,wait 使其调用者阻塞,而waitpid 有一选择项,可使调用者不阻塞。

      waitpid并不等待第一个终止的子进程—它有若干个选择项,可以控制它所等待的特定进程。

      实际上wait函数是waitpid函数的一个特例。

    abort() //会发出一个6号信号

    会话期:是一个或者多个进程组的集合,通常一个会话期开始与用户登录,终止于用户退出。在此期间,该用户运行的所有进程都属于这个会话期。

    中断

      中断是系统对于异步事件的响应()

      中断信号

      中断源

      现场信息

      中断处理程序

      中断向量表

    异步事件的响应:进程执行代码的过程中可以随时被打断,然后去执行异常处理程序

    生活中的中断和计算机系统中的中断

    4)中断的其他概念

    中断向量表保存了中断处理程序的入口地址。

    中断个数固定,操作系统启动时初始化中断向量表。

    中断有优先级(有人敲门,有人打电话,有优先级)

    中断可以屏蔽(张三可以屏蔽电话)。

    信号名称         描述

    SIGABRT  进程停止运行 6

    SIGALRM  警告钟

    SIGFPE      算述运算例外

    SIGHUP    系统挂断

    SIGILL       非法指令

    SIGINT      终端中断 2

    SIGKILL     停止进程(此信号不能被忽略或捕获)

    SIGPIPE    向没有读者的管道写入数据

    SIGSEGV  无效内存段访问

    SIGQUIT   终端退出3

    SIGTERM 终止

    SIGUSR1  用户定义信号1

    SIGUSR2  用户定义信号2

    SIGCHLD  子进程已经停止或退出

    SIGCONT 如果被停止则继续执行

    SIGSTOP   停止执行

    SIGTSTP   终端停止信号

    SIGTOUT  后台进程请求进行写操作

    SIGTTIN   后台进程请求进行读操作

    进程对信号的三种相应

      忽略信号

             不采取任何操作、有两个信号不能被忽略:SIGKILL(9号信号)和SIGSTOP。

    思考1:为什么进程不能忽略SIGKILL、SIGSTOP信号。(如果应用程序可以忽略这2个信号,系统管理无法杀死、暂停进程,无法对系统进行管理。)。SIGKILL(9号信号)和SIGSTOP信号是不能被捕获的。

      捕获并处理信号

             内核中断正在执行的代码,转去执行先前注册过的处理程序。

      执行默认操作

             默认操作通常是终止进程,这取决于被发送的信号。

    signal信号安装函数

    函数原型:__sighandler_t signal(intsignum, __sighandler_t handler);

    返回值,如果成功则返回信号以前的行为

    不可靠信号PK可靠信号

    不可靠信号是前32个信号,不支持排队.在快速发送时可能会造成丢失,因为在进程PCB控制的信号未决信号集,是不能排队的,多个同种信号的到来,只会有一个同种信号在未决集.

    可靠信号

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

    sleep函数几点说明

    1)sleep函数作用,让进程睡眠。

    2)能被信号打断,然后处理信号函数以后,就不再睡眠了。直接向下执行代码

    3)sleep函数的返回值,是剩余的秒数

    raise

      raise

      给自己发送信号。raise(sig)等价于kill(getpid(), sig);

      killpg

     给进程组发送信号。killpg(pgrp, sig)等价于kill(-pgrp, sig);

      sigqueue

      给进程发送信号,支持排队,可以附带信息。

    pause()函数

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

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

    alarm函数,设置一个闹钟延迟发送信号

    告诉linux内核n秒中以后,发送SIGALRM信号;;

    可重入函数概念

      为了增强程序的稳定性,在信号处理函数中应使用可重入函数。

      所谓可重入函数是指一个可以被多个任务调用的过程,任务在调用时不必担心数据是否会出错。因为进程在收到信号后,就将跳转到信号处理函数去接着执行。如果信号处理函数中使用了不可重入函数,那么信号处理函数可能会修改原来进程中不应该被修改的数据,这样进程从信号处理函数中返回接着执行时,可能会出现不可预料的后果。不可再入函数在信号处理函数中被视为不安全函数。

    满足下列条件的函数多数是不可再入的:(1)使用静态的数据结构,如getlogin(),gmtime(),getgrgid(),getgrnam(),getpwuid()以及getpwnam()等等;(2)函数实现时,调用了malloc()或者free()函数;(3)实现时使用了标准I/O函数的

    信号在内核中的表示

      执行信号的处理动作称为信号递达(Delivery),信号从产生到递达之间的状态,称为信号未决(Pending)。

    进程可以选择阻塞(Block)某个信号。被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。

    画图

    当一个信号发送给一个进程时,进程控制块pcb控制着这个信号的走向,未决集(只可读,不可写),阻塞集(可读可写)都是64位数,因为有64个信号,每个位对应一个信号,一个信号要通过这两个集才能抵达

    未决集初始状态全为0,信号首先进入到未决集,未决集的相应位变成1,然后查看阻塞位,如果阻塞位是1,那么信号无法通过,就一直在未决集,此时叫做未决态,一旦阻塞位变成0了,信号马上通过,未决集还没有变成0,

    信号最后由程序决定如何处理(忽略,捕捉,默认行为),处理时未决集变成0,可以接收新的信号.

    在信号处理函数中对某个信号进行解除阻塞时,则只是将pending位清0

    信号集操作函数(状态字表示)

      #include <signal.h>

      intsigemptyset(sigset_t *set);把信号集清空 64bit/8=8个字节   sigset_t是一个64位的数

      intsigfillset(sigset_t *set);把信号集全填写成1

      intsigaddset(sigset_t *set, intsigno);根据signo,把信号集中的对应为置成1

      intsigdelset(sigset_t *set, intsigno);根据signo,把信号集中的对应为置成0

    intsigismember(constsigset_t *set, intsigno);//判断signo是否在信号集中

    sigprocmask读取或更改进程的信号屏蔽状态字(block)

      #include <signal.h>

      int sigprocmask(int how, const sigset_t *set, sigset_t *oset);

      功能:读取或更改进程的信号屏蔽字。

      返回值:若成功则为0,若出错则为-1

    sigpending获取信号未决状态字(pending)信息

       #include <signal.h>

    int sigpending(sigset_t *set);

    sigaction函数

    信号注册函数,与signal一样,只不过功能更加强大.

    int sigaction(int signum,const struct sigaction *act,const struct sigaction *old);

    sigaction结构体

      第二个参数最为重要,其中包含了对指定信号的处理、信号所传递的信息、信号处理函数执行过程中应屏蔽掉哪些函数等等

    structsigaction {

             void (*sa_handler)(int);   //信号处理程序不接受额外数据

             void (*sa_sigaction)(int, siginfo_t *, void *); //信号处理程序能接受额外数据,和sigqueue配合使用

             sigset_t sa_mask; //信号处理函数执行的过程中阻塞哪些信号 sa_mask类型与未注册的信号集一样.但不需要sigprocmask

             int sa_flags; //影响信号的行为SA_SIGINFO表示能接受数据

             void (*sa_restorer)(void); //废弃

    };

    注意1:回调函数句柄sa_handler、sa_sigaction只能任选其一。

     

    sigqueue新的信号发送函数,功能更强大.

        struct sigaction act;
        union sigval  mysigval;
        
        mysigval.sival_int = 111;
        sigemptyset(&act.sa_mask); //信号处理过程中不阻塞其它信号,
        act.sa_flags = SA_SIGINFO; //表示发送数据外加新的内容
        act.sa_sigaction = msigaction; //回调函数赋值
        sigaction(SIGINT, &act, NULL);  //注册信号
        
        sigqueue(getpid(), SIGINT, mysigval); //发送信号
    

      

      sigqueue()比kill()传递了更多的附加信息,但sigqueue()只能向一个进程发送信号,而不能发送信号给一个进程组。

  • 相关阅读:
    C字符串和C++中string的区别 &amp;&amp;&amp;&amp;C++中int型与string型互相转换
    UML的类图关系分为: 关联、聚合/组合、依赖、泛化(继承)
    STL map详细用法和make_pair函数
    字符串旋转(str.find()---KMP)
    层次遍历二叉树
    图像特征提取三大法宝:HOG特征,LBP特征,Haar特征
    位运算---整数间的转化
    最大公倍数
    单链表的实现
    jsp下Kindeditor环境搭建
  • 原文地址:https://www.cnblogs.com/c-slmax/p/5233444.html
Copyright © 2011-2022 走看看