Day4 横空出世的 kernel_clone

前言

昨天的最後提到建立user process的三个系统呼叫:fork(),vfork(),clone(),与建立Kernel thread的两种系统呼叫系统呼叫: Kernel_thread(), kthread_create() ,这些系统呼叫都是呼叫 _do_fork() 完成的,在查询资料的时候,发现了最新版本的 linux kernel,找不到任何一丝 _do_fork() 函数的踪迹,经过抽丝剥茧的查询,终於找到原因了,节录自这里

The old _do_fork() helper doesn't follow naming conventions of in-kernel
helpers for syscalls. The process creation cleanup in [1] didn't change the
name to something more reasonable mainly because _do_fork() was used in quite a few places. So sending this as a separate series seemed the better strategy.

从 Linux v5.10 後,原来旧版的 _do_fork() 已经因为命名规则被新的函数名称 kernel_thread() 取代了,不过这部分的修改就只是修改函数名称,并没有实作有更动,接下来让我们看看几个与Kernel_thread()有关的函数吧!

fork() / vfork() / clone() / kernel_thread()

fork()

以下节录自此

fork() creates a new process by duplicating the calling process.The new process is referred to as the child process. The calling process is referred to as the parent process. The child process and the parent process run in separate memory spaces. At the time of fork() both memory spaces have the same content. Memory writes, file mappings, and unmappings performed by one of the processes do not affect the
other.

程序码如下:

SYSCALL_DEFINE0(fork)
{
#ifdef CONFIG_MMU
	struct kernel_clone_args args = {
		.exit_signal = SIGCHLD,
	};

	return kernel_clone(&args);
#else
	/* can not support in nommu mode */
	return -EINVAL;
#endif
}

fork() 只使用 SIGCHLD 标志,在子行程中只之後发送 SIGCHLD 信号通知父行程, fork() 在执行上完全复制了 parent process 的资料,所以需要很多的资源处理,为了增快速度使用了写入时复制(Copy-on-write,COW),顾名思义就是在child process 开始执行自己的工作之前,都与parent process共享同样的address space。

vfork()

以下节录自此

Standard description (From POSIX.1) The vfork() function has the same effect as fork(2), except that the behavior is undefined if the process created by vfork() either modifies any data other than a variable of type pid_t used to store the return value from vfork(), or returns from the function in which vfork() was called, or calls any other function before successfully calling _exit(2) or one of the exec(3) family of functions.

程序码如下:

SYSCALL_DEFINE0(vfork)
{
	struct kernel_clone_args args = {
		.flags		= CLONE_VFORK | CLONE_VM,
		.exit_signal	= SIGCHLD,
	};

	return kernel_clone(&args);
}

clone()

节录自此

These system calls create a new ("child") process, in a manner similar to fork(2).
By contrast with fork(2), these system calls provide more precise control over what pieces of execution context are shared between the calling process and the child process. For example, using these system calls, the caller can control whether or not the two processes share the virtual address space, the table of file descriptors, and the table of signal handlers. These system calls also allow the new child process to be placed in separate namespaces(7).

程序码如下:

SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp,
		 int __user *, parent_tidptr,
		 int __user *, child_tidptr,
		 unsigned long, tls)
#endif
{
	struct kernel_clone_args args = {
		.flags		= (lower_32_bits(clone_flags) & ~CSIGNAL),
		.pidfd		= parent_tidptr,
		.child_tid	= child_tidptr,
		.parent_tid	= parent_tidptr,
		.exit_signal	= (lower_32_bits(clone_flags) & CSIGNAL),
		.stack		= newsp,
		.tls		= tls,
	};

	return kernel_clone(&args);
}

在linux中没有专门的执行绪,而是把执行绪当成行程看待,实际上仍是使用 task_struct 描述执行绪。 clone()用於创建执行绪,最强大的功能就是可以有效地继承 parent process 的资源,可以选择共享定址空间(address space) 或是不共享。

kernel_thread()

利用 kernel_thread() 建立一个 kernel thread,kernel thread 很像一般的行程,一样拥有 task_structure ,一样有PID,不同的是他们并没有独立的定址空间(address space),而且只能在 kernel mode 运行。

程序码如下:

pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
{
	struct kernel_clone_args args = {
		.flags		= ((lower_32_bits(flags) | CLONE_VM |
				    CLONE_UNTRACED) & ~CSIGNAL),
		.exit_signal	= (lower_32_bits(flags) & CSIGNAL),
		.stack		= (unsigned long)fn,
		.stack_size	= (unsigned long)arg,
	};

	return kernel_clone(&args);
}

在找寻 kernel_thread()的解释时,发现没办法用 "Kernel_thread man7" 找到该系统呼叫,才发现原来 kernel_thread() 根本不是一个系统呼叫,详细解释在此篇

看完了四个跟process有关系的系统呼叫,明天目标就是研究一下 kernel_clone()函数实际上做了什麽事情!!
让我们明天见!!


<<:  【Day4】Navigation导航X注册画面X Firebase Auth

>>:  [Day18] Esp32用STA mode + Relay - (程序码讲解)

例行监督表单撰写实务

上一篇内部稽核讲到 5. 监督作业:进行下列监督作业,以确定本制度之有效性、及时性及确实性: (1)...

认识 C# 的 存取层级修饰词

修饰词~可以限制类别的存取层级 我的举例是:像是一些私密的东西你不想让别人随便乱看一样 就要设隐私权...

DAY1-EXCEL统计分析:前言

统计在现代社会中是不可或缺的,而更重要的是经过统计後的分析。不论在各行各业皆需要专业且精密的统计分析...

Proxmox VE 安装虚拟机:Ubuntu Server 20.04

前一章我们将 Windows 10 成功的在虚拟机装安装起来,本章换个完全不同的作业系统来安装,这...

[Day3] - 前端,後端是在做什麽? --续 前端後端的历史及架构

其实每个时期程序流行的架构以及写法会略有不同,每个时期前端後端负责的范畴也不尽相同,我们无法知道我们...