前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >进程的描述和创建

进程的描述和创建

作者头像
De4dCr0w
发布于 2019-02-27 08:19:34
发布于 2019-02-27 08:19:34
90300
代码可运行
举报
运行总次数:0
代码可运行

版权声明:本文为博主原创文章,未经博主允许不得转载。 罗军 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

基础知识

thread_union

进程在内核态运行时需要自己的堆栈信息,linux内核为每个进程都提供了一个内核栈。对每个进程,Linux内核都把两个不同的数据结构紧凑的存放在一个单独为进程分配的内存区域中:

  • 一个是内核态的进程堆栈stack
  • 另一个是紧挨着进程描述符的小数据结构thread_info,叫做线程描述符。

它们共同组成了thread_union

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
union thread_union
{
    struct thread_info thread_info;
    unsigned long stack[THREAD_SIZE/sizeof(long)];
};

task_struct包含了指向thread_info的字段,而thread_info通过task字段和thread_struct相互联系。

写时复制

  写时复制技术允许父子进程读相同的物理页,只要两者中有一个试图写一个物理页,内核就把这个页的内容拷贝到一个新的物理页,并把这个新的物理页分配给正在写的进程。这样做得目的是为了提高进程创建的效率,因为子进程全部拷贝父进程的地址空间非常慢且效率低,实际上,子进程几乎不必读或修改父进程拥有的所有资源,在很多情况下,子进程立即调用execve(),并清除父进程之前拷贝过来的地址空间。   这里为什么要介绍写时复制呢?   因为wake_up_new_task函数里会执行下列操作:如果子进程和父进程运行在同一个CPU上,而且父进程和子进程不能共享同一组页表,那么,就把子进程插入父进程运行队列,插入时让子进程在父进程前面执行,这里为什么要让子进程先执行呢?   因为如果我们先让父进程运行,那么写时复制机制将会执行一系列不必要的页面复制。

代码分析

do_fork函数

  • 通过查找pidmap_array位图,为子进程分配新的pid
  • 检查父进程的ptrace字段
  • 调用copy_process()复制进程描述符
  • 调用wake_up_new_task()函数
  • 如果设置了CLONE_VFORK标志,则把父进程插入等待队列,并挂起父进程直到子进程释放自己的内存地址空间。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (!(clone_flags & CLONE_UNTRACED)) {
			if (clone_flags & CLONE_VFORK)
				trace = PTRACE_EVENT_VFORK;
			else if ((clone_flags & CSIGNAL) != SIGCHLD)
				trace = PTRACE_EVENT_CLONE;
			else
				trace = PTRACE_EVENT_FORK;
	
			if (likely(!ptrace_event_enabled(current, trace)))
				trace = 0;
		}

从上面的代码可以看出系统调用clone()、fork()、和vfork()都是由do_fork()进行处理的。do_fork通过copy_process函数来创建进程描述符和子进程执行所需要的所有其他内核数据结构。

copy_process函数

  • 检查参数clone_flags所传递标志的一致性。
  • 通过调用security_task_create()以及稍后调用的security_task_alloc()执行所有附加的安全检查
  • 调用dup_task_struct()为子进程获取进程描述符
  • 检查系统中的进程数量(存放在NR_THREADS变量中)是否超过max_threads变量的值
  • 把tsk->did_exec字段初始化为0:它记录了进程发出的execve()系统调用的次数
  • 把新进程的pid存入tsk->pid字段
  • 初始化子进程描述符中的list_head数据结构和自旋锁,并为与挂起信号、定时器及时间统计表相关的几个字段赋初值
  • 调用copy_semundo(),copy_files(),copy_fs(),copy_sighand(),copy_signal(),copy_mm()和copy_namespace()来创建新的数据结构,并把父进程相应数据结构的值复制到新数据结构中。
  • 调用copy_thread(),将保存在父进程的内核栈中的CPU寄存器的值来初始化子进程的内核栈,将eax寄存器置0,子进程返回值为0,将ret_from_fork()的地址存放在thread.eip字段
  • 清除子进程thread_info结构的TIF_SYSCALL_TRACE标志,使ret_from_fork()函数不会把系统调用结束的消息通知给调试进程。
  • 调用sched_fork()完成对新进程调度程序数据结构的初始化,把新进程的状态设置为TASK_RUNNING,并把thread_info结构的preempt_count字段设置为1,从而禁止内核抢占
  • 初始化亲子关系字段
  • 将新进程pid插入散列表中
  • 递增nr_threads变量的值
  • 递增total_forks变量记录被创建的进程的数量

copy_thread函数

  • 将保存在父进程的内核栈中的CPU寄存器的值来初始化子进程的内核栈
  • 将eax寄存器置0,子进程返回值为0
  • 将ret_from_fork()的地址存放在thread.eip字段

dup_task_struct函数

dup_task_struct 根据父进程创建子进程内核栈和进程描述符:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static struct task_struct *dup_task_struct(struct task_struct *orig)
{
	struct task_struct *tsk;
	struct thread_info *ti;
	int node = tsk_fork_get_node(orig);
	int err;
	//创建进程描述符对象
	tsk = alloc_task_struct_node(node);
	if (!tsk)
		return NULL;
	//创建进程内核栈
	ti = alloc_thread_info_node(tsk, node);
	if (!ti)
		goto free_tsk;
	//使子进程描述符和父进程一致
	err = arch_dup_task_struct(tsk, orig);
	if (err)
		goto free_ti;
	//进程描述符stack指向thread_info
	tsk->stack = ti;
	……
	//使子进程thread_info内容与父进程一致但task指向子进程task_struct
	setup_thread_stack(tsk,orig);
	……
	return tsk;
	……
}

代码调试

forkAPI函数,会通过宏指令来跳转到相应的系统调用

forkAPI函数会通过SYS_clone宏指令,最终会调用do_fork函数:

调用copy_process函数

调用dup_task_struct函数

经过dup_task_struct和copy_thread等一系列操作后,子进程被创建,递增总进程数: 创建子进程之前total_forks值:

创建子进程之后total_forks值:

进程上下文的切换:

代码结构图

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Linux下进程的创建过程分析(_do_fork do_fork详解)--Linux进程的管理与调度(八)
Unix标准的复制进程的系统调用时fork(即分叉),但是Linux,BSD等操作系统并不止实现这一个,确切的说linux实现了三个,fork,vfork,clone(确切说vfork创造出来的是轻量级进程,也叫线程,是共享资源的进程)
233333
2018/10/09
2.7K0
Linux下进程的创建过程分析(_do_fork do_fork详解)--Linux进程的管理与调度(八)
一文看懂 | fork 系统调用
Unix标准的复制进程的系统调用时fork(即分叉),但是Linux,BSD等操作系统并不止实现这一个,确切的说linux实现了三个,fork,vfork,clone(确切说vfork创造出来的是轻量级进程,也叫线程,是共享资源的进程)
用户7686797
2021/10/20
2.6K0
分析Linux系统的执行过程
原创作品转载请注明出处 + https://github.com/mengning/linuxkernel/
matt
2022/10/25
9900
分析Linux系统的执行过程
do_fork 的实现
上面讲述了如何通过 fork, vfork, pthread_create 去创建一个进程,或者一个线程。通过分析最终 fork, vfork, pthread_create 最终都会通过系统调用 do_fork 去创建进程。
刘盼
2021/07/05
7530
do_fork 的实现
Linux内核15-内核如何创建进程
不论是clone()、fork()还是vfork(),它们最核心的部分还是调用_do_fork()(一个与体系无关的函数),完成创建进程的工作。它具有如下参数:
Tupelo
2022/08/15
2.1K0
Linux kernel Namespace源码分析
学习一下linux kernel namespace的代码还是很有必要的,让你对docker容器的namespace隔离有更深的认识。我的源码分析,是基于Linux Kernel 4.4.19 (https://www.kernel.org/pub/linux/kernel/v4.x/patch-4.4.19.gz)版本的,由于namespace模块更新很少,因此其他相近版本之间雷同。User namespace由于与其他namespaces耦合在一起,比较难分析,我将在后续再作分析。 Kernel,Nam
Walton
2018/04/13
13K0
Linux kernel Namespace源码分析
linux内核进程创建fork源码解析
    平时写过多进程多线程程序,比如使用linux的系统调用fork创建子进程和glibc中的nptl包里的pthread_create创建线程,甚至在java里使用Thread类创建线程等,虽然使用问题不大,但需要知道底层原理。这次在自己写操作系统的时候,看了一遍linux内核的进程创建过程。算是有了比较深入的理解。
用户4415180
2022/06/23
8.8K0
linux内核进程创建fork源码解析
do_fork实现--上
在前面几节中讲述了如何通过fork, vfork, pthread_create去创建一个进程,或者一个线程。通过分析最终fork, vfork, pthread_create最终会通过系统调用clone去创建进程。
DragonKingZhu
2020/03/24
1.3K0
do_fork实现--上
原来进程是这么创建的
大家好,我是程栩,一个专注于性能的大厂程序员,分享包括但不限于计算机体系结构、性能优化、云原生的知识。
程栩的性能优化笔记
2023/11/01
3860
原来进程是这么创建的
do_fork实现--下
昨天在do_fork实现–上中学习了do_fork创建的前半段,今天我们接着继续分析copy_Process函数
DragonKingZhu
2020/03/24
1.5K0
do_fork实现--下
fork系统调用分析
在Linux中,主要是通过fork的方式产生新的进程,我们都知道每个进程都在 内核对应一个PCB块,内核通过对PCB块的操作做到对进程的管理。在Linux内核中,PCB对应着的结构体就是task_struct,也就是所谓的进程描述符(process descriptor)。该数据结构中包含了程相关的所有信息,比如包含众多描述进程属性的字段,以及指向其他与进程相关的结构体的指针。因此,进程描述符内部是比较复杂的。这个结构体的声明位于include/linux/sched.h中。
Marco爱吃红烧肉
2021/12/24
1K0
fork系统调用分析
《Linux内核分析》之分析fork函数对应的系统调用处理过程
xref: /linux-3.18.6/include/linux/sched.h
WindCoder
2018/09/20
1.1K0
《Linux内核分析》之分析fork函数对应的系统调用处理过程
Linux进程是如何创建出来的?
在 Linux 中,进程是我们非常熟悉的东东了,哪怕是只写过一天代码的人也都用过它。但是你确定它不是你最熟悉的陌生人?我们今天通过深度剖析进程的创建过程,帮助你提高对进程的理解深度。
开发内功修炼
2022/12/07
2.1K0
Linux进程是如何创建出来的?
Linux进程详解
程序是指储存在外部存储(如硬盘)的一个可执行文件, 而进程是指处于执行期间的程序, 进程包括 代码段(text section) 和 数据段(data section), 除了代码段和数据段外, 进程一般还包含打开的文件, 要处理的信号和CPU上下文等等.
用户7686797
2020/08/25
4K0
Linux进程详解
聊聊Linux中线程和进程的联系与区别!
关于进程和线程,在 Linux 中是一对儿很核心的概念。但是进程和线程到底有啥联系,又有啥区别,很多人还都没有搞清楚。
开发内功修炼
2022/12/07
2.3K0
聊聊Linux中线程和进程的联系与区别!
操作系统核心知识点整理--进程篇
操作系统对内存的使用是按段的,例如: 我们编写的一个程序被操作系统加载到内存是按照数据段,代码段等形式分段载入。而操作系统自身的代码也是按段载入的,为了确保安全性,我们用户编写的程序是不能直接访问操作系统的相关段的,因此需要给不同段赋予不同的特权级。
大忽悠爱学习
2023/02/26
7060
操作系统核心知识点整理--进程篇
深入理解Linux内核之内核线程(上)
在linux系统中, 我们接触最多的莫过于用户空间的任务,像用户线程或用户进程,因为他们太活跃了,也太耀眼了以至于我们感受不到内核线程的存在,但是内核线程却在背后默默地付出着,如内存回收,脏页回写,处理大量的软中断等,如果没有内核线程那么linux世界是那么的可怕!本文力求与完整介绍完内核线程的整个生命周期,如内核线程的创建、调度等等,当然本文还是主要从内存管理和进程调度两个维度来解析,且不会涉及到具体的内核线程如kswapd的实现,最后我们会以一个简单的内核模块来说明如何在驱动代码中来创建使用内核线程。
用户7244416
2021/08/06
2.5K0
通过fork来剖析Linux内核的内存管理和进程管理(下)
上一篇文章我们讲到fork的时候内存管理相关的内容,时间大概隔了快一周了,发布下篇文章,写文章确实费时费力,需要仔细推敲,原创不易,希望大家多多支持吧。本文讲解fork的时候进程管理相关的内容,主要讲解fork的时候进程如何组装调用相关的基础设施组件,以及如何加入运行队列的,调度执行的时候究竟会发生什么。
刘盼
2021/03/23
1.7K0
【Linux 内核】进程管理 ( 进程相关系统调用源码分析 | fork() 源码 | vfork() 源码 | clone() 源码 | _do_fork() 源码 | do_fork() 源码 )
Linux 进程相关 " 系统调用 " 对应的源码在 linux-5.6.18\kernel\fork.c 源码中 , 下面开始对该源码的相关 " 系统调用 " 进行分析 ;
韩曙亮
2023/03/30
4.8K0
【Linux 内核】进程管理 ( 进程相关系统调用源码分析 | fork() 源码 | vfork() 源码 | clone() 源码 | _do_fork() 源码 | do_fork() 源码 )
你需要了解的 Linux 进程管理
对于一个操作系统来说,提供运行程序的能力是其本质,而在 Linux 中,轻量、相应快速的进程管理也是其优良特性之一。我会分两篇文章介绍 Linux 进程。这是第一篇,重点在于 Linux 进程的描述和生命周期,下一篇将介绍 Linux 下的进程调度。
CS实验室
2021/03/22
6100
你需要了解的 Linux 进程管理
相关推荐
Linux下进程的创建过程分析(_do_fork do_fork详解)--Linux进程的管理与调度(八)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文