孤儿进程和僵尸进程

如果父进程先退出,子进程还没退出那么子进程的父进程将变为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()只能向一个进程发送信号,而不能发送信号给一个进程组。

linux第2天 信号 wait的更多相关文章

  1. Linux环境进程间通信(二): 信号(上)

    linux下进程间通信的几种主要手段: 管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允 ...

  2. 自学Linux Shell15.1-处理信号

    点击返回 自学Linux命令行与Shell脚本之路 15.1-处理信号 Linux使用信号与系统上运行的进程进行通信.可以使用这些信号控制Shell脚本的运行,只需要让shell脚本在接收到来自Lin ...

  3. linux系统编程之信号(一):中断与信号

    一,什么是中断? 1.中断的基本概念 中断是指计算机在执行期间,系统内发生任何非寻常的或非预期的急需处理事件,使得CPU暂时中断当前正在执行的程序而转去执行相应的事件处理程序,待处理完毕后又返回原来被 ...

  4. Linux内核中的信号机制--一个简单的例子【转】

    本文转载自:http://blog.csdn.net/ce123_zhouwei/article/details/8562958 Linux内核中的信号机制--一个简单的例子 Author:ce123 ...

  5. linux下的常见信号总结

    在linux下有很多信号,按可靠性分为可靠信号和非可靠信号,按时间分为实时信号和非实时信号,linux进程也有三种方式来处理收到的信号: (1)忽略信号,即对信号不做任何处理,其中,有两个信号不能忽略 ...

  6. Linux进程间通信--进程,信号,管道,消息队列,信号量,共享内存

    Linux进程间通信--进程,信号,管道,消息队列,信号量,共享内存 参考:<linux编程从入门到精通>,<Linux C程序设计大全>,<unix环境高级编程> ...

  7. [转]Linux下的常见信号总结

    转自 https://www.cnblogs.com/gaorong/p/6430905.html 在linux下有很多信号,按可靠性分为可靠信号和非可靠信号,按时间分为实时信号和非实时信号,linu ...

  8. linux中的常用信号

    linux中的常用信号,见如下列表: 信号名 值 标注 解释 ------------------------------------------------------------------ HU ...

  9. Linux signal 常见的信号含义表

    http://blog.csdn.net/xgjianstart/article/details/4544418 通过命令 kill -l  可查看全部信号 SIGHUP 终止进程 终端线路挂断 本信 ...

  10. Linux学习 :按键信号 之 异步通知

    一.异步通知概念: 异步通知是指:一旦设备就绪,则主动通知应用程序,应用程序根本就不需要查询设备状态,类似于中断的概念,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的.信号是异步的,一个进 ...

随机推荐

  1. 【Demo】微信上墙

    先看看微信墙效果图 使用简单说明 关于微信公众号 回复 "上墙",点击授权文章进行授权 回复"#上墙内容" 即可发表上墙消息了 查看微信墙列表,点击这里 原文地 ...

  2. ubuntu下安装与卸载qt的方法

    http://blog.csdn.net/huyisu/article/details/24014407 ubuntu下安装与卸载qt的方法 分类: linux 2014-04-18 14:20 18 ...

  3. pro8

    1.本次课学到的知识点 函数程序设计 结构化程序设计思想 程序解析 局部变量和全局变量 2.实验过程中遇到的问题及解决方法 实验过程中会遇到自定义函数的逻辑错误 与缺少定义变量 从主函数开始理清函数关 ...

  4. Dom方式解析XML

    public class TestXML { public static void main(String[] args) throws SAXException, IOException { //D ...

  5. IAdaptable和IAdaptableFactory(转)

    先记在这里,回头研究下. 原文:http://blog.csdn.net/mini_snow/article/details/3877379 1. 简介和简单的实现 IAdapteable实际上在Ec ...

  6. Spring @ResponseBody只能返回String类型数据解决办法

    今天自己搭Spring MVC框架玩,使用AJAX调用Spring controller 并返回map对象,突然发现,哎,怎么@Response中只能返回String, 我用的Spring 3的版本也 ...

  7. TCP协议中的三次握手和四次挥手

    转自: http://blog.csdn.net/whuslei/article/details/6667471/ 建立TCP需要三次握手才能建立,而断开连接则需要四次握手.整个过程如下图所示:

  8. 【C++】C++求vector中的最大最小值

    利用algorithm库里的max_element和min_element可以得到vector的最大最小值,配合distance函数可以得到最大值的位置 #include<vector> ...

  9. 安装sqlserver2008r2 服务器配置,服务帐户配置出错,提示Sql server服务指定的凭据无效

    win+X 点击运行 重置帐户密码使得sql server2008 的服务帐户名,密码与系统设置的Administrator名与密码一致,则可. 上图中有一项reporting的名字不同,该名字为自动 ...

  10. extjs form.load()加载服务端数据

    formPanel.getForm().load({ url: 'getApproveRefundInf?refundIdDetail=${refundIdDetail}', waitMsg: '请稍 ...