红联Linux门户
Linux帮助

我自己对Linux源代码的注释

发布时间:2006-05-16 00:49:20来源:红联作者:火
转贴:

接触Linux内核已有一段时间了,算是对操作系统有了那吗一点点的认识,今天突然心血来潮,想把自己学的一点东西写出来.于是便来到了论坛,小可学艺未精,不当之处还请包涵.
下面先说一下操作系统存在的基础或理由:
计算机中最重要和最智能化的硬件部件是中央处理器(CPU),为什么这么说呢?原因是CPU提供了一套最基本的指令集,这个东西(指令集)是计算机能够进行数据处理的基础.举个例子,当你进行c语言编程时有一条最简单的赋值语句x=3,它是怎么被计算机执行的呢? 在计算机的CPU内部他是把3这个数送入x所在的内存单元,其中最重要的”送”这个命令是谁发出的呢?就是CPU的电子脉冲,CPU里面定义了很多这样的电子脉冲,比如'加'指令的电子脉冲,”跳转”指令(实现循环)的电子脉冲等.而这些电子脉冲合起来就是所谓的CPU指令集.它定义了计算机内部最基本的运算方式,计算机的所有的算法最后都被分解成这些最基本的运算方式,就像是数学上不管怎么难的算式最后都分解成加法,减法,乘法,除法等最基本的算式一样.
利用这些指令集定义的最基本的算法构建的更为复杂的算式就是所谓的计算机软件,
操作系统以是一种软件,以是一大堆最基本的指令集的组合.只不过它是管理其它软件的软件,如果把操作系统看比做物理上的公式的话,那么一般的应用软件就好像是套用这公式的数据一样.现在应该知道操作系统存在的基础和理由了吧!
下面就开始说操作系统的部分了:
操作系统是由一大堆基本指令组成的更为复杂的程序,一般操作系统都是用c语言的编写的,c语言编写的操作系统在安装是被编译器编译成二进制代码存放在计算机的硬盘当中,
计算机打开电源的时候被读入内存,然后就由操作系统利用自己的程序控制整个计算机的软件和硬件.
操作系统是怎么控制整个计算机的软硬件的呢,当然是通过它自己的程序来控制计算机的,现在讨论的问题是操作系统通过什么样的程序来控制计算机的.
总的来说操作系统分为五大部分:
进程管理,内存管理,文件管理,网络管理,设备管理.
下面就以这五部分为总纲,我有一一道来:



进程管理:
什么是进程,
大家都用过计算机,知道什么是计算机程序.我们说写在纸上或者存储在计算机的硬盘上的一段代码就是程序.那什么是进程呢?我们说在计算机上运行的程序就是进程.
那进程和程序和什么区别呢?程序是写在纸上或者是存储在计算机硬盘上一段”死”的代码,当这段代码进入计算机内存被计算机执行的时候就变成了进程,进程是”活”的东西.因为进程是在计算机中被执行的程序,所以它比程序要多考虑的东西就是计算机内部的运行环境.
在操作系统中进程和任务是同一个概念.我们用的操作系统基本上都是多任务操作系统(window/Linux在同一时间有多个进程运行),那么这么多进程是怎么同时在CPU上运行的哪?
这就是进程管理要解决的问题.
在计算机中要执行一个程序,首先将程序的代码从硬盘读入到内存,然后CPU根据程序代码指令一条一条的执行,而这时候的程序就叫进程,计算机中同时存在多个进程在等待被执行,比如你在用window/Linux上网的时候,你一边用QQ和朋友聊天,一边在用MP3播放器听音乐,一边在浏览网页.那么这时候在计算机中就有三个进程在同时运行,一是QQ进程,二是MP3播放器进程,三是浏览器进程.但是计算机中只有一个CPU,在一个时间点上,它只能运行一个进程的指令,比如在2005/11/29/日 08/23/42/秒时计算机的CPU执行的是QQ进程的一条加法(mov)指令.那么计算机是怎样让三个进程同时运行起来的呢(至少我们看来是这样).
在计算机内部所有的正在运行的进程都在等待被CPU执行,操作系统按一定的规律让所有的进程都得到CPU的执行.方法是一会儿让QQ进程执行,过了一定时间后又让MP3播放器进程得到CPU的执行,再过一定时间后让浏览器进程得到CPU的执行.相对于人的反应,CPU的执行速度太快,所以人根本感觉不到这些进程的切换过程,也不会对人的操作产生影响
现在的问题是操作系统是按照什么样的规律给个各进程分配CPU资源的.操作系统是按照个各进程的属性来分配CPU资源的.那进程的属性在什么呢?当操作系统创建一个进程时都就为这个进程分配了一定的内存空间来存放它的属性.在Linux操作系统中,操作系统定义了一个数据结构task_struct来描述各个进程的进程属性,task_struct结构叫进程控制块(PCB), 它是操作系统中重要的数据结构之一.具体定义在/include/linux/sched.h中,注意在Linux操作系统中所有的操作系统源代码都在/usr/src目录下.
task_struct结构的代码如下所示:
(这里以2.40内核为准,可能不同版本的内核代码会有一点点的不同)
(为了保持简洁,从宏关上把握,不拘泥于具体,这里只说一部分,其它的以后会讲到,这里只说功能)

____________________________________________________/include/linux/sched.h

struct task_struct {

volatile long state; /* 进程的状态,在代码后面有说明*/
unsigned long flags; /* 进程标志 */
int sigpending;
mm_segment_t addr_limit; /* 线性地址空间:
0-0xBFFFFFFF 为用户线性空间地址;
0-0xFFFFFFFF 为内核线性空间地址 */

struct exec_domain *exec_domain;
volatile long need_resched;
unsigned long ptrace;

int lock_depth;

long counter; /* 进程的动态优先级,在代码后面有说明 */
long nice; /* 进程的静态优先级,在代码后面有说明 */
unsigned long policy; /* 进程采用的调度策略,在代码后面有说明 */
struct mm_struct *mm; /* 进程属性中指向内存管理的数据结构mm_structd的
指针,在代码后面有说明 */
int has_cpu, processor;
unsigned long cpus_allowed;

struct list_head run_list;
unsigned long sleep_time;

struct task_struct *next_task , *prev_task; /* 所以进程通过这两个指针组成
一个双向链表*/
struct mm_struct *active_mm; /* 指向活动地址空间,在后面的代码有说明 */
/* task state */
struct linux_binfmt *binfmt;
int exit_code, exit_signal;
int pdeath_signal;
unsigned long personality;
int did_exec:1;
int dumpable:1;
pid_t pid; /* 进程标志符,,在代码后面有说明 */
pid_t pgrp; /* 进程组标号,,在代码后面有说明 */
pid_t tty_old_pgrp;
pid_t session;
pid_t tgid;

int leader;

struct task_struct *p_opptr, *p_pptr, *p_cptr, *p_ysptr, *p_osptr;
/* 这五个标志表示一个进程的在计算机中的亲属关系,
分别标志祖先进程,父进程,子进程,弟进程和兄进程,
为了在两个进程之间共享方便而设立 */
struct list_head thread_group;

struct task_struct *pidhist_next;
struct task_struct *pidhist_pprev; /* 上面两个指针是为了在计算机中快速查
一个进程而设立,具体方式以后讲*/
wait_queue_head_t wait_chldexit;
struct completion *vfork_sem;
unsigned long rt_priority; /* 实时进程的优先级标志*/
unsigned long it_real_value, it_prof_value, it_virt_value;
unsigned long it_real_incr, it_prof_incr, it_virt_incr;
struct timer_list real_timer;
struct tms times;
struct tms group_times;
unsigned long start_time;
long per_cpu_utime[NR_CPUS], per_cpu_stime[NR_CPUS];

unsigned long min_flt, maj_flt, nswap, cmin_flt, cmaj_flt, cnswap;
int swappable:1;

uid_t uid,euid,suid,fsuid;
gid_t gid,egid,sgid,fsgid;
int ngroups;
gid_t groups[NGROUPS];
kernel_cap_t cap_effective, cap_inheritable, cap_permitted;
int keep_capabilities:1;
struct user_struct *user;

struct rlimit rlim[RLIM_NLIMITS];
unsigned short used_math;
char comm[16];

int link_count;
struct tty_struct *tty; /* NULL if no tty */
unsigned int locks; /* How many file locks are being held */
struct sem_undo *semundo;
struct sem_queue *semsleeping;

struct thread_struct thread;

struct fs_struct *fs; /* 进程属性中指向和文件管理有关的数据结构*/

struct files_struct *files;

spinlock_t sigmask_lock;
struct signal_struct *sig;

sigset_t blocked;
struct sigpending pending;

unsigned long sas_ss_sp;
size_t sas_ss_size;
int (*notifier)(void *priv);
void *notifier_data;
sigset_t *notifier_mask;


u32 parent_exec_id;
u32 self_exec_id;

spinlock_t alloc_lock;

spinlock_t switch_lock;
};

____________________________________________________/include/linux/sched.h

程序说明如下:

volatile long state 定义了进程的状态,进程总共有6种状态标志,分别是:
一. TASK_RUNING. (正在运行状态或者是可运行状态)
二. TASK_INTERRUPTIBLE,(可打断睡眠状态)
三. TASK_UNINTERRUPTIBLE,(不可打断睡眠状态)
四. TASK_ZOMBLE. (僵死状态)
五. TAKS_STOPPED.(暂停状态)
六. 交换状态.
这六种属性标志了此进程现在的状态,各种不同的状态决定了进程什么时候获得CPU而被执行.
正在运行状态是当前正在运行进程的状态.也就是说他的volatile long state被标志成TASK_RUANING.
可运行状态是那些正在等待CPU资源的进程的状态,这些进程在就绪队列run-queqe中.这些进程只要得到CPU在个资源就马上可以被运行.其也被标志也为TASK_RUANING.
可打断睡眠状态是哪些在等待队列中等待除CPU之外的其它资源的进程,只要它们所等待的资源到达就马上可以从等待队列进入就绪队列,其状态标志也
从TASK_INTERRUPTIBLE状态变成TASK_RUANING可运行状态.
不可打断睡眠状态和可睡眠状态类似,区别在于前者可以用信号唤醒,而后者不可以.
僵死状态是标志那些已经运行完毕释放了大部分资源并退出但还没有释放进程控制快task_struct的进程.
暂停状态是那些遇到突发情况或者收到暂停信号而暂时停止运行的进程的状态.

counter是进程的动态优先级,它定义了一个在就绪队列的进程当它得到CPU后可运行的时间,用静态优先级初始化,当然计算机是以时钟中断做为时间的计数器,每发送一个时钟中断,动态优先级上的时间片就减少一个时钟中断的时间,时间片减到0的时候就退出该进程而调度另一个进程获得CPU.

nice是进程的静态优先级,当一个在就绪队列中的进程获得CPU之后,它被赋予此进程可占有CPU的时间.这个时间被称为时间片.

policy是进程的调度策略标志.有三种调度标志:
SCHED_OTHER 普通进程的调度策略,基于优先权的轮转法.
SCHED_FIFO 实时进程的调度策略,基于先进先出的算法.
SCHED_RR 实时进程的调度策略,基于优先权的轮转法.
在linux中有两种进程,一种是普通进程,一种的实时进程,实时进程的优先级总是高于普通进程,也就是说当就绪队列中即有实时进程也有普通进程时,总是先运行实时进程.不同的进程采用不同的调度策略.

rt_priority 实时进程的优先级.

need_resched 标志下一次有调度机会的时候是否调用此进程.如果此进程没有没有被用户关闭或者其代码全被执行完了,在下一次调度机会应该还被调用.如果被用户关闭则直接退出该进程.

mm_struct *mm mm_struct数据结构是描述内存存储信息的数据结构,进程控制块task_struct中用mm指针指想mm_struct数据结构.也就是在进程的属性中通过mm指针来管理起对应的内存区.

mm_struct *active_active 内核线程用来指向调用它的普通进程的内存地址空间.当普通进程在运行时如果发生系统调用,程序就会从用户态转为内核态,内核态中执行的是内核线程,内核线程没有内存空间地址结构mm_struct,当他需要内存空间地址的时候就会调用用户态对应进程的用以空间地址结构mm_struct.内核线程就是就是通过active_mm指针来指向用户态进程的mm_struct结构.

pid_t pid pid是进程标志符,操作系统每创建一个新的进程就要为这个新进程分配一个进程控制快(PCB),那么系统内核是怎样区分这些进程的呢?就是通过进程标志符pid,系统在为新的进程分配进程控制块的候,它不是自己去创建,而是直接从上一个进程中复制它的进程控制块,其中里面的大部分东西保留下来,只做少量的改动,然后它的进程标志符加1赋值给新的进程控制块.

进程控制块的总共有80多项,在这里就不一一介绍了,以后我们在学习过程用到什么属性就介绍什么属性,这样在宏观上把握,不至于被一个小小的进程控制块给挡住了.

接着上面的进程说明,
前面我们说了一下进程的基本概念和它的属性,接下来我们介绍一下在操作系统内部是怎样通过调度函数调度进程的.
我们在使用计算机的时候,可能同时打开了很多程序,比如同时打开IE浏览器,QQ,word文档等,那么这就意味着内核要同时管理多个用户进程.这些进程中每个进程每隔一段时间就要被CPU执行一次,所有的进程都是这样轮流执行,在Linux操作系统中一个进程占有CPU的时间为50ms(也就是5个时间滴答,关于时间滴答的概念后面介绍),这个时间对于人的反应来讲是相当快的,所以人更不感觉不到进程的调度和切换,各个进程也有轻重缓急之分,后面还会说一个goodness函数,这个函数比较简单,主要作用是综合进程属性(进程控制快)
中的各种因素和数据算出每个进程的权值,权值最大的进程就是调度程序(schedule())最应该调用的进程.
在计算机中进程以六种状态存在,这在进程的属性里已经有了介绍,其中存在于就绪队列TASK_RUNNING中处于运行状态的进程是等待计算机中唯一的资源CPU的进程,它们只要一得到CPU就马上可以被执行,那么操作系统是安什么样的规则来让这些进程合理和公平的得到CPU的呢,这就要用到计算机的调度函数schedule(),

_____________________________________________________________/kernel/sched.c
asmlinkage void schedule(void)
{
struct schedule_data *sched_data;
struct task_struct *prev, *next, *p;
struct list_struct *tmp;
int this_cpu,c;

if(!current->active_mm) BUG(); /* current指当前进程,当前进程所指的内存空间
如果为空,那么进程必定有问题,出错返回 */

need_resched_back:
prev=current;
this_cpu=prev->processor;


if(in_interrupt())
goto scheduling_in_interrupt;

/*上面代码的意思是: 将局部变量prev和this_cpu初始化为当前进程和当前
CPU.然后检查schedule()是否中断调用,若是则直接返回,其中goto后面的
转到的地方在本程序中有定义,比如本进程后面的scheduling_in_interrupt在本程序
的最后有定义,意思是: 打印出”Scheduling in interrupt”字符后退出 */

release_kernel_lock(prev,this_cpu);

if(softirq_active(this_cpu) & softirq_mask(this_cpu))
goto handle_softirq; /* 检查是否有软中断请求,软中断softirq将在后
面讲解 */
hand_softirp_back:
sched_data= & aligned_data[this_cpu].schedule_data;
spin_lock_irp(&runqueue_lock);

/* 局部变量sched_data中保存当前进程的数据区,*/
if(prev->policy == SCHED_RR)
goto move_rr_last;
* 当前基于优先权轮转法的调度策略实时进程,如果要调用它,先检查其count
是否0若是,则将其挂到运行队列的最后面.具体做法在后面的 move_rr_last
中有定义 */

move_rr_back:

switch(prev->state)
{
case TESK_INTERRUPTIBLE:
if(sign_pending(prev))
{
prev->state = TASK_RUNNING;
break;
}
default:
del_form_runqueue(prev);
case TASK_RUNNING;
}
prev->need_resched=0;

/* 上面这段代码意思是: 如果进程的状态为可中断睡眠状态且唤醒它的信号正在迫近,
则将其置为可运行状态,如果状态为运行状态则忽略,如果状态除上述两种之外的其
它状态则直接删除.最后将neet_resched置为0,neet_resched置0意味着本能启
动调度函数shchedule(); */

repeat_schedule:
next = idlt_task(this_cpu);
c = -1000;
if(prev->state == TASK_RUNNING)
goto still_running;

/* 上面的代码中的c是优先级运算函数goodness的一个变量,优先级运算函数
goodness将是我们介绍的下一个函数,它的返回值是进程的权值,调度函数
判断绪队列中权值最大的进程就是最优先要被调用的进程.goodness的具体
算法看下一个函数.
上面代码的意思是: 将next设置为当前cpu的idlt_task.将当前优先级的变量
c赋值为-1000,其中-1000是goodness中最小的权值.然后判断当前进程是否
是可运行进程,若是则转到still_running处执行,still_running在本函数的后面
有解释.意思是: 将当前进程的goodness值赋予goodness优先级变量c并返回

still_running_back:
list_for_each(tmp,&runqueue_head)
{
p = list_entry(tmp, struct task_struct, run_list);
if(can_schedule(p,this_cpu))
int weight = goodness(p,this_cpu,prev->active_mm);
if(weight>c)
c=weight,next=p;
}
/* 上面代码的意思是: 遍历整个就绪队列,计算出每一个进程的goodness权值并且
与当前进程做比较,选择goodness权值最大的那个进程获得cpu资源. */

if(! c)
goto recalculate;

/* 上面代码的意思是: 如果当前goodness权值为0,则运行recalculate:,起代码在
后面有说明,意思是:重新计算每一进程的优先权,最后转到repeat_schedule:处执
行,repeat_schedule:部分在前面已经有讲过,自己看有会过头去看一下吧 */

sched_data->curr = next; /* 将当前进程信息保存到局部变量sched_data中*/

#ifdef CONFIG_SMP
next->has_cpu = 1;
next->processor = this_cpu;
/* 意思为: 将调度数据区指针指向下一个要调度的进程并将当前进程的cpu设置为
下一个进程的当前cpu. */

#endif
spin_unlock_irq(&runqueue_lock);

if(prev == next)
goto same_processor;

/* 意思为: 如果当前运行的进程的goodness还是队列中所以进程中最高的,也就是
说下一个要调度的进程还是当前进程.那么只要重新得到cpu控制权返回
need_resched_back:处执行. */

#ifdef CONFIG_SMP
sched_data->last_schedule = get_cycles();
#endif
kstat.context_swtch ++;

prepare_to_switch()
{
struct mm_struct *mm = next->mm;
struct mm_struct *oldmm = prev->active_mm;
if(! mm)
{
if(next->active_mm) BUG();
next->active = oldmm;
atomic_inc(&old_mm->mm_count);
enter_lazy_tlb(oldmm,next,this_cpu);
}
else
{
if(next->active_mm != mm) BUG();
switch_mm(oldmm, mm, next, this_cpu);
}
if(! prev->mm)
{
prev->active_mm = NULL;
mmdrop(oldmm);
}
}
switch_to(prev,next,prev);
__scheduel_tail(prev);

/* 上面的代码是进行进程切换 */

/* 下面的代码是前面代码中goto要转到的地方 */
same_processor:
reacquire_kernel_lock(current);
if(current -> need_resched)
goto need_resched_back;

return;

recalculate:
{
struct task_struct *p;
spin_unlock_irq(&runqueue_lock);
read_lock(&tasklist_lock);
fot_each_task(p)
p->counter = (p->counter >>1) + NICE_TO_TICKS(p_nice);
read_unlock(&tasklist_lock);
spin_lock_irq(&runqueue_lock);
}
goto repeat_schedule;

still_running:
c = goodness(prev, this_cpu, precv->active_mm);
next=prev;
goto still_running_back;

handle_softirq:
do_softirq();
goto handle_softirq_back;

move_rr_last:
if(! prev->counter)
{
prev->counter = NICE_TO_TICKS(prev_nice);
move_last_runqueue(prev);
}
goto move_rr_back;

scheduling_in_interrupt:
printf(“Scheduling in interrupt \n”);
BUG();
return();
}

在上面的我们说了进程调度程序schedule(),接着说一下上面的函数中用到的进程优先级的权值的运行函数googness();
这个函数的作用很简单,就是算出就绪队列中的进程的优先级,然后调度程序调度优先级最高的进程获得当前CPU,
计算机中进程分为两种,一为普通进程,它的优先级用进程控制块中的counter表示,二
为实时进程,实时进程的优先级用rt_priority表示,它在进程控制块中有定义,当就绪队列中同时存在两种进程的时候,实时进程总是先于普通进程运行,它的实现机制是实时进程的权值以1000做为基础值,也就是说实时进程的权值是1000加上它的进程优先级,而普通进程的优先级就是它的进程优先级.
整个函数比较简单,但却是操作系统内核最为频繁调用的函数之一,也直接关系到操作系统的性能.

此函数如下:

______________________________________________________________/kernel/sched.c

static inline int goodness(struct task_struct * p, int this_cpu,
struct mm_struct this_mm)
int weigth;
weight=-1;
/ * 定义函数返回的权值weight并进行初始化 */

/* 下面是普通进程的处理情况 */
if(p->****** & SCHED_YIELD)
{
goto out;
/ * 如果要放弃本次调度,则直接返回, 转到的out处在后面有定义 */

if(p->policy == SCHED_OTHER)
weight = p-> counter;
if(!weight)
goto out;
/* 函数是普通进程的情况,在这种情况下,进程的权值直接为本进程的剩余时间,如果
剩余时间不存在或为0,则直接返回 */

#ifdef CONFIG_SMP
if(p->processor == this_cpu)
weight += PROC_CHANGE_PENALTY;
#endif
if(p->mm == this_mm || !p->mm)
weight +=1;
weight +=20-p->nice;
goto out;
}

/* 下面是实时进程的处理情况 */
weight = 1000 + p->re_priority;
/* 实时进程的权值至少为1000,也就说它的权值是它是基数1000加上实时进程
的优先级,其中实时进程的优先级用re_priority表示,re_priority在进程的属性
中有定义,
out:
return weight;
/* 前面的代码中有调用 */
}
______________________________________________________________/kernel/sched.c

前面我们说了关于进程的概念,属性以及调度它的函数.接下来我们讲一下进程的创建,
进程创建就是当系统接收到一个要求创建进程的信号时,系统为新进程所做的一系列工作.比如当你在使用计算机上网的时候如果打开了QQ聊天的服务程序的话.当你点击QQ图标的时候,系统就接收到了一个创建QQ进程的信号,接下来系统就要做一系列的工作来完成这个过程,这就是我们下面要讲的内容: 基础创建.
创建进程的系统调用函数(系统调用是下一个要讲的内容)有三个,它们分别是:
sys_fork(), sys_clone(), sys_vfork().由它们三个函数来完成进程的创建,这三个函数都比较简单,它们只是都同时调用了一个函数do_fork(),由于前面三个函数都比较简单,我们在后面讲,我们先讲一下do_fork()函数,这才是创建进程的主体.它被前面三个函数调用.

______________________________________________________________/kernel/fork.c

int do_fork(unsigned long clone_flags, unsigned long stack_start,
struct pt_regs, unsigned long stack_size)
{
int retval = -ENOMEN;
struct task_struct *p;
DECLARE_MUTEX_LOCKED(sem);

if(clone_flags & CLONE_PID)
if(current->pid)
return -EPERM;

/* 上面这段代码的意思是: 如果clone_flags的CLONE_PID置位,则除非是0号进程
否则将返回错误信息EPERM. clone_flags的定义将在后面介绍,0号进程就是操作
系统启动时创建的第一个进程.上面的代码中if(current->pid)要成立,除非是0号
进程.其中pid是进程标志符 */

current->vfork_sem = &sem;

p=alloc_task_struct();
if(! p)
goto fork_out;

/ * 上面代码的意思是: 申请一个新的内存控制块,其中alloc_task_struct是在/include
/asm_i386/process.h中的宏定义:
#define alloc_task_struct() ((struct task_struct * ) _get_free_pages
(GFP_KERNEL, 1))
其中_get_free_pages是要申请两个页面,页面是内存管理的有关内容,这儿大家只要
一个页面占4个字节就可以了.两个页面就是8个字节,而大家查一下进程控制块的大
小就可以知道它只有1600KB左右,有就时说它只有1M左右,那么还剩下将近6.5M
的空间去有什么用呢?其实剩余的空间是作为内核栈空间.至于内核栈的内容以后会
讲到,但是如果申请内存空间失败则直接栈道fork_out处执行,fork_out在后面有
定义 */

*p = * current;

/ * 代码意思: 前面申请了一个存放进程控制块的内存空间,接下创建新进程的进程
控制块的内容,具体做法是先直接将其父进程的进程控制块的内容复制过来,再在
后面对需要更新的部分做一些必要的赋值 */

retval= -EAGAIN;
if(atomic_read(&p->user->processor) >= p->rlim[RLIMIT_NPROC].
rlim_cur)
goto bad_fork_free;
atomic_inc(& p->user->_count);
atomic_inc(& p->user->processes);

/* 上面代码的意思: 如果父亲进程已经用了用户进程,则修改p->user->_count和
p->user->processes,将其数目加1,当然除非用户进程的 RLIMIT_NPROC
已经超过额定数目,如果你对代码不理解,那就先不要理解了,等你学完后面的代码
时就能理解了*/

if(nr_threads >= max_threads)
goto bad_fork_cleanup_count;

/* 上面的代码的意思是: 如果计算机中的进程的最大的进程数大于系统限定的最大
数目就返回错误信号 */

get_exec_domain(p->exec_domain);

if(p->binfmt && p->binfmt->module)

p->did_exec=NULL;
p->swappable=NULL;
p->state = TASK_UNINTERRUPTIBLE;

/ * 上面的代码比较简单: 其中p是指向进程控制块task_struct的指针,did_exec
和swappable以及state都在进程控制块中有定义,是对进程的这三个属性
进行赋值,其意思如下:
did_exec标志的意思是: 此进程是否被执行.
swappable标志的意思是: 此进程占有的内存页面是否可以被换出到交换空间.
state在前面有讲过.
那么上面代码的意思是刚创建的进程没有被执行并且内存空间页面不可以被换
出内存,当前状态设置为不可打断睡眠状态 */

copy_flags(clone_flags, p);
p->pid = get_pid(clone_flags);

/ * 上面代码涉及到进程控制块中的进程标志flags,在 task_struct中有定义,flags
的意思是进程中有一些特殊的时候要靠它来决定进程调度函数的调度,
上面的意思是: 将传进来的参数clone_flags通过copy_flags()函数转化为子进程
的进程标志flags, 然后为新的子进程创建一个进程标志符pid */

p->run_list.next=NULL;
p->run_list.prev=NULL;

/ * 对进程队列run_list进行初始化,进程队列run_list以后将 */

if((clone_flags & CLONG_VFORK) || ! (clone_flags & CLONG_PARENT))
{
p->p_opptr = current;
if(! (p->ptrace & PT_PTRACED))
p->p_pptr = current;
}

/ * 前面我们说过创建新进程的系统调用函数有三个,分别是sys_fork,sys_vfork,
sys_clone.
上面的意思是: 如果使用的sys_vfork()函数调用创建函数或者传人的参数
clone_flags的CLONE_PARENT位为1,则将子进程的的p_opptr()指针
指向当前进程.其中p_opptr指针在进程控制块task_struct中有定义,意思
是它指向进程的祖先进程.如果ptrace的PT_PTRACED不为1,则将进程的
指向父进程的指针指向当前进程.其中p_pptr是进程指向其父亲进程的指针,
同样在进程控制块task_struct中有定义 * /

p->p_cptr = NULL;

/ * 进程的p_cptr指针指向它的儿子进程,在这里初始化为NULL * /

init_waitqueue_head(&p->wait_chldexit);

/ * 意思是: 初始化等待队列,等待队列是就绪队列一个基础,等待队列的进程当它得到
它等待的资源(除CPU资源,CPUs是就绪队列中的进程等待的资源)后马上进入
就绪队列,等待调度函数的调用以获得CPU资源 * /

/ * 下面所以的代码都是对进程控制块task_struct一些变量做一些初始化,
这儿先就不过多的介绍了,现在只要知道有这些东西就行了 * /

p->vfork_sem=NULL;
spin_lock_inin(& p->alloc_lock);

p->sigpending = 0;
init_sigpending(& p->pending);


p->it_real_value = p->it_virt_value =p->it_prof_value = 0;
p->it_real_incr = p->it_virt_incr= p->it_prof_incr =0;
init_timer(&p->real_timer);
p->real_timer.data = (unsigned long) p ;
p->leader = 0;
p->tty_old_pgrd = 0;
p->times.tms_utime = p->times.tms_stimes = 0;
p->times.tms_cutime= p->times.tms_cstime = 0;

# ifdef CONFIG_SMP
{
int i;
p->has_cpu = 0;
p->proessor = current->proessor;
for(i=0; i p->per_cpu_utime[i] = p->per_cpu_stime[i] =0;
spin_lock_init(& p->sigmask_lock);
}
endif
p->lock_depth = -1;
p->start_time = jiffies;

/ * 初始化在此结束 */

retval = -ENOMEN;
if(copy_files(clone_flags, p))
goto bad_fork_cleanup;
if(copy_fs(clone_flags, p))
goto bad_fork_cleanup_files;
if(copy_sighand(clone_flags, p))
goto bad_fork_cleanup_fs;
if(copy_mm(clone_flags, p))
goto bad_fork_cleanup_sighand;

/ * 上面代码的意思是: 根据传人的参数clone_flags决定复制打开文件表,文件
系统信息,信号处理句柄和存储信息管理(上面4个分别代表的信息)还是直接
和父亲进程共享,仅仅将引用数加1 * /

retval = copy_thread(0, clone_flags, stack_start, stack_size, p, reps);
if(retval)
goto bad_fork_cleanup_sighand;
p->semundo = NULL;

p->parent_exec_id = p->self_exec_id;

p->swappable = 1;
p->exit_signal = clone_flags & CSIGNAL;
p->pdeath_signal = 0;

p->counter = (current->counter +1) >>1;
current->counter >>=1;

/ * 将父亲进程的时间片加1再除2然后赋值给子进程,同时将父亲进程的时间片
减半 * /
if(! current ->counter)
current->need_resched = 1;
/ * 如果父亲进程的时间片减半后为0的话,直接将need_resched设置为1,
need_resched在前面已经说过,它的意思是: 如果设置为1,下一此调度函数
可以直接调度这个进程,但是如果设置为0则表示下一次调度函数不能调度此
函数 */

retval = pid;
p->gpid = retval;
INIT_LIST_HEAD(&p->thread_group);
writh_lock_irq(&taslist_lock);
if(clone_flags & CLONE_THREAD)
{
p->tgid = current->tgid;
list_add(&p->thread_group, ¤t->thread_group);
}
SET_LINKS(p);
hash_pid(p);
nr_threads ++;
writh_unlock_irq(&tasklist_lock);

if(p->ptrace & PT_PTRACED)
send_sig(SIGSTOP, p, 1);

wake_up_processor(p);
++ total_forks;

/ * 下面的前面goto代码要要转到执行的地方 */

fork_out:
if((clone_flags & CLONE_VFORK) && (retval >0))
down(&sem);
return retval;

bad_fork_cleanup_sighand:
exit_sighand(p);

bad_fork_cleanup_fs:
exit_fs(p);

bad_fork_cleanup_files:
exit_files(p);

bad_ford_cleanup:

put_exec_domain(p->exec_domain);
if(p->binfim && p->binfim->module)
bad_fork_cleanup_count:
atomic_dec(&p->user->processes);
free_uid(p->user);

bad_fork_free:
free_task_struct(p);
goto fork_out;
}

______________________________________________________________/kernel/fork.c

上面讲解了三个进程创建函数sys_fork, sys_clone, sys_vfork****同要调用的函数
do_fork(),下面讲一下这三个函数,其实这三个函数很简单
sys_fork()函数如下:
_______________________________________________/arch/i386/kernel/process.c

asmlinkage int sys_fork(struct pt_regs regs)
{
return do_fork(SIGCHLD, regs.esp, ®s, 0)
}

_______________________________________________/arch/i386/kernel/process.c

sys_clone()函数如下:_____________________________________________/arch/i386/kernel/process.c

asmlinkage int sys_clone(struct pt_regs regs)
{
unsigned long clone_flags;
unsigned long newsp;

clone_flags = regs.ebx;
newsp = regs.ecx;
if(! Newsp)
newsp = regs.esp;

return do_fork(SIGCHLD, regs.esp, ®s, 0)
}

_______________________________________________/arch/i386/kernel/process.c

sys_vfork()函数如下:
_______________________________________________/arch/i386/kernel/process.c

asmlinkage int sys_vfork(struct pt_regs regs)
{
return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, ®s, 0)
}

_______________________________________________/arch/i386/kernel/process.c
文章评论

共有 3 条评论

  1. 194.88.214.* 于 2007-07-19 05:59:27发表:

    http://www.ringtones-dir.net/get/ ringtones site free. ringtones site, Free nokia ringtones here, Download ringtones FREE. From website .

  2. 194.88.214.* 于 2007-07-19 05:59:11发表:

    http://www.ringtones-dir.net/get/ ringtones site free. ringtones download: ringtones site, Free nokia ringtones here, Download ringtones FREE. samsung ringtones From website .

  3. 71.203.172.* 于 2007-07-19 05:59:00发表:

    http://www.ringtones-dir.net/download/ download ringtones. nokia ringtones: ringtones site, Free nokia ringtones here, Download ringtones FREE. http://www.ringtones-dir.net/free/ [link=http://www.ringtones-dir.net]ring tones[/link] From site .