进程函数一步步理解Linux之信号

上班之余抽点时光出来写写博文,希望对新接触的朋友有帮助。今天在这里和大家一起学习一下进程函数

    

一步步懂得Linux之信号

    作者:gaopenghigh ,转载请注明出处。 (原文地址)

    


    

信号的应用

    

信号的观点

    信号是通知进程发生了某件事的技巧。每个信号都有一个名字,这些名字都以”SIG”开头。 须要注意的是,SIGUSR1和SIGUSR2这两个信号是给用户自定义的,可用于应用程序。

    对于信号,内核有三种处置方式:

    

  • 疏忽。但有两种信号不能被疏忽:SIGKILL和SIGSTOP,因为它们为超级用户供给了使进 程终止或停止的牢靠方法。 * 捕捉信号。前提是要提前通知内核在某个信号发生时调用用户的某个函数。 * 执行系统默认动作。对大部分信号的默认动作是终止进程。

    

signal函数

    signal函数是关于信号最基本的函数。

    #include <signal.h> void (*signal(int signo, void (*func)(int)))(int);

    

    这个函数非常不直观,用畸形一点的语言来讲就是:

    

  • signal函数的返回指是一个函数地址,该函数没有返回值(void),并且须要一个int型 的参数。
  • signal函数须要两个参数,第一个参数是signo,整型,代表信号名;第二个参数是func ,这是一个函数地址,该函数没有返回值(void),且须要一个int型的参数。
  • 总的来讲signal函数就是注册一个信号signo,当这个信号发生时,调用func函数去处置 ,同时signal函数返回注册之前该信号的处置程序的指针。

    参数func可以是SIG_DFL,代表应用默认处置动作,还可以是SIG_IGN,代表疏忽,当 然更可以是其他具体的处置函数,这个函数就叫做信号处置程序(signal handler) 或者信号捕捉程序(signal-catching function),此时处置动作就叫做捕捉。

    从signal的定义来看,不转变进程对信号的处置方式,就不能知道进程原本对信号的处置 方式,稍后介绍的sigaction函数能解决这个问题。

    

信号相干的术语

    信号发生(generation)后,内核会递送(delivery)这个信号给进程,在发生和递送的 这个时光间隔内,称信号是“未决的(pending)”。

    进程可以选择对一些信号“阻塞”,当为进程发生了一个信号,且进程对该信号设置了阻塞 ,且进程对该信号的处置方式是默认或者捕捉,那么该进程就将该信号保持为未决 (pending)状态,直到阻塞被消除,或者进程对该信号的动作改为疏忽。

    如果进程对一个信号接触阻塞之前,该信号发生了多次,那么消除阻塞后,内核也只递送 这个信号一次。

    每个进程都有一个“信号屏蔽字(signal mask),它划定了进程当前要阻塞的信号集。

    

kill和raise函数

    kill发送信号给进程或进程组。 raise答应进程发送一个信号给自己。

    #include <signal.h> int kill(pid_t pid, int signo); int raise(int signo); /* 成功时返回0,出错返回-1 */ /* raise(signo) 等价于 kill(get_pid(), signo) */ /* * 参数pid的4种不同情况: * pid > 0 发送给pid进程 * pid == 0 发送给统一进程组的所有进程 * pid < 0 发送给进程组ID等于pid绝对值的所有进程 * pid == -1 发送给有权限发送的所有进程 */

    

    

alarm和pause函数

    alarm函数用来设置一个定时器,定时器超时后会发生一个SIGALRM信号,对该信号的默认 处置方式是闭幕进程。

    #include <signal.h> unsigned int alarm(unsigned int secondes); /* 返回值:0或者以前设置的闹钟时光的余留秒树 */

    

    如果在调用alarm时,以前也设置过一个闹钟且该闹钟还没有超时,那么该闹钟的余留时光 将作为这次alarm调用的返回值,以前注销的闹钟也会被新的闹钟所代替。

    pause函数使调用进程挂起,直到接收到一个信号。

    #include <unistd.h> int pause(void); /* 返回-1,并将errno设置为EINTR */

    

    

信号集

    数据类型sigset_t表示一个信集,有上面几个处置信号集的函数:

    #include<signal.h> int sigemptyset(sigset_t *set); /* 清空信号集 */ int sigfillset(sigset_t *set); /* 填满信号集,使其包括每一个可能的信号 */ int sigaddset(sigset_t *set, int signo); int sigdelset(sigset_t *set, int signo); int sigismember(const sigset_t *set, int signo);

    

    

sigprocmask和sigpending函数

    进程有一个信号屏蔽字,它划定了当前阻塞哪些进程。调用sigprocmask函数可以检查和 更改进程的信号屏蔽字:

    #include <signal.h> int sigprocmask(int how, const sigset_t *restrict set, sitset_t *restrict oset); /* * 成功返回0, 出错返回-1 * 如果 oset 长短空指针,则进程之前的信号屏蔽字由 oset 返回 * 若干 set 长短空指针,则根据不同的 how 对进程的信号屏蔽字做设置: * how = SIG_BLOCK,之前的屏蔽字 + set代表的屏蔽字 * how = SIG_UNBLOCK,之前的屏蔽字 - set代表的屏蔽字 * how = SIG_SETMASK,用 set代表的屏蔽字 代替之前的屏蔽字 */

    

    sigpending函数返回进程对当前进程处于pending状态的信号集。

    #include <signal.h> int sigpending(sigset_t *set);

    

    

sigaction函数

    sigaction函数检查或修改与指定信号相干的处置动作。它比signal函数直观且更轻易 懂得,事实上代替了老式的signal函数:

    #include <signal.h> int sigaction(int signo, const struct sigaction *restrict act, struct sigaction *restrict oact); /* 成功返回0,错误返回-1 */

    

    该函数应用一个名为sigaction的结构:

    struct siaction { void (*sig_handler)(int); /* signal handler OR SIG_IGN, SIG_DFL */ sigset_t sa_mask; /* 要阻塞的信号 */ int sa_flags; void (*sa_sigaction)(int, siginfo_t *, void *); /* 替换的handler */ };

    

    参数说明:

    

  • sig_handler表示handler函数的地址。
  • sa_mask说明了一个信号集,当调用handler时,会把这个信号集里面的信号都加到屏 蔽字中,让它们都阻塞。
  • sa_flags说明了一系列的参数。
  • sa_sigaction是一个替换的handler,当sa_flags中涌现了SA_SIGINFO时,就会调用 这个函数,该函数应用了一个siginfo_t类型的参数,它包括了一些这次信号相干的信息 。
    每日一道理
今天阳光很好,坐在窗前,看窗外如此晴朗的天感觉特别舒心,雨过天晴后的世界总给人一种明媚,仿佛阳光照耀在“心田”上空,让前些天被风雨践踏的花朵从新得到爱的关怀,重现生命的活力!

    

sigsuspend函数

    sigsuspend函数设置进程的信号屏蔽字,然后在捕捉到一个信号或者发生了一个会终止 该进程的信号之前,该进程被挂起,而当接收到了信号且从信号处置程序返回时,进程的 信号屏蔽字又会恢复为调用sigsuspend函数之前的值。

    #include <signal.h> int sigsuspend(const sigset_t *sigmask); /* 返回值:-1,并将errno设置为EINTR */

    

    

abort函数

    abort函数将SIGABRT信号发送给调用进程。

    #include <stdlib.h> void abort(void);

    

    

sleep函数

    #include <unistd.h> unsigned int sleep(unsigned int seconds); /* 返回值:0或者未休眠够的秒数 */

    

    sleep函数使调用进程被挂起,直到以下条件之一被满足:

    

  • 已经过了seconds指定的时光。 * 调用进程捕捉到一个信号并从信号处置程序返回。

    

信号在内核中的实现

    对于信号,内核必须:

    * 记着每个进程阻塞哪些信号。 * 当从内核态切换到用户态时,对任何一个进程都要检查是否有一个信号已经到达。这几 乎在每个定时中断时都发生。 * 肯定是否可以疏忽信号。 * 处置信号。

    

与信号有关的数据结构

    对每个进程,内核必须跟踪什么信号当前正在挂起或被屏蔽,以及每个线程组是如何处置 所有信号的。这些信息都记录在进程描述符task_struct中,如下图所示:

    进程和函数

    在进程描述符中,几个重要的字段是:

    

  • struct signal_struct *signal 是指向进程的信号描述符的指针,信号描述符用来 跟踪同享的挂起信号。当应用设置了CLONE_THREAD位的clone()系统调用来fork新进程时 ,新的进程同享信号描述符。在signal_struct结构中有一个字段是struct sigpending share_pending,该字段中寄存同享挂起信号的数据结构。
  • struct sighand_struct *sighand 是进程的信号处置程序描述符的指针。
  • sigset_t blocked 表示被阻塞的信号集。
  • struct sigpending pending 寄存私有挂起信号的数据结构。

    应用kill等系统调用,信号可以发送给整个线程组,也可以发送个特定的进程。所以,为 了跟踪当前pending的信号是什么,内核让每个进程关联了两个pending信号队列:

    

  • 同享pending信号队列。位于task_structsignalshare_pending字段,它寄存 整个线程组的pending信号。
  • 私有pending信号队列。位于task_tructpending字段。

    这两个字段都是sigpending结构,该结构定义为:

    struct sigpending { struct list_head list; sigset_t signal; };

    

    其中,list是一个sigqueue结构的双向链表头。该链表表示了具体的信号的信息。

    

发生信号

    很多内核函数都能发生信号,它们所做的任务事实上就是更新一个或多个的进程描述符, 然后唤醒一些进程,促使它们接收信号。

    像一个进程发送信号的大致过程如下:

    1. 检查进程是否疏忽信号。

    

  1. 检查私有pending信号队列上是否已经有另外一个相同的信号,有则什么都不做。
  2. 调用send_signal把信号添加到进程的pending信号会合。
  3. 通知进程有新的pending的信号,唤醒进程,如果进程已经在运行,则强制其从新调度 。因为在从调度函数返回时,每个进程都检查是否存在挂起的信号。

    

传递和执行信号

    内核从内核空间恢复到用户空间执行前,会检查是否存在pending的信号。也就是说当内核 处置完一个中断或异常时就会做这样的检查。如果有pending的信号,则调用do_signal 函数处置。该函数首先斟酌私有pending信号队列中的所有信号,从最低编号开始,然后再 斟酌同享队列中的信号。

    如果信号有一个专门的处置程序,do_signal调用handler_signal()调用信号处置程序 执行。

    须要注意的是,信号处置程序是定义在用户态中的函数,而信号处置是在返回用户态前, 也就是在内核态中。另外,由于信号处置程序可以调用系统调用,这种情况下,执行了系 统调用的服务例程之后,控制权应当返回到信号处置程序而不是返回到被中断程序的畸形 代码流。

    于是,do_signal函数中会调用setup_frame()对用户态堆栈停止设置,使(1)恢复用 户态时首先执行的是信号处置程序,(2)信号处置程序结束时,通过sigreturn系统调 用把用户态堆栈硬件上下文拷贝到内核态堆栈,同时恢复用户态堆栈原始的状态。这时再 从内核态恢复到用户态时,就能执行“畸形”的程序流程。

    捕获一个信号的示意图如下:

    进程和函数

    JH, 2013-05-13

    


    参考资料:

    

文章结束给大家分享下程序员的一些笑话语录: 手机终究会变成PC,所以ip会比wm更加畅销,但是有一天手机强大到一定程度了就会发现只有wm的支持才能完美享受。就好比树和草,草长得再高也是草,时间到了条件成熟了树就会窜天高了。www.ishuo.cn

原文地址:https://www.cnblogs.com/xinyuyuanm/p/3076928.html