当前位置: 首页 > news >正文

宁波 手机网站建设汕头网站优化找谁

宁波 手机网站建设,汕头网站优化找谁,网站自适应手机代码,网上软文发稿平台上篇#xff1a;【Linux】-- 进程信号#xff08;认识、应用#xff09;_川入的博客-CSDN博客 目录 信号其他相关常见概念 pending handler block 信号处理的过程 sigset_t sigset_t使用 系统接口 sigpending sigprocmask 捕捉方法 sigaction struct sigactio …上篇【Linux】-- 进程信号认识、应用_川入的博客-CSDN博客 目录 信号其他相关常见概念 pending handler block 信号处理的过程 sigset_t sigset_t使用 系统接口 sigpending sigprocmask 捕捉方法 sigaction struct sigactio sa_mask 补充 可重入函数 volatile SIGCHLD信号 信号其他相关常见概念 实际执行信号的处理动作称为信号递达(Delivery)。 信号递达可能是默认、可能是忽略、可能是自定义捕捉。信号从产生到递达之间的状态,称为信号未决(Pending)。 信号产生进程不是立即处理这个信号不代表其不会处理。意味着未来会处理于是从收到信号到未来准备处理时信号存在但是没有被处理 —— 信号被临时保存PCB的位图中此时就被称为信号未决 —— Pending位图。融汇贯通的理解         所以前面所提的临时存储概念是不准确的应该称作为信号未决(Pending)。 进程可以选择阻塞 (Block)某个信号。 进程是可以将处于未决的信号就是不递达其屏蔽某些信号 —— 阻塞 (Block)。 忽略和阻塞的区别         忽略已经递达了已经处理该信号了只不过处理动作是忽略。         阻塞根本就不会进行抵达不进入信号处理流程。阻塞即永远不递达 被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作. 注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。         为了支持递达、未决、阻塞三个概念其在内核中有对应的表现 —— 内核当中有对应的三张表。 pending         pending就是之前所提的位图操作系统就是修改pending位图中指定的位置来完成信号的发送过程。 handler         当收到了一个信号操作系统会在pending中修改对应的位图。处理信号的时候就会根据为1的信号拿着信号去handler数值中索引。对应调用handler中的函数指针的方法去完成信号捕捉就可以了。         所以signal是两个参数的原因也在此是根据第一个元素signum找到handler数组对应的位置将第二个参数handler作为数据存入。 但是需要注意进程对于信号的处理方式有三种 默认进程自带的程序员写好的逻辑忽略也是信号的一种处理方式自定义动作捕捉信号        所以此处的handler中并不只是signal函数这么简单。 路径/usr/include/bits/signum.h block         block位图结构和pending一摸一样。位图中的内容代表的含义是对应的信号是否被阻塞。 信号处理的过程 信号处理的大致流程图可以画为。 1. 向操作系统向pending中发送信号。 2. 处理信号遍历pending找到为1的信号。 3. 找到之后去对应的block中查看是否为1。block为1该信号永远不递达。block为0该信号合适的时候递达。 4. block为0且需要抵达根据handler数组中的数据处理信号。 sigset_t         为了支持我们更好的编程信号在内核当中是一个位图不可能让我们直接操作其也不可能让我们操作。操作系统为我们提供了一个类型sigset_t。         因为操作系统提供的类型需要与操作系统提供的.h文件相对应也就是与系统调用接口相对应。因为有的接口不允许用户传语言层的参数需要传一个结构体、一个位图等。于是操作系统必须提供对应的类型。 融汇贯通的理解         其实语言级的.h、.hpp也一样因为只要涉及硬件级别的操作就必须通过操作系统那么就需要使用操作系统提供的.h以及操作系统提供的类型 —— 语言级的.h、.hpp一定对操作系统提供的.h与类型有着包含。最典型的就是文件操作。 # define _SIGSET_NWORDS (1024 / (8 * sizeof (unsigned long int))) typedef struct{unsigned long int __val[_SIGSET_NWORDS];} __sigset_t;#endif sigset_t是位图结构操作系统提供的类型。 每个信号只有一个bit的未决标志非0即1不记录该信号产生了多少次阻塞标志也是这样表示的。因此未决和阻塞标志可以用相同的数据类型 sigset_t 来表示sigset_t 称为信号集这个类型可以表示每个信号的 有效 或  无效 状态在阻塞信号集中 有效 和 无效 的含义是该信号是否被阻塞而在未决信号集中 有效 和 无效 的含义是该信号是否处于未决状态。阻塞信号集也叫做当前进程的信号屏蔽字(Signal Mask)这里的 屏蔽 应该理解为阻塞而不是忽略。 sigset_t使用 1. sigset_t —— 不允许用户自己进行位操作 —— 操作系统给我们提供了对应的操作位图的方法。 #include signal.h系统接口意义返回值 int sigemptyset(sigset_t *set); 初始化set所指向的信号集所有信号位清0表示该信号集不包含任何有效信号。 成功时返回0 错误时返回-1 int sigfillset(sigset_t *set); 初始化set所指向的信号集使其中所有信号的对应bit置位表示该信号集的有效信号包括系统支持的所有信号。 int sigaddset (sigset_t *set, int signo); 向set所指向的信号位添加某种有效信号。 int sigdelset(sigset_t *set, int signo); 向set所指向的信号位删除某种有效信号。 int sigismemberconst sigset_t *set, int signo); 判断在set所指向的信号集中是否包含某种信号. 包含返回1 不包含返回0 错误返回-1 2.  sigset_t —— user是可以直接使用该类型 —— 和内置类型 自定义类型没有任何差别。 3.  sigset_t —— 一定需要对应的系统接口来完成对应的功能其中系统接口需要的参数可能就包含了sigset_t定义的变量或者对象。 系统接口 sigpending #include signal.h - int sigpending(sigset_t *set); 获取pending位图。 - 参数         set类型为sigset_t的位图。 返回值如果发生错误将设置errno以指示原因 成功时返回0。错误时返回-1。为什么pending只有获取为什么没有设置因为其实前面讲的信号产生就是在设置。 sigprocmask #include signal.h - int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); - 参数         how参数更改信号屏蔽字。         set类型为sigset_t的位图。         oldset将原来的信号屏蔽字备份到oldset里。 返回值如果发生错误将设置errno以指示原因 成功时返回0。错误时返回-1。how参数宏下列宏没有任何交集不自持按位|、按位。 选项含义SIG_BLOCKset包含了我们希望从当前信号屏蔽字中解除阻塞的信号相当于maskmask|~setSIG_UNBLOCKset包含了我们希望从当前信号屏蔽字中解除阻塞的信号相当于maskmask|~setSIG_SETMASK    设置当前信号屏蔽字为set所指向的值相当于maskset#问如果我们对所有的信号都进行了自定义捕捉是不是就写了一个不会被异常或者用户杀掉的进程 不是也不可能操作系统的设计者也考虑到了。 #include iostream #include signal.h #include unistd.h #include cassertstatic void handler(int signum) {std::cout 捕捉 信号 signum std::endl;// 不要终止进程exit }int main() {// 将block全部设置for(int sig 1; sig 31; sig){signal(sig, handler);}return 0; } 9号信号与19号信号属于管理员信号我们是无法设定自定义捕捉动作的。为的就是防止我们将所有的31个信号全部捕捉。 9号信号 19号信号 #问如果我们将对所有的信号都进行了block是不是就写了一个不会被异常或者用户杀掉的进程 不是也不可能操作系统的设计者也考虑到了。 #include iostream #include signal.h #include unistd.h #include cassert// 打印pending static void showPending(sigset_t pending) {for (int sig 1; sig 31; sig){if (sigismember(pending, sig))std::cout 1;elsestd::cout 0;}std::cout std::endl; }// 设置block static void blockSig(int sig) {sigset_t bset;sigemptyset(bset);sigaddset(bset, sig);int n sigprocmask(SIG_BLOCK, bset, nullptr);assert(n 0);(void)n; }int main() {// 将block全部设置for(int sig 1; sig 31; sig){blockSig(sig);}// 循环打印pendingsigset_t pending;while(true){sigpending(pending);showPending(pending);sleep(1);}return 0; } 9号信号与19号信号属于管理员信号我们是无法设定block。为的就是防止我们将所有的31个信号全部block。 19号信号 Note:         9号信号与19号信号永远不会被阻塞、永远不会被捕捉。 捕捉方法 1. 信号在合适的时候处理什么时候 合适的时候内核态返回到用户态的时候处理。 #问什么叫做用户态什么叫做内核态 我们调用某些系统调用或者是时间片到了、或者是我们主导的调用了陷入内核的汇编指令。以此进入操作系统在操作系统内执行操作系统的代码。其中执行操作系统底层的代码的状态就称作为内核态 —— 同理执行用户层代码时称作为用户态。  用户态是一个受管控的状态。内核态是一个操作系统执行自己代码的一个状态具备非常高的优先级。#问从内核态返回到用户态即必须要进入过内核态。为什么要进入内核态如何进入的内核态 在操作系统或者是在硬件CPU上执行代码的时候执行的一大批代码都在内存之中保存着二进制。CPU执行的时候区分是用户的代码还是内核的代码即执行用户的代码就是用户态执行内核的代码就是内核态。 内核范畴 相关数据在操作系统内部普通用户没有权利去进行检测。内核状态系统调用接口。 用户状态hello world。 我们大部分执行的是我们的代码所以我们是用户态。 进入内核态最典型的方式 进行系统调用。而有时候缺陷、陷阱、异常等也可能进入内核态。#问我们怎么进入内核态和用户态我们不用担心 在汇编语言中有一个中断编号80有一个汇编指令int。以int 80可以让我们陷入内核即代码的执行权限由我下达给操作系统让操作系统去执行。 int 80内置在系统调用函数中我们不用管 用户态是一个受管控的状态即不管是哪一个用户都叫做普通用户。其启动的进程或者是任务都是需要以用户态来运行的。受管控受访问权限的约束、资源限制等如果基本不受任何资源的约束也就是不受权限的管控。 页表分为 用户级页表用于映射用户所写的代码和数据所对应的物理内存位于用户空间0 ~ 3G内核级页表用于映射操作系统的代码和数据所对应的物理内存位于内核空间3 ~ 4G 内核级页表不同于用户级页表。对于用户级页表而言因为不同进程的代码和数据是不相同的所以需要每一个进程有属于自己的用户级页表进程的独立性的体现。对于内核级页表而言因为所有的进程都是在一个操作系统上跑的只有一份操作系统所以内核级页表只有一份。 当我们的代码中有类似open的系统调用时根本不用担心因为操作系统也在进程地址空间里。所以无非就是跳转到open对应的内核地址空间里。 即操作系统的代码可以找到因为整个操作系统被映射进了所有进程的3~4G中。所以所有进程想调系统调用只是在自己的地址空间上通过函数调用跳转到系统里就可以了。 融会贯通的理解 #问进程切换的过程是什么         操作系统内有一个switch process对应的函数然后当我们对应的进程时间片到了操作系统底层硬件给我们发时钟中断。操作系统就直接在CPU找当执行的进程然后通过其的地址空间找到对应的进程切换的函数因为在当前进程中切换所以可以将CPU内所有的临时数据压到PCB当中即切换成功。下面一个进程运用其进程地址空间中3~4G与内核级页表找到恢复上下文的代码和数据。         时钟中断执行的频率很高100次/秒时钟中断的主要工作是处理和时间有关的所有信息、决定是否执行调度程序以及处理下半部分。 和时间有关的所有信息包括系统时间、进程的时间片、延时、使用CPU的时间、各种定时器进程更新后的时间片为进程调度提供依据然后在时钟中断返回时决定是否要执行调度程序。 #问我们凭什么有权利执行操作系统的代码 凭的是我们是处于内核态还是用户态。因为CPU里面的寄存器一般分为两类一套可见程序员可用一套不可见权限、控制等CPU自己用。其中有一个寄存器叫做CR3寄存器 —— 表示当前CPU的执行权限。 只有内核态才可以访问操作系统里结合前面所提其实执行open的时候就会执行其本身自带的指令int 80该指令第一件事就会将CR3寄存器由用户态变为内核态于是后面权限检查发现是内核态于是就可以访问操作系统使用内核级页表于是跳转到操作系统执行open的代码。 #问为什么从用户态 - 内核态 因为有时候有一些功能大部分情况下无法在用户态无法去执行的。因为操作系统是软硬件资源的管理者换句话说就是任何普通用户无法不能、不可以绕过操作系统去访问对应的软硬件资源。 用户需要通过访问软硬件资源达到自身的目的。用户需要操作系统不允许所以便有了先变成内核然后用户通过其访问 —— 系统调用接口 #问怎么从用户态 - 内核态 目前最常见就是系统调用接口。通过特定的接口陷入内核。 #问为什么从内核态 - 用户态 当前用户的代码还没有执行完。当前用户层还有若干个进程没有被调度完。计算机当中操作系统是为了给用户提供服务的所以执行用户的代码为主执行操作系统的代码为辅以此协同完成。所以是必定要从内核态 - 用户态不反回用户态就无法给用户提供完整的服务 —— 可以说进入内核态是一种临时的状态。 #问CPU如何知晓的当前其执行的代码是用户的还是内核的 硬件上 CPU寄存器分为两类一套可见一套不可见。执行内核代码中其第一件事就是自行自带的指令int 80该指令第一件事就会将CR3寄存器由用户态变为内核态于是后面权限检查发现是内核态。 软件上 除用户级页表将虚拟的用户地址空间进行物理内存的实质化同时每一个进程由独立的用户级页表用以保证进程的独立性之外还有内核级页表用于将进程地址空间中的内核地址空间映射到物理内存中存储的操作系统开机启动电脑无非就是将操作系统加载到物理内存用以运行。以此达到进程对于操作系统的代码和数据的访问由于操作系统只有一份只需要一份Windows、Linux。所以所有进程只需要看见同一份资源即同一份内核级页表即可。 融会贯通的理解         所有进程都在一个操作系统下跑 —— 必须看见同一份操作系统 —— 同一份内核级页表。         CPU执行代码的时候都是进程的代码进程而如何切换软硬件体系都可以找到操作系统并且所有的操作系统执行都是在地址空间中 —— 执行系统调用 —— 在当前进程地址空间内执行跳转 —— 与动态库类似区别更改实行级别、状态、权限等。 2. 信号处理的整个流程 内核态处理默认、忽略是水到渠成的事情。 融会贯通的理解         进程由内核态 - 用户态的时遍历检查pending中处于1的信号然后其block中为0即进行handler中的递达。 忽略即pengding中的1置0而后直接返回上层的用户代码处继续执行。默认大部分是终止直接将当前进程进入终止逻辑不调度该进程了将进程对应的PCB、地址空间、页表释放无需返回用户态继续运行。 进程终止_exit()就是直接终止说白了就是这种终止无需返回到用户态。进程终止exit()上层用户层由刷新行为特殊处理操作系统需要返回到用户态将缓冲区数据进行刷新然后再进行终止。 终止了如何还能执行用户层代码—— 操作系统提供有一些相关接口。进程终止之前帮我们返回到用户态执行特定的方法 进程暂停当前进程在内核态将进程PCB的状态由运行态改为T状态然后无需返回用户态直接执行调度算法将进程放入等待队列里。然后从新选择进程调度。        默认和忽略都是处于内核态。 捕捉动作是最难的下图以捕捉动作为例 #问执行我们所写的捕捉动作时处于什么状态 此时处于信号检测信号处理 - 所以是处于内核态的。 #问当前的状态能不能执行user handler方法的 能执行但是操作系统不想执行。因为不要认为操作系统内核态不能访问用户层的代码、数据。只要操作系统愿意想访问就访问。 融汇贯通的理解 以文件操作中为例         用户通过read读取文件中的内容而文件中的内容就是属于用户层的数据但是read是系统接口是属于内核层。         所以我们获取文件内容的方式就是通过我们自己写的用户级缓冲区buffer获取数据而缓冲区的数据就是操作系统拷贝进的。 文件数据读取操作系统愿意做因为数据只是拷贝没有什么问题但是如果以操作系统内核态的身份如果user handler有非法的操作那就完了 — 操作系统是不会相信普通用户任何人的 — 不能用内核态执行用户的代码。 进行user handler方法时从内核态切回用户态 —— 以用户态的身份执行我们所写的方法如此所有行为我们自己负责。 在完成之后在用户的层面上没有能力跳转回执行内核层的代码然后继续向后执行。 因为递达完成需要将pending置0此操作需要以内核态身份执行。并且当时在哪被中断进入内核这个位置只有操作系统知道。 图像简易化 1 - 2 - 3 - 4 - 51次的信号检测4次的状态的切换sigaction #include signal.h int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); 检查并更改信号动作。 参数         signum信号的编号         act输入型参数 一个结构体至少包含对于信号的处理 —— 回调函数        oldact输出型参数 一个结构体曾经对于这个信号的老的处理方法。返回值 成功时返回0出现错误时返回-1并设置errno错误码以指示错误。struct sigactio 结构体操作系统提供的数据类型。结构体名称能与函数相同 —— 不建议 struct sigaction {        void     (*sa_handler)(int);                                  /*信号捕捉对应的回调函数*/         void     (*sa_sigaction)(int, siginfo_t *, void *);   /*实时信号使用*/        sigset_t   sa_mask;         int        sa_flags;                                                /*实时信号使用*/         void     (*sa_restorer)(void);                               /*实时信号使用*/ }; 该函数不仅可以捕捉普通信号也可以捕捉实时信号但是不考虑实时信号即其中很多字段不管。 #include iostream #include signal.h #include unistd.hvoid handler(int signum) {std::cout 获取了一个信号 signum std::endl; }int main() {std::cout getpid: getpid() std::endl; // 内核数据类型用户栈定义的struct sigaction act, oact;act.sa_flags 0;sigemptyset(act.sa_mask); // 清空原理后面讲解act.sa_handler handler;// 设置进当前调用进程的pcb中sigaction(2, act, oact);std::cout default action : oact.sa_handler std::endl;while(true) sleep(1);return 0; } Linux的设计方案在任意时刻只能处理一层信号 —— 不允许信号正在被处理时又来信号需要处理 —— 信号什么时候来挡不住但是可以挡得住信号什么时候被处理。 sa_mask 当某个信号的处理函数被调用时内核自动将当前信号加入进程的信号屏蔽字当信号处理函数返回时自动恢复原来的信号屏蔽字这样就保证了在处理某个信号时,如果这种信号再次产生那么它会被阻塞到当前处理结束为止。 如果在调用信号处理函数时除了当前信号被自动屏蔽之外还希望自动屏蔽另外一些信号则用sa_mask字段说明这些需要额外屏蔽的信号当信号处理函数返回时自动恢复原来的信号屏蔽字。 #include iostream #include signal.h #include unistd.hvoid handler(int signum) {std::cout 获取了一个信号 signum std::endl;sleep(10); }int main() {std::cout getpid: getpid() std::endl; // 内核数据类型用户栈定义的struct sigaction act, oact;act.sa_flags 0;sigemptyset(act.sa_mask); // 保证初始状态下信号屏蔽字全为0act.sa_handler handler;// 设置进当前调用进程的pcb中sigaction(2, act, oact);std::cout default action : oact.sa_handler std::endl;while(true) sleep(1);return 0; } 实验在信号处理期间会屏蔽该信号。 #include iostream #include signal.h #include unistd.hvoid showPending(sigset_t* pending) {for(int sig 1; sig 31; sig){if(sigismember(pending, sig)) std::cout 1;else std::cout 0;}std::cout std::endl; }void handler(int signum) {std::cout 获取了一个信号 signum std::endl;sigset_t pending;int c 7;while(true){sigpending(pending);showPending(pending);c--;if(!c) break;sleep(1);} }int main() {std::cout getpid: getpid() std::endl; // 内核数据类型用户栈定义的struct sigaction act, oact;act.sa_flags 0;sigemptyset(act.sa_mask); // 保证初始状态下信号屏蔽字全为0act.sa_handler handler;// 设置进当前调用进程的pcb中sigaction(2, act, oact);std::cout default action : oact.sa_handler std::endl;while(true) sleep(1);return 0; } 这也就是为什么会有block或者信号屏蔽字这样的字段。也就是为了支持操作系统内处理普通信号防止其进行递归式的处理。让其在自己的处理周期内只会调用一层不出现太多的调用层次。 在处理2号信号的期间捎带的屏蔽一下信号3、4、5、6 #include iostream #include signal.h #include unistd.hvoid showPending(sigset_t* pending) {for(int sig 1; sig 31; sig){if(sigismember(pending, sig)) std::cout 1;else std::cout 0;}std::cout std::endl; }void handler(int signum) {std::cout 获取了一个信号 signum std::endl;sigset_t pending;int c 20;while(true){sigpending(pending);showPending(pending);c--;if(!c) break;sleep(1);} }int main() {std::cout getpid: getpid() std::endl; // 内核数据类型用户栈定义的struct sigaction act, oact;act.sa_flags 0;sigemptyset(act.sa_mask); // 保证初始状态下信号屏蔽字全为0act.sa_handler handler;// 在处理2号信号的期间捎带的屏蔽一下信号3、4、5、6sigaddset(act.sa_mask, 3);sigaddset(act.sa_mask, 4);sigaddset(act.sa_mask, 5);sigaddset(act.sa_mask, 6);// 设置进当前调用进程的pcb中sigaction(2, act, oact);std::cout default action : oact.sa_handler std::endl;while(true) sleep(1);return 0; } 因为我们只是将2号信号设置为自定义捕捉其他信号是默认。所以在执行完2号信号的自定义捕捉后处于被block状态的信号才会被递达。此处最后递达的是4号信号 Note         信号捕捉并没有创建新的进程或者线程。信号的处理整个流程都是单进程的就是这一个进程处理信号时处于此进程的上下文中处理。 补充 可重入函数 main函数调用insert函数向一个链表head中插入节点node1,插入操作分为两步,刚做完第一步的 时候,因为硬件中断使进程切换到内核,再次回用户态之前检查到有信号待处理,于是切换到sighandler函数,sighandler也调用insert函数向同一个链表head中插入节点node2,插入操作的两步都做完之后从sighandler返回内核态,再次回到用户态就从main函数调用的insert函数中继续往下执行,先前做第一步之后被打断,现在继续做完第二步。结果是main函数和sighandler先后向链表中插入两个节点而最后只有一个节点真正插入链表中了。于是便出现了经典的内存泄漏问题。 排查代码的时候可以发现我们的代码写的没有任何问题。对应的main函数、单链表头插insert、信号捕捉sighandler、函数调用都没有问题 —— 这个问题的产生严格来说并不是代码问题而是因为操作系统调度导致的进程时序的变化 —— 时序问题。 此问题存在且非常不容易排查。 像上例这样,insert函数被不同的控制流程调用,有可能在第一次调用还没返回时就再次进入该函数,这称为重入。insert函数访问一个全局链表,有可能因为重入而造成错乱,像这样的函数称为不可重入函数反之 如果一个函数只访问自己的局部变量或参数,则称为可重入(Reentrant) 函数。 可重入函数 VS 不可重入函数         是函数的一种特征目前我们用的90%函数都是不可重入的。 不可重入函数好编写。可重入函数不好编写书写成本高。如果一个函数符合以下条件之一则是不可重入的:   调用了new、malloc或free因为new、malloc也是用全局链表来管理堆的。 99%的STL容器都是不可重入的。函数里面带static的。调用了标准I/O库函数。标准I/O库的很多实现都以不可重入的方式使用全局数据结构。 可重入函数需要保证函数其是独立的没有访问任何的全局数据。 volatile 编译做优化的时候自作聪明它一看flag是一个全局的数据发现在main函数里没有任何一个语句是改flag的。其认为每一次检测flag都需要访问内存并将数据拷贝进CPU的寄存器中于是自作聪明将第一次的flag的值一直放在edx中后面的检测就不去内存拿了继续看edx中的数据就是0于是一直检测不过。 所以为了解决这个问题一些可能被优化的字段我们显性的告诉编译器不要这么优化。 SIGCHLD信号 只有Linux采用了这样的方案 在进程等待中用 wait 和 waitpid 函数清理僵尸进程父进程可以阻塞等待子进程结束也可以非阻塞地查询是否有子进程结束等待清理也就是轮询的方式。采用第一种方式父进程阻塞了就不能处理自己的工作了采用第二种方式父进程在处理自己的工作的同时还要记得时不时地轮询一下程序实现复杂。其实子进程在终止时会给父进程发SIGCHLD信号该信号的默认处理动作是忽略父进程可以自定义SIGCHLD信号的处理函数这样父进程只需专心处理自己的工作不必关心子进程了子进程终止时会通知父进程父进程在信号处理函数中调用wait清理子进程即可。本质上也就是子进程通过操作系统来给父进程写入信号逻辑同文上。验证子进程在终止时会给父进程发SIGCHLD信号 #include cstdio #include cstdlib #include signal.h #include unistd.h #include sys/wait.h #include sys/types.hvoid handler(int sig) {printf(子进程退出:%d\n, sig); }// 证明子进程退出会向父进程发送信号 int main() {signal(SIGCHLD, handler);pid_t cid;if ((cid fork()) 0){ // childsleep(1);exit(0);}while(true) sleep(1);return 0; } 父进程在信号处理函数中调用 wait / waitpid 清理子进程Note         我们需要注意如果我们有10个子进程而如果其中5个子进程在一个时刻退出。由于普通信号只会有与没有没有个数的表达而对于第6个子进程也是需要进行判断是否退出的我们知道5个子进程退出是我们站在上帝的视角。此时我们使用常规的 wait / waitpid 去使用信号处理就会阻塞在那里。 解决方法 使用全局变量记录所有子进程的pid用以遍历的非阻塞等待。使用waitpid第一个参数为-1等待任意一个子进程并非阻塞等待。#include stdio.h #include stdlib.h #include signal.h #include sys/wait.h #include sys/types.hvoid handler(int sig) {pid_t id;while ((id waitpid(-1, NULL, WNOHANG)) 0){printf(wait child success: %d\n, id);}printf(child is quit! %d\n, getpid()); }int main() {signal(SIGCHLD, handler);pid_t cid;if ((cid fork()) 0){// childprintf(child : %d\n, getpid());sleep(3);exit(1);}//fatherwhile (1){printf(father proc is doing some thing!\n);sleep(1);}return 0; } 父进程不关心子进程的任何推出信息#include iostream #include unistd.h #include signal.h// 如果我们不想等待子进程并且我们还想让子进程退出之后自动释放僵尸子进程 int main() {// OS 默认就是忽略的signal(SIGCHLD, SIG_IGN); // 手动设置对子进程进行忽略if(fork() 0){std::cout child: getpid() std::endl;sleep(5);exit(0);}while(true){std::cout parent: getpid() 执行我自己的任务! std::endl;sleep(1);} } #问操作系统默认就是忽略的在此处我们还自己写一个忽略 也看得出来差别是很大的。忽略的概念是不一样的可以理解为对信号的处理有第四方案操作系统级别的忽略。可以理解为我们的忽略为1操作系统的忽略为2是不同级别的忽略。
http://www.tj-hxxt.cn/news/229603.html

相关文章:

  • 房地产网站做编辑刚刚入行wordpress怎么修改关键词
  • 寻乌建设局网站贵州省城乡建设厅官网
  • 在iis里面创建网站数字展馆
  • 企业网站建设毕业设计论文区块链 做网站
  • 广州外贸网站效果wordpress页面无法更新
  • 训做网站的心得体会范文wordpress快
  • liferay做网站好吗前端静态页面接单
  • 贵阳平台网站建设网站建设的分项报价
  • 大良网站建设市场初一下电脑课书做网站
  • 企业网站管理系统下载住房
  • 企业营销型网站应该有哪些内容百度广告联盟平台官网
  • 西安百度推广网站网络营销推广方案设计
  • 广东省自然资源厅网站住房和建设部执业资格注册中心网站
  • 网站设计套用模板专门做照片的网站
  • 站群网站建设杭州市工程建设招标网
  • 商城网站开发 价格网站设计大概在什么价位
  • 手机端网站开发页wordpress 创建文章
  • 招聘网站设计师要求电脑商城网站源码
  • 自媒体135的网站是多少哪里可以找到免费的源码
  • 广州做网站商城的公司免费做h5的网站知乎
  • 网站设计工程师是it行业吗百度手机助手下载安装
  • 帝国cms 门户网站如何安装网站模板
  • 织梦系统做的网站怎么样免费网站建设系统
  • 网站底部版权代码专门做超市海报的网站
  • 做网站需要的东西网站开发者模式下载视频教程
  • 谷歌自建站和优化jquery网站引导插件
  • 利用网站空间做代理深圳网站制作880
  • 建立企业网站收费标准seo整站网站推广优化排名
  • 网站内容过滤做一个游戏小程序需要多少钱
  • 卓进网站中国能源建设集团有限公司是央企