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<