fork 为何返回父进程,子进程 不同值?

深入解析进程创建与消息传递机制
本文详细探讨了在操作系统中,父进程如何通过fork函数创建子进程,并且阐述了消息传递机制如何确保父进程和子进程在执行fork后的不同行为。重点介绍了消息在进程间通信中的作用,以及特定函数如task_mm和do_fork如何实现进程间的协调与信息交换。
int pid = fork();
	if (pid != 0) { /* parent process */
		printf("parent is running, child pid:%d\n", pid);
		spin("parent");
	}
	else {	/* child process */
		printf("child is running, pid:%d\n", getpid());
		spin("child");
	}

父进程 创建 子进程之后,子进程和 父进程 有相同 代码,都将从 fork 返回,而返回值不同。



PUBLIC int fork()
{
	MESSAGE msg;
	msg.type = FORK;


	send_recv(BOTH, TASK_MM, &msg);// 父进程发的,先发 ,等待接受。因为 子进程 和父进程有相同代码,所以子进程 也在等这个消息。!!!!虽然 没有发!!!
	assert(msg.type == SYSCALL_RET);
	assert(msg.RETVAL == 0);


	return msg.PID;
}

 正是 上面函数中的msg 不同,导致了返回值不同。

父进程 给 TASK_MM 发消息,说要fork 子进程,


PUBLIC void task_mm()
{
	init_mm();

	while (1) {
		send_recv(RECEIVE, ANY, &mm_msg);
		int src = mm_msg.source;
		int reply = 1;

		int msgtype = mm_msg.type;

		switch (msgtype) {
		case FORK:
			mm_msg.RETVAL = do_fork();//TASK_MM  接到消息 后开始执行 do_fork
			break;
		case EXIT:
			do_exit(mm_msg.STATUS);
			reply = 0;
			break;
		/* case EXEC: */
		/* 	mm_msg.RETVAL = do_exec(); */
		/* 	break; */
		case WAIT:
			do_wait();
			reply = 0;
			break;
		default:
			dump_msg("MM::unknown msg", &mm_msg);
			assert(0);
			break;
		}

		if (reply) {
			mm_msg.type = SYSCALL_RET;
			send_recv(SEND, src, &mm_msg);
		}
	}
}

PUBLIC int do_fork()
{
	/* find a free slot in proc_table */
	struct proc* p = proc_table;
	int i;
	for (i = 0; i < NR_TASKS + NR_PROCS; i++,p++)
		if (p->p_flags == FREE_SLOT)
			break;

	int child_pid = i;
	assert(p == &proc_table[child_pid]);
	assert(child_pid >= NR_TASKS + NR_NATIVE_PROCS);
	if (i == NR_TASKS + NR_PROCS) /* no free slot */
		return -1;
	assert(i < NR_TASKS + NR_PROCS);

	/* duplicate the process table */
	int pid = mm_msg.source;
	u16 child_ldt_sel = p->ldt_sel;
	*p = proc_table[pid];
	p->ldt_sel = child_ldt_sel;
	p->p_parent = pid;
	sprintf(p->name, "%s_%d", proc_table[pid].name, child_pid);

	/* duplicate the process: T, D & S */
	struct descriptor * ppd;

	/* Text segment */
	ppd = &proc_table[pid].ldts[INDEX_LDT_C];
	/* base of T-seg, in bytes */
	int caller_T_base  = reassembly(ppd->base_high, 24,
					ppd->base_mid,  16,
					ppd->base_low);
	/* limit of T-seg, in 1 or 4096 bytes,
	   depending on the G bit of descriptor */
	int caller_T_limit = reassembly(0, 0,
					(ppd->limit_high_attr2 & 0xF), 16,
					ppd->limit_low);
	/* size of T-seg, in bytes */
	int caller_T_size  = ((caller_T_limit + 1) *
			      ((ppd->limit_high_attr2 & (DA_LIMIT_4K >> 8)) ?
			       4096 : 1));

	/* Data & Stack segments */
	ppd = &proc_table[pid].ldts[INDEX_LDT_RW];
	/* base of D&S-seg, in bytes */
	int caller_D_S_base  = reassembly(ppd->base_high, 24,
					  ppd->base_mid,  16,
					  ppd->base_low);
	/* limit of D&S-seg, in 1 or 4096 bytes,
	   depending on the G bit of descriptor */
	int caller_D_S_limit = reassembly((ppd->limit_high_attr2 & 0xF), 16,
					  0, 0,
					  ppd->limit_low);
	/* size of D&S-seg, in bytes */
	int caller_D_S_size  = ((caller_T_limit + 1) *
				((ppd->limit_high_attr2 & (DA_LIMIT_4K >> 8)) ?
				 4096 : 1));

	/* we don't separate T, D & S segments, so we have: */
	assert((caller_T_base  == caller_D_S_base ) &&
	       (caller_T_limit == caller_D_S_limit) &&
	       (caller_T_size  == caller_D_S_size ));

	/* base of child proc, T, D & S segments share the same space,
	   so we allocate memory just once */
	int child_base = alloc_mem(child_pid, caller_T_size);
	/* int child_limit = caller_T_limit; */
	printl("{MM} 0x%x <- 0x%x (0x%x bytes)\n",
	       child_base, caller_T_base, caller_T_size);
	/* child is a copy of the parent */
	phys_copy((void*)child_base, (void*)caller_T_base, caller_T_size);

	/* child's LDT */
	init_desc(&p->ldts[INDEX_LDT_C],
		  child_base,
		  (PROC_IMAGE_SIZE_DEFAULT - 1) >> LIMIT_4K_SHIFT,
		  DA_LIMIT_4K | DA_32 | DA_C | PRIVILEGE_USER << 5);
	init_desc(&p->ldts[INDEX_LDT_RW],
		  child_base,
		  (PROC_IMAGE_SIZE_DEFAULT - 1) >> LIMIT_4K_SHIFT,
		  DA_LIMIT_4K | DA_32 | DA_DRW | PRIVILEGE_USER << 5);

	/* tell FS, see fs_fork() */
	MESSAGE msg2fs;
	msg2fs.type = FORK;
	msg2fs.PID = child_pid;
	send_recv(BOTH, TASK_FS, &msg2fs);

	/* child PID will be returned to the parent proc */
	mm_msg.PID = child_pid;// 这里 mm_msg 是个全局变量 ,do_fork 返回后,TASK_MM 给 父进程发消息 返回 子进程的id。

	/* birth of the child */
	MESSAGE m;
	m.type = SYSCALL_RET;
	m.RETVAL = 0;
	m.PID = 0;
	send_recv(SEND, child_pid, &m);// 重点!!注意这是TASK_MM 给 子进程  发的消息,而此刻 子进程在等这个消息。

	return 0;
}

如此 ,父进程 和子进程 的到不同返回值。


<think>我们要实现一个父进程监听子进程,当子进程报错时返回的功能。 在Unix-like系统中,我们可以使用`fork`创建子进程,然后父进程使用`wait`或`waitpid`来等待子进程结束,并获取其退出状态。 子进程报错时,通常我们会让子进程以非0状态退出,父进程通过检查退出状态来判断。 步骤: 1. 使用os.fork()创建子进程。 2. 在子进程中执行任务,如果发生错误,则通过sys.exit(0)退出。 3. 在父进程中,使用os.wait()或os.waitpid()等待子进程结束,然后检查子进程的退出状态。 注意:os.wait()返回一个元组(pid, status),我们可以通过os.WIFEXITED(status)判断是否正常退出,通过os.WEXITSTATUS(status)获取退出状态码(如果正常退出,这个状态码是exit()传递的参数)。 如果子进程因为信号而终止,我们可以用os.WIFSIGNALED(status)判断,并用os.WTERMSIG(status)得到导致子进程终止的信号。 这里我们主要关注子进程的退出状态码(非0表示错误)。 我们写一个示例,其中子进程模拟一个可能出错的任务(比如除以零),但为了避免真正让解释器崩溃,我们可以在子进程中捕获异常并退出。 注意:在子进程中,如果发生未捕获的异常,Python解释器会以状态码1退出。但我们也可以自己控制。 示例代码: </think> 以下是使用Python实现的父进程监听子进程并在子进程报错时返回的解决方案: ```python import os import sys import time def child_process(): """子进程任务(模拟可能报错的操作)""" try: print(f"[子进程 {os.getpid()}] 开始执行") time.sleep(1) # 模拟报错(如除以零) result = 10 / 0 except Exception as e: print(f"[子进程] 发生错误: {str(e)}") sys.exit(1) # 非0退出码表示异常退出 def parent_process(): """父进程监听子进程""" pid = os.fork() if pid == 0: # 子进程 child_process() else: # 父进程 print(f"[父进程 {os.getpid()}] 创建子进程: {pid}") _, status = os.wait() # 等待子进程结束 if os.WIFEXITED(status): # 检查是否正常退出 exit_code = os.WEXITSTATUS(status) if exit_code != 0: print(f"[父进程] 子进程异常退出,错误码: {exit_code}") return # 检测到错误时返回 print("[父进程] 子进程正常结束") else: print(f"[父进程] 子进程被信号终止") if __name__ == "__main__": parent_process() ``` ### 关键机制说明: 1. **进程创建**:`os.fork()`创建子进程返回0表示子进程,非0子进程PID)表示父进程 2. **错误处理**: - 子进程用`try/except`捕获异常 - 错误时调用`sys.exit(1)`返回0状态码 3. **进程监控**: - `os.wait()`阻塞父进程直至子进程结束 - `os.WIFEXITED()`检测退出方式 - `os.WEXITSTATUS()`获取子进程退出码 4. **错误响应**:父进程检测到非0退出码时执行返回操作 ### 执行结果示例: ``` [父进程 1234] 创建子进程: 1235 [子进程 1235] 开始执行 [子进程] 发生错误: division by zero [父进程] 子进程异常退出,错误码: 1 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值