Linux系统创建一个新进程(下)

浏览创建进程的相关关键代码

看一下do_fork      /linux-3.18.6/kernel/fork.c#do_fork

1651	p = copy_process(clone_flags, stack_start, stack_size,      // 创建进程的主要代码
1652			 child_tidptr, NULL, trace);

看一下copye_process      /linux-3.18.6/kernel/fork.c#copy_process

1240	p = dup_task_struct(current);      // 复制PCB

看一下dup_task_struct      /linux-3.18.6/kernel/fork.c#dup_task_struct

320	err = arch_dup_task_struct(tsk, orig);      // 执行复制,orig 当前进程
316	ti = alloc_thread_info_node(tsk, node);      // 实际就是alloc一个内核堆栈

324 tsk->stack = ti; // 把alloc后返回的地址赋给stack

看一下arch_dup_task_struct     /linux-3.18.6/kernel/fork.c#arch_dup_task_struct

290int __weak arch_dup_task_struct(struct task_struct *dst,
291					       struct task_struct *src)
292{
293	*dst = *src;      // 就是把数据结构加*,原来它是数据结构的指针,加*,表示它的值
294	return 0;
295}

看一下alloc_thread_info_node     /linux-3.18.6/kernel/fork.c#alloc_thread_info_node

150static struct thread_info *alloc_thread_info_node(struct task_struct *tsk,
151						  int node)
152{
153	struct page *page = alloc_kmem_pages_node(node, THREADINFO_GFP,
154						  THREAD_SIZE_ORDER);
155
156	return page ? page_address(page) : NULL;
157}

做了实际分配内核堆栈空间的效果,实际的代码是alloc_kmem_pages_node,创建了一定大小的页面,页面有一部分用来存放thread_info,另一部分从高地址向低地址就是内核堆栈

回到dup_task_struct,现在已经把父进程的PCB,也就是task_struct数据结构复制过来了,也就是由p所指向的子进程的PCB(进程描述符)

1240	p = dup_task_struct(current);      // 复制PCB

往后的代码,有大量地修改子进程内容的代码,做初始化,这些都可以抽象掉

1375	retval = copy_files(clone_flags, p);

1378	retval = copy_fs(clone_flags, p);      // 初始化文件系统

1381	retval = copy_sighand(clone_flags, p);

1384	retval = copy_signal(clone_flags, p);      // 初始化信号

1387	retval = copy_mm(clone_flags, p);      // 初始化内存

1390	retval = copy_namespaces(clone_flags, p);

1393	retval = copy_io(clone_flags, p);      // 初始化IO

1396 retval = copy_thread(clone_flags, stack_start, stack_size, p); // 关键的内容

看一下copy_thread    /linux-3.18.6/arch/x86/kernel/process_32.c#132

135	struct pt_regs *childregs = task_pt_regs(p);

从这里可以看到,从子进程的pid,也就是内核堆栈的位置,找到了栈空间,SAVE_ALL的一些内容,SAVE_ALL的地址

139	p->thread.sp = (unsigned long) childregs;      // 调度到子进程时的内核栈底

把栈底赋上

拷贝内核堆栈数据和指定新进程的第一条指令地址

159	*childregs = *current_pt_regs();      // 复制内核堆栈

当前进程,也就是父进程,因为我们这个执行过程还在父进程的执行上下文当中。父进程的内核堆栈的栈底,也就是SAVE_ALL的内容,把它拷贝过来,这个地方实际就是做内核堆栈里已有数据的拷贝

值得注意的是:在复制内核堆栈的时候,只复制了与SAVE_ALL相关的那一部分,只复制了struct pt_regs

看一下struct pt_regs数据结构的内容    /linux-3.18.6/arch/x86/include/asm/ptrace.h

9#ifdef __i386__
10
11struct pt_regs {

// SAVE_ALL压到内核堆栈里的内容
12 unsigned long bx; 13 unsigned long cx; 14 unsigned long dx; 15 unsigned long si; 16 unsigned long di; 17 unsigned long bp; 18 unsigned long ax; // 传递的系统调用号 19 unsigned long ds; 20 unsigned long es; 21 unsigned long fs; 22 unsigned long gs; 23 unsigned long orig_ax; // 原来的eax
// 执行int 0x80指令的时候,CPU自动压到内核堆栈里面的内容
24 unsigned long ip; 25 unsigned long cs; 26 unsigned long flags; 27 unsigned long sp; 28 unsigned long ss; 29}; 30 31#else /* __i386__ */

在复制内核堆栈的时候,i386只复制了内核堆栈最栈底的那一部分内容,也就是系统调用压栈的过程,int 0x80指令(CPU自动)和SAVE_ALL压到内核堆栈里的内容

160	childregs->ax = 0;      // 为什么子进程的fork返回0,这里就是原因!

返回值存放在eax,pid=0就是在这赋值的。因为子进程的返回值是0,所以拷贝完还需要修改一下内核堆栈里压入的返回值

161	if (sp)
162		childregs->sp = sp;      // sp是传递给copy_thread的第二个参数stack_start

包括栈底的数据

164	p->thread.ip = (unsigned long) ret_from_fork;      // 调度到子进程时的第一条指令地址

赋值thread.ip的内容为ret_from_fork,子进程得到进程调度,得到CPU的时候,是从这个位置开始执行的

看一下entry_32.S    /linux-3.18.6/arch/x86/kernel/entry_32.S

系统调用总控程序,找到ret_from_fork

290ENTRY(ret_from_fork)
291	CFI_STARTPROC
292	pushl_cfi %eax
293	call schedule_tail
294	GET_THREAD_INFO(%ebp)
295	popl_cfi %eax
296	pushl_cfi $0x0202		# Reset kernel eflags
297	popfl_cfi
298	jmp syscall_exit		// 在这里会跳转到syscall_exit
299	CFI_ENDPROC
300END(ret_from_fork)

syscall_exit 在哪个地方呢?

490ENTRY(system_call)

493	pushl_cfi %eax			# save orig_eax
494	SAVE_ALL			// 这里进行SAVE_ALL

501syscall_call:			// 这里进行system_call
502	call *sys_call_table(,%eax,4)
503syscall_after_call: // 这里call返回了,返回到内核堆栈 // 也就是内核堆栈怎么压栈,它就又怎么出来了
504 movl %eax,PT_EAX(%esp) # store the return value 505syscall_exit:

call返回,返回到内核堆栈,也就是内核堆栈怎么压栈,它就又怎么出来了,所以到syscall_exit的时候,实际上和sys_call之前它的堆栈状态是一样的

所以ret_from_fork跳到syscall_exit来,就可以继续往下执行,就可以正常地返回到用户态

也就是说当子进程获得CPU控制权,开始运行的时候,它的ret_from_fork可以把后面的堆栈出栈出栈,从iret返回到用户态,这时候返回到用户态,就不是原来父进程的进程空间了,而是子进程的进程空间了


(下篇完)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,482评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,377评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,762评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,273评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,289评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,046评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,351评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,988评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,476评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,948评论 2 324
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,064评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,712评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,261评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,264评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,486评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,511评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,802评论 2 345

推荐阅读更多精彩内容

  • 此文仅用于MOOCLinux内核分析作业张依依+原创作品转载请注明出处+《Linux内核分析》MOOC课程http...
    uglyyouth阅读 2,266评论 0 4
  • 进程是怎么描述的?这是一个提纲挈领性的东西,它可以把内存管理,文件系统,信号,进程间通信等等全都串联起来 进程的描...
    那只大象阅读 3,581评论 0 6
  • 又来到了一个老生常谈的问题,应用层软件开发的程序员要不要了解和深入学习操作系统呢? 今天就这个问题开始,来谈谈操...
    tangsl阅读 4,088评论 0 23
  • 进程的描述 进程控制块PCB -- task_struct 操作系统的三大核心功能:1、进程管理2、内存管理3、文...
    _Iridescent阅读 1,974评论 0 1
  • 我相信爱是 萤火般的光芒 在黑夜里 迷路的人 才能找到方向 握住希望 把星星都装进行囊 就不会害怕 孤单流浪 我相...
    西域狂龙阅读 191评论 0 0