进程管理源码
‘壹’ 给我一个操作系统源代码的下载地址
我劝你要是制作操作系统的话不要依靠那些免费的资源,买几本书比什么都好。linux源代码我下载过,根本没法看。你需要先明白操作系统的工作原理,熟练使用汇编+C语言才可能编写出一个很烂的操作系统。给你推荐一本《30天自制操作系统》,不是打广告,我看完也有很大收获。里面还有源代码光盘。
你实在想要的话,可以用这个
网页链接
这是Linux的内核代码,你可以免费看。不过我估计你看到后已经崩溃了。
顺便说一句,我其实早就看到过这个贴,而且我打赌你现在已经忘记这个了。
加油。
还有,在知道上提问,态度要好一点。
‘贰’ 如何查看linux系统源码
一般在Linux系统中的/usr/src/linux*.*.*(*.*.*代表的是内核版本,如2.4.23)目录下就是内核源代码(如果没有类似目录,是因为还没安装内核代码)。另外还可从互连网上免费下载。注意,不要总到http://www.kernel.org/去下载,最好使用它的镜像站点下载。请在http://www.kernel.org/mirrors/里找一个合适的下载点,再到pub/linux/kernel/v2.6/目录下去下载2.4.23内核。
代码目录结构
在阅读源码之前,还应知道Linux内核源码的整体分布情况。现代的操作系统一般由进程管理、内存管理、文件系统、驱动程序和网络等组成。Linux内核源码的各个目录大致与此相对应,其组成如下(假设相对于Linux-2.4.23目录):
1.arch目录包括了所有和体系结构相关的核心代码。它下面的每一个子目录都代表一种Linux支持的体系结构,例如i386就是Intel CPU及与之相兼容体系结构的子目录。PC机一般都基于此目录。
2.include目录包括编译核心所需要的大部分头文件,例如与平台无关的头文件在include/linux子目录下。
3.init目录包含核心的初始化代码(不是系统的引导代码),有main.c和Version.c两个文件。这是研究核心如何工作的好起点。
4.mm目录包含了所有的内存管理代码。与具体硬件体系结构相关的内存管理代码位于arch/*/mm目录下。
5.drivers目录中是系统中所有的设备驱动程序。它又进一步划分成几类设备驱动,每一种有对应的子目录,如声卡的驱动对应于drivers/sound。
6.ipc目录包含了核心进程间的通信代码。
7.moles目录存放了已建好的、可动态加载的模块。
8.fs目录存放Linux支持的文件系统代码。不同的文件系统有不同的子目录对应,如ext3文件系统对应的就是ext3子目录。
Kernel内核管理的核心代码放在这里。同时与处理器结构相关代码都放在arch/*/kernel目录下。
9.net目录里是核心的网络部分代码,其每个子目录对应于网络的一个方面。
10.lib目录包含了核心的库代码,不过与处理器结构相关的库代码被放在arch/*/lib/目录下。
11.scripts目录包含用于配置核心的脚本文件。
12.documentation目录下是一些文档,是对每个目录作用的具体说明。
一般在每个目录下都有一个.depend文件和一个Makefile文件。这两个文件都是编译时使用的辅助文件。仔细阅读这两个文件对弄清各个文件之间的联系和依托关系很有帮助。另外有的目录下还有Readme文件,它是对该目录下文件的一些说明,同样有利于对内核源码的理解。
在阅读方法或顺序上,有纵向与横向之分。所谓纵向就是顺着程序的执行顺序逐步进行;所谓横向,就是按模块进行。它们经常结合在一起进行。对于Linux启动的代码可顺着Linux的启动顺序一步步来阅读;对于像内存管理部分,可以单独拿出来进行阅读分析。实际上这是一个反复的过程,不可能读一遍就理解。
‘叁’ windows 进程管理源代码详解,要详细的,
Windows 是闭源开发的商业软件,受商业版权法保护,别说大家都没有,就算有也不能给你的(被微软起诉那可不是小事)。
Linux的是开源的,干嘛不去读Linux源码呢?
新版本的太长,写不开,给你最经典的0.11版的进程管理源码。其他的你自己去查。作为一个好的程序员,必须学会充分利用搜索引擎。
---------------------------------------
linux0.11通过一个进程数组管理进程,最大允许64个进程存在。进程的状态有就绪态,运行态,暂停态,睡眠态和僵死态等。睡眠态又可分为可中断和不可中断两种。对于进程的管理,我觉得按照进程的状态来讲会清晰一些。
1.0号任务
0号比较特殊,是"纯手工制作",下面就是制作过程
mem_init(main_memory_start,memory_end);
trap_init();
blk_dev_init();
char_dev_init();
tty_init();
time_init();
sched_init();
buffer_init(buffer_memory_end);
hd_init();
floppy_init();
sti();
move_to_user_mode();
这么多init当然不全是为0任务准备的,他们是初始化系统。对任务0来说,重要的是sched_init()和move_to_user_mode()两个。sched_init()中手动设置了任务0的任务段描述符tss和局部段描述符ldt,并设置了tr和ldtr寄存器:
set_tss_desc(gdt+FIRST_TSS_ENTRY,&(init_task.task.tss)); //设置tss段描述符
set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt));//设置ldt描述符
...
ltr(0); //加载tss描述符地址到tr
lldt(0); //加载ldt描述符地址到ldtr
我们来看一下任务0的tss和ldt是什么样子
/*ldt*/ {0,0},\
{0x9f,0xc0fa00},\
{0x9f,0xc0f200},\
/*tss*/{0,PAGE_SIZE+(long)&init_task,0x10,0,0,0,0,(long)&pg_dir,\
0,0,0,0,0,0,0,0,\
0,0,0x17,0x17,0x17,0x17,0x17,0x17,\
_LDT(0),0X80000000,\
{}\
},\
从ldt 可以看到,任务的代码和数据段均为640k,基址为0,DPL=3。这说明虽然任务0的代码还是处在内核段内,但是任务的级别已经是用户态了。从tss也可以看到这一点,代码和数据都被置为0x17,也就是局部段描述符表第2项。还可以看到任务0的内核太堆栈被设置在init_task之后的一页处,用户态堆栈就是move_to_user_mode之前的内核态堆栈。这和普通进程不一样,普通进程的用户态堆栈在其64Mb地址空间的末端。
move_to_user_mode是在堆栈中创建一个任务切换的假象,用iret跳转到外层3,这样cpu就会自动根据tr加载tss,并初始化各个寄存器运行任务0。所以,任务0其实就是内核空间中的用户态任务。
2.进程的创建
进程创建是由系统调用sys_fork完成的,主要使用了两个函数find_empty_process和_process。前者在进程在进程数组中找一个不用的进程号给子进程,后者完成子进程信息的创建,主要是复制父进程的信息。
我们来看一下_process的代码:
int _process(int nr,long ebp,long edi,long esi,long gs,long none,
long ebx,long ecx,long edx,long fs,long es,long ds,
long eip,long cs,long eflags,long esp,long ss)
{
struct task_struct *p;
int i;
struct file *f;
//首先为子进程的进程描述符分配一块内存
p=(struct task_struct *)get_free_page();
if(!p)
return -EAGAIN;
//将新任务结构指针加入数组中
task[nr]=p;
//复制当前用户的任务结构到子进程中。当然堆栈不复制
*p=*current;
//将子进程的状态设为不可中断等待,防止现在被调度
p->state=TASK_UNINTERRUPTIBLE;
P->pid=last_pid;
p->father=current->pid;
p->count=p->priority;
p->signal=0;
p->alarm=0;
p->leader=0;
p->utime=p->stime=0;
p->cutime=p->cstime=0;
p->start_time=jiffies;
p->tss.back_link=0;
//新进程的内核态堆栈在进程描述符页末端
p->tss.esp0=PAGE_SIZE+(long)p;
P->tss.ss0=0x10;
//ip为父进程调用fork的下一条指令
p->tss.eip=eip;
//fork的返回值对子进程来说是0,对父进程来说是它的pid,通过这个区别,在fork调用返回后,父子进程的代码段便被分割来,
p->tss.eax=0;
//虽然他们是在一个代码文件中写的
p->tss.ecx=ecx;
p->tss.edx=edx;
p->tss.ebx=ebx;
p->tss.esp=esp;
p->tss.ebp=ebp;
p->tss.esi=esi;
p->tss.edi=edi;
p->tss.es=es&0xffff;
p->tss.cs=cs&0xffff;
p->tss.ss=ss&0xffff;
p->tss.ds=ds&0xffff;
p->tss.fs=fs&0xffff;
p->tss.gs=gs&0xffff;
p->tss.ldt=_LDT(nr);
p->tss.trace_bitmap=0x80000000;
//如果父任务使用了协处理器,还要保存到tss中
if(last_task_used_math==current)
_asm("clts;fnsave %0"::"m"(p->tss.i387));
//为新任务设置新的代码和数据段基地址。注意,因为linux0.11只支持64M的进程空间,所以进程的线性地址空间在64M的边界处。
//然后为新任务复制父进程的页表。通过_page_tales,父子任务此时共用一个只读的代码数据段。在写操作时,写时复制会为新进程申请新的物理页面。
if(_mem(nr,p)){
task[nr]=NULL;
free_page((long)p);
return -EAGAIN;
}
for(i=0;i<NR_OPEN;i++)
if(f=p->filp)
f->f_count++;
if(current->pwd)
current->pwd->i_count++;
if(current->root)
current->root->i_count++;
if(current->executable)
current->executable->i_count++;
//设置新任务的tss和ldt描述符
set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss));
set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&(p->ldt));
//返回子进程的pid
return last_pid;
}
参数都是堆栈中的值,nr是调用_process之前的find_empty_process的返回值,作为任务数组的序号。
3.进程的调度
进程创建之后并不是立即执行。系统会在适当的时刻调用系统调度函数schele,它会选择适当的进程运行。调度函数可能在系统调用结束之后,进程暂停/ 睡眠/退出,时钟中断等多个地方调用。调度函数主要是通过进程的时间片来选择一个运行时间最短的进程运行。如果没有找到就运行空闲pause系统调用,在 Pause中,调度函数又会被调用。下面是主要代码
while(1){
c=-1;
next=0;
i=NR_TASKS;
p=&task[NR_TASKS];
//寻找就绪任务中运行时间最短的任务
while(--i){
if(!(*--p))
continue;
if((*p)->state==TASK_RUNNING&&(*p)->counter>c)
c=(*p)->counter,next=i;
}
//如果找到,就退出。否则重新计算任务的时间片。注意,如果没有任务,next始终为0,结果就被切换到任务0
if(c)break;
for(p=&LAST_TASK;p>&FIRST_TASK;--p)
if(*p)
(*p)->counter=((*p)->counter>>1)+(*)->priority;
}
switch_to(next);//通过长跳转,转换任务到新任务。
}
时钟中断是执行调度的重要措施,正是由于不断的执行调度,系统才能在多个任务之间进行切换,完成多任务操作系统的目标。在调度器初始化时,除了手动设置了任务0,还对8253开启了定时器中断,对系统"点火".中断中调用了do_timer函数。现在这是个很短的函数:
void do_timer(long cpl)
{
...
//根据优先级调整进程运行时间
if(cpl)
current->utime++;
else
current->stime++;
//处理定时器中断队列
if(next_timer){
next_timer->jiffies--;
while(next_timer&&next_timer->jiffies<=0){
void(*fn)(void);
fn=next_timer->fn;
next_timer->fn=NULL;
next_timer=next_timer->next;
(fn)();
}
}
..
//对进程时间片减1。如果大于0 ,表示时间片还没有用完,继续
if((--current->counter>0)return;
//注意,内核态任务是不可抢占的,在0.11中,除非内核任务主动放弃处理器,它将一直运行。
if(!cpl)return;
//调度
schele();
}
4.进程的终止
进程可以自己中止,也可以被中止信号中止。do_exit执行退出。如果它有子进程的话,子进程就会成为孤儿进程,这里会将它的孩子托付给进程1(即init进程)管理。在成为僵死进程之后,还要通知父进程,因为他的父进程可能在等它呢。
‘肆’ 如何查看 linux 内核源代码
Linux的内核源代码可以从很多途径得到。一般来讲,在安装的linux系统下,/usr/src/linux目录下的东西就是内核源代码。
对于源代码的阅读,要想比较顺利,事先最好对源代码的知识背景有一定的了解。对于linux内核源代码来讲,我认为,基本要求是:1、操作系统的基本知识;2、对C语言比较熟悉,最好要有汇编语言的知识和GNU C对标准C的扩展的知识的了解。另外在阅读之前,还应该知道Linux内核源代码的整体分布情况。我们知道现代的操作系统一般由进程管理、内存管理、文件系统、驱动程序、网络等组成。看一下Linux内核源代码就可看出,各个目录大致对应了这些方面。Linux内核源代码的组成如下(假设相对于linux目录):
arch 这个子目录包含了此核心源代码所支持的硬件体系结构相关的核心代码。如对于X86平台就是i386。
include 这个目录包括了核心的大多数include文件。另外对于每种支持的体系结构分别有一个子目录。
init 此目录包含核心启动代码。
mm 此目录包含了所有的内存管理代码。与具体硬件体系结构相关的内存管理代码位于arch/*/mm目录下,如对应于X86的就是arch/i386/mm/fault.c 。
drivers 系统中所有的设备驱动都位于此目录中。它又进一步划分成几类设备驱动,每一种也有对应的子目录,如声卡的驱动对应于drivers/sound。
ipc 此目录包含了核心的进程间通讯代码。
moles 此目录包含已建好可动态加载的模块。
fs Linux支持的文件系统代码。不同的文件系统有不同的子目录对应,如ext2文件系统对应的就是ext2子目录。
kernel 主要核心代码。同时与处理器结构相关代码都放在arch/*/kernel目录下。
net 核心的网络部分代码。里面的每个子目录对应于网络的一个方面。
lib 此目录包含了核心的库代码。与处理器结构相关库代码被放在arch/*/lib/目录下。
scripts此目录包含用于配置核心的脚本文件。
Documentation 此目录是一些文档,起参考作用。
俗话说:“工欲善其事,必先利其器”。 阅读象Linux核心代码这样的复杂程序令人望而生畏。它象一个越滚越大的雪球,阅读核心某个部分经常要用到好几个其他的相关文件,不久你将会忘记你原来在干什么。所以没有一个好的工具是不行的。由于大部分爱好者对于Window平台比较熟悉,并且还是常用Window系列平台,所以在此我介绍一个Window下的一个工具软件:Source Insight。这是一个有30天免费期的软件,可以从www.sourcedyn.com下载。安装非常简单,和别的安装一样,双击安装文件名,然后按提示进行就可以了。安装完成后,就可启动该程序。这个软件使用起来非常简单,是一个阅读源代码的好工具。它的使用简单介绍如下:先选择Project菜单下的new,新建一个工程,输入工程名,接着要求你把欲读的源代码加入(可以整个目录加)后,该软件就分析你所加的源代码。分析完后,就可以进行阅读了。对于打开的阅读文件,如果想看某一变量的定义,先把光标定位于该变量,然后点击工具条上的相应选项,该变量的定义就显示出来。对于函数的定义与实现也可以同样操作。别的功能在这里就不说了,有兴趣的朋友可以装一个Source Insight,那样你阅读源代码的效率会有很大提高的。怎么样,试试吧!