Linux源代码注释.docx
- 文档编号:5608508
- 上传时间:2022-12-29
- 格式:DOCX
- 页数:17
- 大小:28.80KB
Linux源代码注释.docx
《Linux源代码注释.docx》由会员分享,可在线阅读,更多相关《Linux源代码注释.docx(17页珍藏版)》请在冰豆网上搜索。
Linux源代码注释
接触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
structtask_struct{
volatilelongstate;/*进程的状态,在代码后面有说明*/
unsignedlongflags;/*进程标志*/
intsigpending;
mm_segment_taddr_limit;/*线性地址空间:
0-0xBFFFFFFF为用户线性空间地址;
0-0xFFFFFFFF为内核线性空间地址*/
structexec_domain*exec_domain;
volatilelongneed_resched;
unsignedlongptrace;
intlock_depth;
longcounter;/*进程的动态优先级,在代码后面有说明*/
longnice;/*进程的静态优先级,在代码后面有说明*/
unsignedlongpolicy;/*进程采用的调度策略,在代码后面有说明*/
structmm_struct*mm;/*进程属性中指向内存管理的数据结构mm_structd的
指针,在代码后面有说明*/
inthas_cpu,processor;
unsignedlongcpus_allowed;
structlist_headrun_list;
unsignedlongsleep_time;
structtask_struct*next_task,*prev_task;/*所以进程通过这两个指针组成
一个双向链表*/
structmm_struct*active_mm;/*指向活动地址空间,在后面的代码有说明*/
/*taskstate*/
structlinux_binfmt*binfmt;
intexit_code,exit_signal;
intpdeath_signal;
unsignedlongpersonality;
intdid_exec:
1;
intdumpable:
1;
pid_tpid;/*进程标志符,,在代码后面有说明*/
pid_tpgrp;/*进程组标号,,在代码后面有说明*/
pid_ttty_old_pgrp;
pid_tsession;
pid_ttgid;
intleader;
structtask_struct*p_opptr,*p_pptr,*p_cptr,*p_ysptr,*p_osptr;
/*这五个标志表示一个进程的在计算机中的亲属关系,
分别标志祖先进程,父进程,子进程,弟进程和兄进程,
为了在两个进程之间共享方便而设立*/
structlist_headthread_group;
structtask_struct*pidhist_next;
structtask_struct*pidhist_pprev;/*上面两个指针是为了在计算机中快速查
一个进程而设立,具体方式以后讲*/
wait_queue_head_twait_chldexit;
structcompletion*vfork_sem;
unsignedlongrt_priority;/*实时进程的优先级标志*/
unsignedlongit_real_value,it_prof_value,it_virt_value;
unsignedlongit_real_incr,it_prof_incr,it_virt_incr;
structtimer_listreal_timer;
structtmstimes;
structtmsgroup_times;
unsignedlongstart_time;
longper_cpu_utime[NR_CPUS],per_cpu_stime[NR_CPUS];
unsignedlongmin_flt,maj_flt,nswap,cmin_flt,cmaj_flt,cnswap;
intswappable:
1;
uid_tuid,euid,suid,fsuid;
gid_tgid,egid,sgid,fsgid;
intngroups;
gid_tgroups[NGROUPS];
kernel_cap_tcap_effective,cap_inheritable,cap_permitted;
intkeep_capabilities:
1;
structuser_struct*user;
structrlimitrlim[RLIM_NLIMITS];
unsignedshortused_math;
charcomm[16];
intlink_count;
structtty_struct*tty;/*NULLifnotty*/
unsignedintlocks;/*Howmanyfilelocksarebeingheld*/
structsem_undo*semundo;
structsem_queue*semsleeping;
structthread_structthread;
structfs_struct*fs;/*进程属性中指向和文件管理有关的数据结构*/
structfiles_struct*files;
spinlock_tsigmask_lock;
structsignal_struct*sig;
sigset_tblocked;
structsigpendingpending;
unsignedlongsas_ss_sp;
size_tsas_ss_size;
int(*notifier)(void*priv);
void*notifier_data;
sigset_t*notifier_mask;
u32parent_exec_id;
u32self_exec_id;
spinlock_talloc_lock;
spinlock_tswitch_lock;
};
____________________________________________________/include/linux/sched.h
程序说明如下:
volatilelongstate定义了进程的状态,进程总共有6种状态标志,分别是:
一.TASK_RUNING.(正在运行状态或者是可运行状态)
二.TASK_INTERRUPTIBLE,(可打断睡眠状态)
三.TASK_UNINTERRUPTIBLE,(不可打断睡眠状态)
四.TASK_ZOMBLE.(僵死状态)
五.TAKS_STOPPED.(暂停状态)
六.交换状态.
这六种属性标志了此进程现在的状态,各种不同的状态决定了进程什么时候获得CPU而被执行.
正在运行状态是当前正在运行进程的状态.也就是说他的volatilelongstate被标志成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*mmmm_struct数据结构是描述内存存储信息的数据结构,进程控制块task_struct中用mm指针指想mm_struct数据结构.也就是在进程的属性中通过mm指针来管理起对应的内存区.
mm_struct*active_active内核线程用来指向调用它的普通进程的内存地址空间.当普通进程在运行时如果发生系统调用,程序就会从用户态转为内核态,内核态中执行的是内核线程,内核线程没有内存空间地址结构mm_struct,当他需要内存空间地址的时候就会调用用户态对应进程的用以空间地址结构mm_struct.内核线程就是就是通过active_mm指针来指向用户态进程的mm_struct结构.
pid_tpidpid是进程标志符,操作系统每创建一个新的进程就要为这个新进程分配一个进程控制快(PCB),那么系统内核是怎样区分这些进程的呢?
就是通过进程标志符pid,系统在为新的进程分配进程控制块的候,它不是自己去创建,而是直接从上一个进程中复制它的进程控制块,其中里面的大部分东西保留下来,只做少量的改动,然后它的进程标志符加1赋值给新的进程控制块.
进程控制块的总共有80多项,在这里就不一一介绍了,以后我们在学习过程用到什么属性就介绍什么属性,这样在宏观上把握,不至于被一个小小的进程控制块给挡住了.
接着上面的进程说明,
前面我们说了一下进程的基本概念和它的属性,接下来我们介绍一下在操作系统内部是怎样通过调度函数调度进程的.
我们在使用计算机的时候,可能同时打开了很多程序,比如同时打开IE浏览器,QQ,word文档等,那么这就意味着内核要同时管理多个用户进程.这些进程中每个进程每隔一段时间就要被CPU执行一次,所有的进程都是这样轮流执行,在Linux操作系统中一个进程占有CPU的时间为50ms(也就是5个时间滴答,关于时间滴答的概念后面介绍),这个时间对于人的反应来讲是相当快的,所以人更不感觉不到进程的调度和切换,各个进程也有轻重缓急之分,后面还会说一个goodness函数,这个函数比较简单,主要作用是综合进程属性(进程控制快)
中的各种因素和数据算出每个进程的权值,权值最大的进程就是调度程序(schedule())最应该调用的进程.
在计算机中进程以六种状态存在,这在进程的属性里已经有了介绍,其中存在于就绪队列TASK_RUNNING中处于运行状态的进程是等待计算机中唯一的资源CPU的进程,它们只要一得到CPU就马上可以被执行,那么操作系统是安什么样的规则来让这些进程合理和公平的得到CPU的呢,这就要用到计算机的调度函数schedule(),
_____________________________________________________________/kernel/sched.c
asmlinkagevoidschedule(void)
{
structschedule_data*sched_data;
structtask_struct*prev,*next,*p;
structlist_struct*tmp;
intthis_cpu,c;
if(!
current->active_mm)BUG();/*current指当前进程,当前进程所指的内存空间
如果为空,那么进程必定有问题,出错返回*/
need_resched_back:
prev=current;
this_cpu=prev->processor;
if(in_interrupt())
gotoscheduling_in_interrupt;
/*上面代码的意思是:
将局部变量prev和this_cpu初始化为当前进程和当前
CPU.然后检查schedule()是否中断调用,若是则直接返回,其中goto后面的
转到的地方在本程序中有定义,比如本进程后面的scheduling_in_interrupt在本程序
的最后有定义,意思是:
打印出”Schedulingininterrupt”字符后退出*/
release_kernel_lock(prev,this_cpu);
if(softirq_active(this_cpu)&softirq_mask(this_cpu))
gotohandle_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)
gotomove_rr_last;
*当前基于优先权轮转法的调度策略实时进程,如果要调用它,先检查其count
是否0若是,则将其挂到运行队列的最后面.具体做法在后面的move_rr_last
中有定义*/
move_rr_back:
switch(prev->state)
{
caseTESK_INTERRUPTIBLE:
if(sign_pending(prev))
{
prev->state=TASK_RUNNING;
break;
}
default:
del_form_runqueue(prev);
caseTASK_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)
gotostill_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,structtask_struct,run_list);
if(can_schedule(p,this_cpu))
intweight=goodness(p,this_cpu,prev->active_mm);
if(weight>c)
c=weight,next=p;
}
/*上面代码的意思是:
遍历整个就绪队列,计算出每一个进程的goodness权值并且
与当前进程做比较,选择goodness权值最大的那个进程获得cpu资源.*/
if(!
c)
gotorecalculate;
/*上面代码的意思是:
如果当前goodness权值为0,则运行recalculate:
起代码在
后面有说明,意思是:
重新计算每一进程的优先权,最后转到repeat_schedule:
处执
行,repeat_schedule:
部分在前面已经有讲过,自己看有会过头去看一下吧*/
sched_data->curr=next;/*将当前进程信息保存到局部变量sched_data中*/
#ifdefCONFIG_SMP
next->has_cpu=1;
next->processor=this_cpu;
/*意思为:
将调度数据区指针指向下一个要调度的进程并将当前进程的cpu设置为
下一个进程的当前cpu.*/
#endif
spin_unlock_irq(&runqueue_lock);
if(prev==next)
gotosame_processor;
/*意思为:
如果当前运行的进程的goodness还是队列中所以进程中最高的,也就是
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Linux 源代码 注释