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'>/* [&lt;][&gt;]<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> &lt;<a href='28.html'>errno.h</a>&gt;              <i><font color='green'>// 错误号头文件。包含系统中各种出错号。(Linus 从minix 中引进的)。</font></i>
<a name='L19'>
<a name='L20'><font color='darkred'>#include</font> &lt;<a href='36.html'>linux/sched.h</a>&gt;        <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> &lt;<a href='34.html'>linux/kernel.h</a>&gt;       <i><font color='green'>// 内核头文件。含有一些内核常用函数的原形定义。</font></i>
<a name='L23'><font color='darkred'>#include</font> &lt;<a href='24.html'>asm/segment.h</a>&gt;        <i><font color='green'>// 段操作头文件。定义了有关段寄存器操作的嵌入式汇编函数。</font></i>
<a name='L24'><font color='darkred'>#include</font> &lt;<a href='25.html'>asm/system.h</a>&gt;         <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 &amp; 0xfff;
<a name='L42'>  start &amp;= 0xfffff000;
<a name='L43'>  start += <a href='../S/36.html#L343' title='Defined at 343 in include/linux/sched.h.'>get_base</a> (current-&gt;ldt[2]);  <i><font color='green'>// 此时start 变成系统整个线性空间中的地址位置。</font></i>
<a name='L44'>  <b>while</b> (size &gt; 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-&gt;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-&gt;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&amp;D");
<a name='L67'>  <b>if</b> (data_limit &lt; code_limit)  <i><font color='green'>// 如果数据段长度 &lt; 代码段长度也不对。</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-&gt;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-&gt;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-&gt;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-&gt;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-&gt;pid = last_pid;            <i><font color='green'>// 新进程号。由前面调用find_empty_process()得到。</font></i>
<a name='L110'>  p-&gt;father = current-&gt;pid;     <i><font color='green'>// 设置父进程号。</font></i>
<a name='L111'>  p-&gt;counter = p-&gt;priority;
<a name='L112'>  p-&gt;signal = 0;                <i><font color='green'>// 信号位图置0。</font></i>
<a name='L113'>  p-&gt;alarm = 0;
<a name='L114'>  p-&gt;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-&gt;utime = p-&gt;stime = 0;      <i><font color='green'>// 初始化用户态时间和核心态时间。</font></i>
<a name='L117'>  p-&gt;cutime = p-&gt;cstime = 0;    <i><font color='green'>// 初始化子进程用户态和核心态时间。</font></i>
<a name='L118'>  p-&gt;start_time = jiffies;      <i><font color='green'>// 当前滴答数时间。</font></i>
<a name='L119'><i><font color='green'>// 以下设置任务状态段TSS 所需的数据(参见列表后说明)。</font></i>
<a name='L120'>  p-&gt;tss.back_link = 0;
<a name='L121'>  p-&gt;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-&gt;tss.ss0 = 0x10;            <i><font color='green'>// 堆栈段选择符(内核数据段)[??]。</font></i>
<a name='L124'>  p-&gt;tss.eip = eip;             <i><font color='green'>// 指令代码指针。</font></i>
<a name='L125'>  p-&gt;tss.eflags = eflags;       <i><font color='green'>// 标志寄存器。</font></i>
<a name='L126'>  p-&gt;tss.eax = 0;
<a name='L127'>  p-&gt;tss.ecx = ecx;
<a name='L128'>  p-&gt;tss.edx = edx;
<a name='L129'>  p-&gt;tss.ebx = ebx;
<a name='L130'>  p-&gt;tss.esp = esp;
<a name='L131'>  p-&gt;tss.ebp = ebp;
<a name='L132'>  p-&gt;tss.esi = esi;
<a name='L133'>  p-&gt;tss.edi = edi;
<a name='L134'>  p-&gt;tss.es = es &amp; 0xffff;      <i><font color='green'>// 段寄存器仅16 位有效。</font></i>
<a name='L135'>  p-&gt;tss.cs = cs &amp; 0xffff;
<a name='L136'>  p-&gt;tss.ss = ss &amp; 0xffff;
<a name='L137'>  p-&gt;tss.ds = ds &amp; 0xffff;
<a name='L138'>  p-&gt;tss.fs = fs &amp; 0xffff;
<a name='L139'>  p-&gt;tss.gs = gs &amp; 0xffff;
<a name='L140'>  p-&gt;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-&gt;tss.trace_bitmap = 0x80000000;
<a name='L142'>  (高16 

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?