Tini 源码分析(1)

Tini 源码分析(1)

(奇怪,我是一个写 Go 的,为什么要来分析 C 代码的项目啊!)

Tini 简介


Tini 是一个超轻量级的 init 进程管理器,被设计作为容器的 1 号进程。

Tini 只会做以下的事情:

  • 只生成一个子进程(这意味着 Tini 应该运行在容器中),并等待子进程退出
  • 收割僵尸进程
  • 执行信号转发

Tini 只能管理一个进程,容器的最佳实践一般都是一个容器即一个进程,因此 Tini 在容器化场景足够使用了。

Tini 的使用


如果你使用的是 Docker 1.13 或更高版本, Tini 已包含在 Docker 中。包括所以版本的 Docker CE。如果要启用 Tini,只需将 --init 参数传递给 Docker run 即可。

Tini 的优势

使用 Tini 有几个好处:

  • 它可以保护你应用免受僵尸进程的侵害,这可能会随着时间而推移,使你的整个系统缺少可分配的 PID(并使其不可用)。
  • 它确保默认信号处理程序适用于你运行的 Docker 镜像中的应用。例如,使用 Tini 可以让 SIGTERM 正确终止你的进程,即使没有显式的编写信号处理程序。
  • 它运行起来是完全透明的。没有 Tini 的 Docker 镜像和有 Tini 的Docker 镜像使用起来是没有区别的。

如果你想详细了解 Tini 的好处,可以查看:What is advantage of Tini?

Tini 源码分析


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

int main(int argc, char *argv[]) {
   
    // 用于存放 子进程的pid
	pid_t child_pid;

	// 这些被传递到函数中,以获得一个exitcode。
	int child_exitcode = -1;  // 这不是有效的exitcode;让我们判断子进程是否已经退出。
	int parse_exitcode = 1;   // 默认情况下,如果解析失败,则返回1。

	/* 解析命令行参数 */
	char* (*child_args_ptr)[];
	int parse_args_ret = parse_args(argc, argv, &child_args_ptr, &parse_exitcode);
	if (parse_args_ret) {
   
		return parse_exitcode;
	}

	/* 解析环境 */
	if (parse_env()) {
   
		return 1;
	}

	/* 配置信号 */
	sigset_t parent_sigset, child_sigset;
	struct sigaction sigttin_action, sigttou_action;
	memset(&sigttin_action, 0, sizeof sigttin_action);
	memset(&sigttou_action, 0, sizeof sigttou_action);

	signal_configuration_t child_sigconf = {
   
		.sigmask_ptr = &child_sigset,
		.sigttin_action_ptr = &sigttin_action,
		.sigttou_action_ptr = &sigttou_action,
	};

	if (configure_signals(&parent_sigset, &child_sigconf)) {
   
		return 1;
	}

	/* 当父进程退出时,该进程上会触发的信号。 */
	if (parent_death_signal && prctl(PR_SET_PDEATHSIG, parent_death_signal)) {
   
		PRINT_FATAL("Failed to set up parent death signal");
		return 1;
	 }

#if HAS_SUBREAPER
	/* If available and requested, register as a subreaper */
	if (register_subreaper()) {
   
		return 1;
	};
#endif

	/* 我们可以正常收割僵尸进程吗?如果没有,警告。 */
	reaper_check();

	/* 创建子进程 */
	int spawn_ret = spawn(&child_sigconf, *child_args_ptr, &child_pid);
	if (spawn_ret) {
   
		return spawn_ret;
	}
	free(child_args_ptr);

	while (1) {
   
		/* 等待信号并转发信号 */
		if (wait_and_forward_signal(&parent_sigset, child_pid)) {
   
			return 1;
		}

		/* 现在,收割僵尸进程 */
		if (reap_zombies(child_pid, &child_exitcode)) {
   
			return 1;
		}

		if (child_exitcode != -1) {
   
			PRINT_TRACE("Exiting: child has exited");
			return child_exitcode;
		}
	}
}

在这里插入图片描述

解析命令行参数



// 解析命令行参数
int parse_args(const int argc, 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值