TLPI 第24 章 练习:Process Creation
笔记和练习博客总目录见开始读TLPI。24-1在一个程序执行以下一系列 fork() 调用之后会产生多少个新进程假设所有调用都成功fork();fork();fork();总共会有8个进程新进程有7个。如下图初始进程 P0 ├─ 第一次fork后分裂 │ P0父 │ └─ P1子1 │ ├─ 第二次fork各自分裂 │ │ P0 → P2 │ │ P1 → P3 │ │ ├─ 第三次fork全部再分裂 │ │ │ P0 → P4 │ │ │ P2 → P5 │ │ │ P1 → P6 │ │ │ P3 → P7 │ │ │ │ │ │ 全部8个进程P0 P1 P2 P3 P4 P5 P6 P724-2编写一个程序来演示在 vfork() 之后子进程可以关闭一个文件描述符例如描述符 0而不会影响父进程中的相应文件描述符。原书提供的答案如procexec/vfork_fd_test.c#define_BSD_SOURCE/* To get vfork() declaration from unistd.h in case _XOPEN_SOURCE 700 */#includetlpi_hdr.hintmain(intargc,char*argv[]){switch(fork()){case-1:errExit(vfork);case0:if(close(STDOUT_FILENO)-1)errMsg(close - child);_exit(EXIT_SUCCESS);default:break;}/* Now parent closes STDOUT_FILENO twice: only the second close should fail, indicating that the close(STDOUT_FILENO) by the child did not affect the parent. */if(close(STDOUT_FILENO)-1)errMsg(first close);if(close(STDOUT_FILENO)-1)errMsg(second close);exit(EXIT_SUCCESS);}我修改了close之后的提示这样可以明确是first还是second close。他的解题思路挺巧妙就是如果第一次close失败说明影响了否则就没影响。运行如下$ ./vfork_fd_test ERROR[EBADF Badfiledescriptor]second close我的代码如下代码模板抄自fork(2) #includesignal.h#includestdint.h#includestdio.h#includestdlib.h#includeunistd.hintmain(void){pid_tpid;if(signal(SIGCHLD,SIG_IGN)SIG_ERR){perror(signal);exit(EXIT_FAILURE);}pidvfork();switch(pid){case-1:perror(fork);exit(EXIT_FAILURE);case0:puts(Child exiting.);close(STDOUT_FILENO);exit(EXIT_SUCCESS);default:printf(Child is PID %jd\n,(intmax_t)pid);puts(Parent exiting.);exit(EXIT_SUCCESS);}}运行如下$ ./ex24-2 child - Child exiting. parent - Child is PID16238parent - Parent exiting.我的思路是子进程关闭标准输出后看父进程输出信息是否能成功。不要被题目误导如果把vfork换成fork行为是一样的。但输出顺序是不同的因为vfork保证了子进程先执行。以下是vfork换成fork后的输出$ ./ex24-2 parent - Child is PID16229parent - Parent exiting. child - Child exiting.24-3假设我们可以修改程序源代码如何在某一特定时刻获取进程的核心转储core dump同时让进程继续执行由signal(7)和core(5)可知生成core dump的方式有两种killabort代码如下#includesignal.h#includestdint.h#includestdio.h#includestdlib.h#includeunistd.h#defineTESTSIGSIGABRTintmain(void){pid_tpid;if(signal(SIGCHLD,SIG_IGN)SIG_ERR){perror(signal);exit(EXIT_FAILURE);}pidfork();switch(pid){case-1:perror(fork);exit(EXIT_FAILURE);case0:puts(child - Child exiting.);raise(TESTSIG);puts(child - Never get here.);_exit(0);default:printf(parent - Child is PID %jd\n,(intmax_t)pid);for(;;)pause();puts(parent - Never get here.);}}执行$ ./ex24-3 parent - Child is PID16416child - Child exiting. ^C验证$ coredumpctl list TIME PIDUIDGID SIG COREFILE EXE SIZE Tue2026-07-14 01:52:22 UTC1641610001000SIGABRT present /home/vagrant/tlpi-book/ex/ex24-322.5K其实这道题很简单的但还是花了近1小时原因是发现vfork和fork行为不一。这道题只能用fork而非vfork因为vfork:vfork() differs from fork(2) in that the calling thread is suspended until the child terminates (ei‐ther normally, by calling _exit(2), or abnormally, after delivery of a fatal signal), or it makes acall to execve(2). Until that point, the child shares all memory with its parent, including thestack.24-4.在其他 UNIX 系统实现环境中运行清单 24-5 所示程序fork_whos_on_first.c进行测试以此探究各类系统在调用fork()创建子进程后是如何调度父进程与子进程的。fork_whos_on_first.c在目录procexec下。代码如下#includesys/wait.h#includetlpi_hdr.hintmain(intargc,char*argv[]){intnumChildren,j;pid_tchildPid;if(argc1strcmp(argv[1],--help)0)usageErr(%s [num-children]\n,argv[0]);numChildren(argc1)?getInt(argv[1],GN_GT_0,num-children):1;setbuf(stdout,NULL);/* Make stdout unbuffered */for(j0;jnumChildren;j){switch(childPidfork()){case-1:errExit(fork);case0:printf(%d child\n,j);_exit(EXIT_SUCCESS);default:printf(%d parent\n,j);wait(NULL);/* Wait for child to terminate */break;}}exit(EXIT_SUCCESS);}运行如下parent总是优先于child调度$ ./fork_whos_on_first50parent0child1parent1child2parent2child3parent3child4parent4child以上是在OEL 9.7上的行为。其他的UNIX环境可使用FreeBSD。参见VirtualBox上安装FreeBSD在FreeBSD上编译TLPI示例代码在FreeBSD下的行为是一样的$uname-aFreeBSD freebsd14.4-RELEASE FreeBSD14.4-RELEASE releng/14.4-n273675-a456f852d145 GENERIC amd64 tlpifreebsd:~/tlpi-dist/procexec $ ./fork_whos_on_first50parent0child1parent1child2parent2child3parent3child4parent4child24-5假设在清单 24-6 的程序中子进程也需要等待父进程完成某些操作。为了强制执行这一点需要对程序进行哪些修改清单 24-6即procexec/fork_sig_sync.c。代码如下/* Listing 24-6 */#includesignal.h#includecurr_time.h/* Declaration of currTime() */#includetlpi_hdr.h#defineSYNC_SIGSIGUSR1/* Synchronization signal */staticvoid/* Signal handler - does nothing but return */handler(intsig){}intmain(intargc,char*argv[]){pid_tchildPid;sigset_tblockMask,origMask,emptyMask;structsigactionsa;setbuf(stdout,NULL);/* Disable buffering of stdout */sigemptyset(blockMask);sigaddset(blockMask,SYNC_SIG);/* Block signal */if(sigprocmask(SIG_BLOCK,blockMask,origMask)-1)errExit(sigprocmask);sigemptyset(sa.sa_mask);sa.sa_flagsSA_RESTART;sa.sa_handlerhandler;if(sigaction(SYNC_SIG,sa,NULL)-1)errExit(sigaction);switch(childPidfork()){case-1:errExit(fork);case0:/* Child *//* Child does some required action here... */printf([%s %ld] Child started - doing some work\n,currTime(%T),(long)getpid());sleep(2);/* Simulate time spent doing some work *//* And then signals parent that its done */printf([%s %ld] Child about to signal parent\n,currTime(%T),(long)getpid());if(kill(getppid(),SYNC_SIG)-1)errExit(kill);/* Now child can do other things... */_exit(EXIT_SUCCESS);default:/* Parent *//* Parent may do some work here, and then waits for child to complete the required action */printf([%s %ld] Parent about to wait for signal\n,currTime(%T),(long)getpid());sigemptyset(emptyMask);if(sigsuspend(emptyMask)-1errno!EINTR)errExit(sigsuspend);printf([%s %ld] Parent got signal\n,currTime(%T),(long)getpid());/* If required, return signal mask to its original state */if(sigprocmask(SIG_SETMASK,origMask,NULL)-1)errExit(sigprocmask);/* Parent carries on to do other things... */exit(EXIT_SUCCESS);}}此程序为父子进程设置了一个联络信号SYNC_SIG子进程做一些工作后发送联络信号而父进程则等待这个信号。运行如下$ ./fork_sig_sync[02:39:5816571]Parent about towaitforsignal[02:39:5816572]Child started - doing some work[02:40:0016572]Child about to signal parent[02:40:0016571]Parent got signal如果父进程也需要做一些工作则将之前的同步机制再使用一次即可即子进程等待父进程的同步信号而父进程在完成任务后向子进程发送同步信号。代码如下#includesignal.h#includecurr_time.h/* Declaration of currTime() */#includetlpi_hdr.h#defineSYNC_SIGSIGUSR1/* Synchronization signal */staticvoid/* Signal handler - does nothing but return */handler(intsig){}intmain(intargc,char*argv[]){pid_tchildPid;sigset_tblockMask,origMask,emptyMask;structsigactionsa;setbuf(stdout,NULL);/* Disable buffering of stdout */sigemptyset(blockMask);sigaddset(blockMask,SYNC_SIG);/* Block signal */if(sigprocmask(SIG_BLOCK,blockMask,origMask)-1)errExit(sigprocmask);sigemptyset(sa.sa_mask);sa.sa_flagsSA_RESTART;sa.sa_handlerhandler;if(sigaction(SYNC_SIG,sa,NULL)-1)errExit(sigaction);switch(childPidfork()){case-1:errExit(fork);case0:/* Child *//* Child does some required action here... */printf([%s %ld] Child started - doing some work\n,currTime(%T),(long)getpid());sleep(2);/* Simulate time spent doing some work *//* And then signals parent that its done */printf([%s %ld] Child about to signal parent\n,currTime(%T),(long)getpid());if(kill(getppid(),SYNC_SIG)-1)errExit(kill);/* Now child can do other things... */if(sigsuspend(emptyMask)-1errno!EINTR)errExit(sigsuspend);printf([%s %ld] Child got signal\n,currTime(%T),(long)getpid());_exit(EXIT_SUCCESS);default:/* Parent *//* Parent may do some work here, and then waits for child to complete the required action */printf([%s %ld] Parent about to wait for signal\n,currTime(%T),(long)getpid());sigemptyset(emptyMask);if(sigsuspend(emptyMask)-1errno!EINTR)errExit(sigsuspend);printf([%s %ld] Parent got signal\n,currTime(%T),(long)getpid());/* If required, return signal mask to its original state */if(sigprocmask(SIG_SETMASK,origMask,NULL)-1)errExit(sigprocmask);/* Parent carries on to do other things... */printf([%s %ld] Parent started - doing some work\n,currTime(%T),(long)getpid());sleep(2);/* Simulate time spent doing some work */printf([%s %ld] Child about to signal parent\n,currTime(%T),(long)getpid());if(kill(childPid,SYNC_SIG)-1)errExit(kill);exit(EXIT_SUCCESS);}}

相关新闻

Mac mini本地智能体实战:OpenClaw+飞书机器人零云依赖工作流

Mac mini本地智能体实战:OpenClaw+飞书机器人零云依赖工作流

1. 这不是“又一个AI工具链”,而是Mac mini上真正能落地的智能体工作流 它来了——这句话在技术圈里从来不是营销话术,而是某种临界点被击穿后的集体直觉。过去三个月,我用一台 Mac mini M2(16GB内存) 搭建了一套从本…

2026/7/16 9:08:03 阅读更多 →
MetaBlriGer V2.7实战:为任意拓扑角色注入MetaHuman级面部动画

MetaBlriGer V2.7实战:为任意拓扑角色注入MetaHuman级面部动画

1. 项目概述:打通MetaHuman与自定义角色的桥梁最近在做一个高保真数字人项目,客户要求角色既能拥有MetaHuman级别的细腻表情,又要保留其独特的、非标准化的面部拓扑结构。这听起来像是个不可能的任务,对吧?毕竟&#x…

2026/7/16 9:06:03 阅读更多 →
AI生成内容在社交媒体泛滥:LinkedIn长文超40%为AI创作

AI生成内容在社交媒体泛滥:LinkedIn长文超40%为AI创作

两个月前,我在浏览 LinkedIn 时注意到一个现象:那些看起来结构完美、用词精准的长篇职业分享,读起来总有一种奇怪的“光滑感”——没有拼写错误,没有语法问题,但就是缺少了真实经验带来的粗糙感和具体细节。起初我以为…

2026/7/16 9:04:02 阅读更多 →

最新新闻

pacemaker-mgmt终极指南:从入门到精通的集群管理控制台全解析

pacemaker-mgmt终极指南:从入门到精通的集群管理控制台全解析

pacemaker-mgmt终极指南:从入门到精通的集群管理控制台全解析 【免费下载链接】pacemaker-mgmt Management Console for Pacemaker 项目地址: https://gitcode.com/openeuler/pacemaker-mgmt 前往项目官网免费下载:https://ar.openeuler.org/ar/ …

2026/7/16 10:04:47 阅读更多 →
Multisim无源蜂鸣器仿真:从基础连接到旋律生成的完整指南

Multisim无源蜂鸣器仿真:从基础连接到旋律生成的完整指南

1. 先搞清楚无源蜂鸣器在 Multisim 里到底怎么用无源蜂鸣器在 Multisim 仿真中最容易让人困惑的点是:它不像有源蜂鸣器那样给电就响,必须靠外部电路提供振荡信号才能发声。很多人第一次用的时候直接接直流电源,发现完全没声音,就以…

2026/7/16 10:02:45 阅读更多 →
Ubuntu 22.04 LTS核心特性与开发环境配置指南

Ubuntu 22.04 LTS核心特性与开发环境配置指南

1. Ubuntu 22.04 LTS 技术特性解析2022年4月21日,Canonical正式发布了Ubuntu 22.04 LTS(Jammy Jellyfish)长期支持版本。作为Linux发行版中的重要里程碑,这个版本最引人注目的技术特性就是采用了Linux 5.15内核。对于开发者而言&a…

2026/7/16 10:02:45 阅读更多 →
FydeOS:国产ChromeOS替代方案深度解析与实战指南

FydeOS:国产ChromeOS替代方案深度解析与实战指南

1. FydeOS 是什么?ChromeOS 的国内替代方案深度解析第一次接触 FydeOS 是在三年前帮朋友的老旧笔记本寻找轻量级系统时。这台2015年的联想小新Air 13在Windows 10下已经卡得无法正常使用,但刷入FydeOS后竟然能流畅运行Android版微信和WPS,续航…

2026/7/16 10:00:42 阅读更多 →
微软Modern OS核心技术解析与架构革新

微软Modern OS核心技术解析与架构革新

1. Modern OS的诞生背景与微软的战略转向2019年台北国际电脑展(COMPUTEX)上,微软首次公开提出Modern OS概念,这标志着Windows操作系统发展史上的重要转折点。当时微软消费者业务副总裁Yusuf Mehdi在主题演讲中明确表示&#xff1a…

2026/7/16 9:58:40 阅读更多 →
上拉电阻应用场景与阻值计算:硬件工程师必备设计指南

上拉电阻应用场景与阻值计算:硬件工程师必备设计指南

上拉电阻是硬件工程师必须掌握的基础知识,也是笔面试中的高频考点。这次我们重点分析什么场景需要用上拉电阻,以及如何正确选择阻值。 上拉电阻的核心作用是将不确定的电平拉至高电平,确保电路在无驱动时保持稳定状态。对于硬件工程师来说&a…

2026/7/16 9:56:39 阅读更多 →

日新闻

HarmonyOs应用《重要日》开发第6篇 - 数据持久化存储

HarmonyOs应用《重要日》开发第6篇 - 数据持久化存储

本篇深入剖析 ImportantDays 项目的数据持久化方案——基于 HarmonyOS ArkData 模块的 Preferences 轻量级存储,以及 PreferenceUtil 工具类的单例封装。一、HarmonyOS 数据存储方案对比 HarmonyOS 提供了多种数据存储方案:方案适用场景特点Preferences轻…

2026/7/16 0:08:27 阅读更多 →
Python实现跨境电商商品图批量翻译教程

Python实现跨境电商商品图批量翻译教程

一、问题引入做跨境电商的卖家朋友,你是否遇到过这样的困扰?每次上架新品到亚马逊、Shopee或Lazada等平台,都需要处理大量商品图片的多语言版本。比如上架200款衣服,每款需要翻译成英语、日语、韩语等5种语言,这意味着…

2026/7/16 0:08:27 阅读更多 →
鸿蒙 7 新特性实战①:从 0 到 1 掌握 @kit 标准导入规范

鸿蒙 7 新特性实战①:从 0 到 1 掌握 @kit 标准导入规范

从鸿蒙 7(HarmonyOS NEXT)开始,官方全面完成了从 ohos.* 零散模块到 kit.* 领域套件的体系重构。对开发者来说,第一道门槛不是 API 用法变化,而是统一的导入规范——旧体系默认导入、解构导入混用的混乱局面被彻底终结…

2026/7/16 0:10:29 阅读更多 →

周新闻

互联网大厂 Java 求职面试:燕双非的搞笑回答与技术探讨

互联网大厂 Java 求职面试:燕双非的搞笑回答与技术探讨

互联网大厂 Java 求职面试:燕双非的搞笑回答与技术探讨 在一个阳光明媚的上午,互联网大厂的面试官坐在桌前,准备迎接他的面试候选人——燕双非,一个以搞笑和幽默著称的程序员。第一轮提问 面试官:燕双非,作…

2026/7/15 21:09:01 阅读更多 →
车载以太网PMA测试设备选型:示波器、VNA、信号源3类仪器关键参数与预算评估

车载以太网PMA测试设备选型:示波器、VNA、信号源3类仪器关键参数与预算评估

车载以太网PMA测试设备选型:示波器、VNA、信号源3类仪器关键参数与预算评估在智能驾驶和车联网技术快速发展的今天,车载以太网作为新一代车载网络的核心传输技术,其物理层性能直接决定了数据传输的可靠性和稳定性。1000BASE-T1作为当前主流的…

2026/7/15 19:42:20 阅读更多 →
VSCode EIDE 插件 2.0:APM32/STM32 项目迁移实战,5步完成Keil工程转换

VSCode EIDE 插件 2.0:APM32/STM32 项目迁移实战,5步完成Keil工程转换

VSCode EIDE 插件 2.0:APM32/STM32 项目迁移实战指南嵌入式开发领域正经历一场工具链的静默革命。当传统Keil用户首次打开VSCode的扩展市场搜索EIDE时,往往会惊讶于这个看似简单的插件竟能重构十余年的开发习惯。本文将揭示如何用五个精准步骤&#xff0…

2026/7/15 17:52:08 阅读更多 →

月新闻