Tini 源码分析(2)

本文是Tini源码分析的第二部分,详细探讨了Tini如何处理父进程死亡后的信号、检查收割条件、创建子进程、转发信号以及收割僵尸进程的过程。文章深入解析了相关函数,如setpgid、tcsetpgrp、sigtimedwait和waitpid,阐述了Tini如何确保子进程的正常管理和资源回收。
摘要由CSDN通过智能技术生成

Tini 源码分析(2)

Tini 源码分析


我会从程序在 main 函数中执行的顺序,来一步一步的分析源码。每一个函数我都会贴上源码,然后进行分析。我把 main 函数的大概流程画了出来,大家可以参考一下。文章太长发不了,只能分割成两章了,这是第二章。

在这里插入图片描述

父进程死亡,此进程收到的信号


if (parent_death_signal && prctl(PR_SET_PDEATHSIG, parent_death_signal)) {
   
		PRINT_FATAL("Failed to set up parent death signal");
		return 1;
	 }

这段代码就比较简单了,int prctl ( int option,unsigned long arg2 ) 这个系统调用指令是为进程制定而设计的,明确的选择取决于option,PR_SET_PDEATHSIG :arg2作为处理器信号pdeath被输入,正如其名,如果父进程不能再用,进程接受这个信号。

检测是否能收割


void reaper_check () {
   
	/* 检查我们是否能正确收割僵尸进程 */
#if HAS_SUBREAPER
	int bit = 0;
#endif

	if (getpid() == 1) {
   
		return;
	}

#if HAS_SUBREAPER
	if (prctl(PR_GET_CHILD_SUBREAPER, &bit)) {
   
		PRINT_DEBUG("Failed to read child subreaper attribute: %s", strerror(errno));
	} else if (bit == 1) {
   
		return;
	}
#endif

	PRINT_WARNING(reaper_warning);
}

此函数检测自己是否能正确的收割僵尸进程的。判断条件1,如果此进程为 init 进程,则可以收割僵尸进程,直接返回;如果不满足,第二个判断条件,是否设置了参数或者环境变量,使此进程开启子进程收割者的命令,如果可以则直接返回;否则,打印警告信息,该进程无法正常收割僵尸进程。

创建子进程


// 创建子进程
int spawn(const signal_configuration_t* const sigconf_ptr, char* const argv[], int* const child_pid_ptr) {
   
	pid_t pid;

	// TODO: check if tini was a foreground process to begin with (it's not OK to "steal" the foreground!")

	pid = fork();
	if (pid < 0) {
   
		PRINT_FATAL("fork failed: %s", strerror(errno));
		return 1;
	} else if (pid == 0) {
   

		// 把子进程放在一个进程组中,如果有tty的话,让它成为前台进程。
		if (isolate_child()) {
   
			return 1;
		}

		// 将所有的信号处理程序恢复到我们 触碰 它们之前的样子。
		if (restore_signals(sigconf_ptr)) {
   
			return 1;
		}

		execvp(argv[0], argv);

		// execvp只会在出错时返回,所以要确保我们检查errno,并为我们遇到的错误提供正确的返回状态。
		// See: http://www.tldp.org/LDP/abs/html/exitcodes.html#EXITCODESREF
		int status = 1;
		switch (errno) {
   
			case ENOENT:
				status = 127;
				break;
			case EACCES:
				status = 126;
				break;
		}
		PRINT_FATAL("exec %s failed: %s", argv[0], strerror(errno));
		return status;
	} else {
   
		// Parent
		PRINT_INFO("Spawned child process '%s' with pid '%i'", argv[0], pid);
		*child_pid_ptr = pid;
		return 0;
	}
}

这里面主进程 fork() 了一个子进程出来。后面则是主进程和子进程的不同路线了,如果 pid 小于 0 则表示进程创建失败,打印错误信息并返回。

如果 pid == 0 则表示此进程为子进程,会进入以下的代码块:

else if (pid == 0) {
   

		// 把子进程放在一个进程组中,如果有tty的话,让它成为前台进程。
		if (isolate_child()) {
   
			return 1;
		}

		// 将所有的信号处理程序恢复到我们 触碰 它们之前的样子。
		if (restore_signals(sigconf_ptr)) {
   
			return 1;
		}

		execvp(argv[0], argv);

		// execvp只会在出错时返回,所以要确保我们检查errno,并为我们遇到的错误提供正确的返回状态。
		// See: http://www.tldp.org/LDP/abs/html/exitcodes.html#EXITCODESREF
		int status = 1;
		switch (errno) {
   
			case ENOENT:
				status = 127;
				break;
			case EACCES:
				status = 126<
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值