68.html
来自「linux 0.11中文版 有注释」· HTML 代码 · 共 162 行
HTML
162 行
<html>
<head>
<title>kernel/fork.c</title>
<meta name='robots' content='noindex,nofollow'>
<meta name='generator' content='GLOBAL-5.4.1'>
</head>
<body text='#191970' bgcolor='#f5f5dc' vlink='gray'>
<a name='TOP'><h2><a href='../mains.html'>root</a>/<a href='../files/104.html'>kernel</a>/fork.c</h2>
<i><font color='green'>/* [<][>]<a href='#L34'>[^]</a><a href='#L176'>[v]</a>[top]<a href='#BOTTOM'>[bottom]</a><a href='../mains.html'>[index]</a><a href='../help.html'>[help]</a> */</font></i>
<hr>
<h2>DEFINITIONS</h2>
This source file includes following definitions.
<ol>
<li><a href='#L34' title='Defined at 34.'>verify_area</a>
<li><a href='#L56' title='Defined at 56.'>copy_mem</a>
<li><a href='#L92' title='Defined at 92.'>copy_process</a>
<li><a href='#L176' title='Defined at 176.'>find_empty_process</a>
</ol>
<hr>
<pre>
<a name='L1'><i><font color='green'>/*</font></i>
<a name='L2'><i><font color='green'>* linux/kernel/fork.c</font></i>
<a name='L3'><i><font color='green'>*</font></i>
<a name='L4'><i><font color='green'>* (C) 1991 Linus Torvalds</font></i>
<a name='L5'><i><font color='green'>*/</font></i>
<a name='L6'>
<a name='L7'><i><font color='green'>/*</font></i>
<a name='L8'><i><font color='green'>* 'fork.c' contains the help-routines for the 'fork' system call</font></i>
<a name='L9'><i><font color='green'>* (see also system_call.s), and some misc functions ('verify_area').</font></i>
<a name='L10'><i><font color='green'>* Fork is rather simple, once you get the hang of it, but the memory</font></i>
<a name='L11'><i><font color='green'>* management can be a bitch. See 'mm/mm.c': 'copy_page_tables()'</font></i>
<a name='L12'><i><font color='green'>*/</font></i>
<a name='L13'><i><font color='green'>/*</font></i>
<a name='L14'><i><font color='green'>* 'fork.c'中含有系统调用'fork'的辅助子程序(参见system_call.s),以及一些其它函数</font></i>
<a name='L15'><i><font color='green'>* ('verify_area')。一旦你了解了fork,就会发现它是非常简单的,但内存管理却有些难度。</font></i>
<a name='L16'><i><font color='green'>* 参见'mm/mm.c'中的'copy_page_tables()'。</font></i>
<a name='L17'><i><font color='green'>*/</font></i>
<a name='L18'><font color='darkred'>#include</font> <<a href='28.html'>errno.h</a>> <i><font color='green'>// 错误号头文件。包含系统中各种出错号。(Linus 从minix 中引进的)。</font></i>
<a name='L19'>
<a name='L20'><font color='darkred'>#include</font> <<a href='36.html'>linux/sched.h</a>> <i><font color='green'>// 调度程序头文件,定义了任务结构task_struct、初始任务0 的数据,</font></i>
<a name='L21'><i><font color='green'>// 还有一些有关描述符参数设置和获取的嵌入式汇编函数宏语句。</font></i>
<a name='L22'><font color='darkred'>#include</font> <<a href='34.html'>linux/kernel.h</a>> <i><font color='green'>// 内核头文件。含有一些内核常用函数的原形定义。</font></i>
<a name='L23'><font color='darkred'>#include</font> <<a href='24.html'>asm/segment.h</a>> <i><font color='green'>// 段操作头文件。定义了有关段寄存器操作的嵌入式汇编函数。</font></i>
<a name='L24'><font color='darkred'>#include</font> <<a href='25.html'>asm/system.h</a>> <i><font color='green'>// 系统头文件。定义了设置或修改描述符/中断门等的嵌入式汇编宏。</font></i>
<a name='L25'>
<a name='L26'><b>extern</b> <b>void</b> <a href='../S/94.html#L414' title='Defined at 414 in mm/memory.c.'>write_verify</a> (<b>unsigned</b> <b>long</b> address);
<a name='L27'>
<a name='L28'><b>long</b> last_pid = 0;
<a name='L29'>
<a name='L30'><i><font color='green'>//// 进程空间区域写前验证函数。</font></i>
<a name='L31'><i><font color='green'>// 对当前进程的地址addr 到addr+size 这一段进程空间以页为单位执行写操作前的检测操作。</font></i>
<a name='L32'><i><font color='green'>// 若页面是只读的,则执行共享检验和复制页面操作(写时复制)。</font></i>
<a name='L33'><b>void</b>
<a name='L34'><a href='../R/726.html' title='Multiple refered from 14 places.'>verify_area</a> (<b>void</b> *addr, <b>int</b> size)
<a name='L35'><font color='red'>{</font>
<a name='L36'> <b>unsigned</b> <b>long</b> start;
<a name='L37'>
<a name='L38'> start = (<b>unsigned</b> <b>long</b>) addr;
<a name='L39'><i><font color='green'>// 将起始地址start 调整为其所在页的左边界开始位置,同时相应地调整验证区域大小。</font></i>
<a name='L40'><i><font color='green'>// 此时start 是当前进程空间中的线性地址。</font></i>
<a name='L41'> size += start & 0xfff;
<a name='L42'> start &= 0xfffff000;
<a name='L43'> start += <a href='../S/36.html#L343' title='Defined at 343 in include/linux/sched.h.'>get_base</a> (current->ldt[2]); <i><font color='green'>// 此时start 变成系统整个线性空间中的地址位置。</font></i>
<a name='L44'> <b>while</b> (size > 0)
<a name='L45'> <font color='red'>{</font>
<a name='L46'> size -= 4096;
<a name='L47'><i><font color='green'>// 写页面验证。若页面不可写,则复制页面。(mm/memory.c,261 行)</font></i>
<a name='L48'> <a href='../S/94.html#L414' title='Defined at 414 in mm/memory.c.'>write_verify</a> (start);
<a name='L49'> start += 4096;
<a name='L50'> <font color='red'>}</font>
<a name='L51'><font color='red'>}</font>
<a name='L52'>
<a name='L53'><i><font color='green'>// 设置新任务的代码和数据段基址、限长并复制页表。</font></i>
<a name='L54'><i><font color='green'>// nr 为新任务号;p 是新任务数据结构的指针。</font></i>
<a name='L55'><b>int</b>
<a name='L56'><a href='../S/68.html#L148' title='Refered from 148 in kernel/fork.c.'>copy_mem</a> (<b>int</b> nr, <b>struct</b> task_struct *p)
<a name='L57'><font color='red'>{</font>
<a name='L58'> <b>unsigned</b> <b>long</b> old_data_base, new_data_base, data_limit;
<a name='L59'> <b>unsigned</b> <b>long</b> old_code_base, new_code_base, code_limit;
<a name='L60'>
<a name='L61'> code_limit = <a href='../S/36.html#L346' title='Defined at 346 in include/linux/sched.h.'>get_limit</a> (0x0f); <i><font color='green'>// 取局部描述符表中代码段描述符项中段限长。</font></i>
<a name='L62'> data_limit = <a href='../S/36.html#L346' title='Defined at 346 in include/linux/sched.h.'>get_limit</a> (0x17); <i><font color='green'>// 取局部描述符表中数据段描述符项中段限长。</font></i>
<a name='L63'> old_code_base = <a href='../S/36.html#L343' title='Defined at 343 in include/linux/sched.h.'>get_base</a> (current->ldt[1]); <i><font color='green'>// 取原代码段基址。</font></i>
<a name='L64'> old_data_base = <a href='../S/36.html#L343' title='Defined at 343 in include/linux/sched.h.'>get_base</a> (current->ldt[2]); <i><font color='green'>// 取原数据段基址。</font></i>
<a name='L65'> <b>if</b> (old_data_base != old_code_base) <i><font color='green'>// 0.11 版不支持代码和数据段分立的情况。</font></i>
<a name='L66'> <a href='../S/72.html#L24' title='Defined at 24 in kernel/panic.c.'>panic</a> ("We don't support separate I&D");
<a name='L67'> <b>if</b> (data_limit < code_limit) <i><font color='green'>// 如果数据段长度 < 代码段长度也不对。</font></i>
<a name='L68'> <a href='../S/72.html#L24' title='Defined at 24 in kernel/panic.c.'>panic</a> ("Bad data_limit");
<a name='L69'> new_data_base = new_code_base = nr * 0x4000000; <i><font color='green'>// 新基址=任务号*64Mb(任务大小)。</font></i>
<a name='L70'> p->start_code = new_code_base;
<a name='L71'> <a href='../S/36.html#L325' title='Defined at 325 in include/linux/sched.h.'>set_base</a> (p->ldt[1], new_code_base); <i><font color='green'>// 设置代码段描述符中基址域。</font></i>
<a name='L72'> <a href='../S/36.html#L325' title='Defined at 325 in include/linux/sched.h.'>set_base</a> (p->ldt[2], new_data_base); <i><font color='green'>// 设置数据段描述符中基址域。</font></i>
<a name='L73'> <b>if</b> (<a href='../S/94.html#L230' title='Defined at 230 in mm/memory.c.'>copy_page_tables</a> (old_data_base, new_data_base, data_limit))
<a name='L74'> <font color='red'>{</font> <i><font color='green'>// 复制代码和数据段。</font></i>
<a name='L75'> <a href='../S/94.html#L158' title='Defined at 158 in mm/memory.c.'>free_page_tables</a> (new_data_base, data_limit); <i><font color='green'>// 如果出错则释放申请的内存。</font></i>
<a name='L76'> <b>return</b> -<a href='../S/28.html#L41' title='Defined at 41 in include/errno.h.'>ENOMEM</a>;
<a name='L77'> <font color='red'>}</font>
<a name='L78'> <b>return</b> 0;
<a name='L79'><font color='red'>}</font>
<a name='L80'>
<a name='L81'><i><font color='green'>/*</font></i>
<a name='L82'><i><font color='green'>* Ok, this is the main fork-routine. It copies the system process</font></i>
<a name='L83'><i><font color='green'>* information (task[nr]) and sets up the necessary registers. It</font></i>
<a name='L84'><i><font color='green'>* also copies the data segment in it's entirety.</font></i>
<a name='L85'><i><font color='green'>*/</font></i>
<a name='L86'><i><font color='green'>/*</font></i>
<a name='L87'><i><font color='green'>* OK,下面是主要的fork 子程序。它复制系统进程信息(task[n])并且设置必要的寄存器。</font></i>
<a name='L88'><i><font color='green'>* 它还整个地复制数据段。</font></i>
<a name='L89'><i><font color='green'>*/</font></i>
<a name='L90'><i><font color='green'>// 复制进程。</font></i>
<a name='L91'><b>int</b>
<a name='L92'><a href='../S/77.html#L280' title='Refered from 280 in kernel/system_call.s.'>copy_process</a> (<b>int</b> nr, <b>long</b> ebp, <b>long</b> edi, <b>long</b> esi, <b>long</b> gs, <b>long</b> none,
<a name='L93'> <b>long</b> ebx, <b>long</b> ecx, <b>long</b> edx,
<a name='L94'> <b>long</b> fs, <b>long</b> es, <b>long</b> ds,
<a name='L95'> <b>long</b> eip, <b>long</b> cs, <b>long</b> eflags, <b>long</b> esp, <b>long</b> ss)
<a name='L96'><font color='red'>{</font>
<a name='L97'> <b>struct</b> task_struct *p;
<a name='L98'> <b>int</b> <a href='../D/839.html' title='Multiple defined in 4 places.'>i</a>;
<a name='L99'> <b>struct</b> file *f;
<a name='L100'>
<a name='L101'> p = (<b>struct</b> task_struct *) <a href='../S/94.html#L101' title='Defined at 101 in mm/memory.c.'>get_free_page</a> (); <i><font color='green'>// 为新任务数据结构分配内存。</font></i>
<a name='L102'> <b>if</b> (!p) <i><font color='green'>// 如果内存分配出错,则返回出错码并退出。</font></i>
<a name='L103'> <b>return</b> -<a href='../S/28.html#L40' title='Defined at 40 in include/errno.h.'>EAGAIN</a>;
<a name='L104'> task[nr] = p; <i><font color='green'>// 将新任务结构指针放入任务数组中。</font></i>
<a name='L105'><i><font color='green'>// 其中nr 为任务号,由前面find_empty_process()返回。</font></i>
<a name='L106'> *p = *current; <i><font color='green'>/* NOTE! this doesn't copy the supervisor stack */</font></i>
<a name='L107'><i><font color='green'>/* 注意!这样做不会复制超级用户的堆栈 */</font></i> (只复制当前进程内容)。
<a name='L108'> p->state = <a href='../S/36.html#L22' title='Defined at 22 in include/linux/sched.h.'>TASK_UNINTERRUPTIBLE</a>; <i><font color='green'>// 将新进程的状态先置为不可中断等待状态。</font></i>
<a name='L109'> p->pid = last_pid; <i><font color='green'>// 新进程号。由前面调用find_empty_process()得到。</font></i>
<a name='L110'> p->father = current->pid; <i><font color='green'>// 设置父进程号。</font></i>
<a name='L111'> p->counter = p->priority;
<a name='L112'> p->signal = 0; <i><font color='green'>// 信号位图置0。</font></i>
<a name='L113'> p->alarm = 0;
<a name='L114'> p->leader = 0; <i><font color='green'>/* process leadership doesn't inherit */</font></i>
<a name='L115'><i><font color='green'>/* 进程的领导权是不能继承的 */</font></i>
<a name='L116'> p->utime = p->stime = 0; <i><font color='green'>// 初始化用户态时间和核心态时间。</font></i>
<a name='L117'> p->cutime = p->cstime = 0; <i><font color='green'>// 初始化子进程用户态和核心态时间。</font></i>
<a name='L118'> p->start_time = jiffies; <i><font color='green'>// 当前滴答数时间。</font></i>
<a name='L119'><i><font color='green'>// 以下设置任务状态段TSS 所需的数据(参见列表后说明)。</font></i>
<a name='L120'> p->tss.back_link = 0;
<a name='L121'> p->tss.esp0 = <a href='../D/324.html' title='Multiple defined in 4 places.'>PAGE_SIZE</a> + (<b>long</b>) p; <i><font color='green'>// 堆栈指针(由于是给任务结构p 分配了1 页</font></i>
<a name='L122'><i><font color='green'>// 新内存,所以此时esp0 正好指向该页顶端)。</font></i>
<a name='L123'> p->tss.ss0 = 0x10; <i><font color='green'>// 堆栈段选择符(内核数据段)[??]。</font></i>
<a name='L124'> p->tss.eip = eip; <i><font color='green'>// 指令代码指针。</font></i>
<a name='L125'> p->tss.eflags = eflags; <i><font color='green'>// 标志寄存器。</font></i>
<a name='L126'> p->tss.eax = 0;
<a name='L127'> p->tss.ecx = ecx;
<a name='L128'> p->tss.edx = edx;
<a name='L129'> p->tss.ebx = ebx;
<a name='L130'> p->tss.esp = esp;
<a name='L131'> p->tss.ebp = ebp;
<a name='L132'> p->tss.esi = esi;
<a name='L133'> p->tss.edi = edi;
<a name='L134'> p->tss.es = es & 0xffff; <i><font color='green'>// 段寄存器仅16 位有效。</font></i>
<a name='L135'> p->tss.cs = cs & 0xffff;
<a name='L136'> p->tss.ss = ss & 0xffff;
<a name='L137'> p->tss.ds = ds & 0xffff;
<a name='L138'> p->tss.fs = fs & 0xffff;
<a name='L139'> p->tss.gs = gs & 0xffff;
<a name='L140'> p->tss.ldt = <a href='../S/36.html#L260' title='Defined at 260 in include/linux/sched.h.'>_LDT</a> (nr); <i><font color='green'>// 该新任务nr 的局部描述符表选择符(LDT 的描述符在GDT 中)。</font></i>
<a name='L141'> p->tss.trace_bitmap = 0x80000000;
<a name='L142'> (高16
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?