Linux内核学习大总结之一:进程管理

yanglilibaobao 2006-12-05 04:19:18
首先,让我们了解一下linux的进程队列结构。

1.1linux进程队列其实是一个双向链表,每一个结点就是一个进程描述符。进程描述符里面包含了进程所有的信息:进程所打开的文件、进程的地址空间、挂起信号、进程状态和其他更多的信息。以下是进程描述符的部分定义:

shruct task_struct

{

unsigned long state;

int prio;

unsigned long policy;

struct task_struct *parent;

struct list_head tasks;

pid_t pid;

......

} ;

linux通过slab分配器分配task-struct结构。它实际上是一个栈,其栈顶(向上增长的栈)或栈底(向下增长的栈)中有一个thread info结构,其中的task指针指向task_struct。

1.2那么,如何识别这些进程呢?进程描述符里面有一个pid,它就是进程间区别标志。它实际上是一个短整型数据,也就是说它最大值为32767。一般来说,32767对于很多桌面系统已经足够,但是对于大型服务器,就必须修改这个上限。

那么,又如何寻找这些进程呢?呵呵,进程队列是一个双向链表,那么我们当然可以遍历它寻找需要的进程。当然,这不是什么好主意,因为这很耗费时间!一般的,有些硬件体系有专门的寄存器存放了指向当前task_struct的指针,可以很快的找到当前进程。在X86系列机中,寄存器非常有限,一般使用通过计算thread_info的偏移量的方法来找到当前进程。

1.3接下来,我们讨论linux进程的状态。

·运行(TASK_RUNNING):一般指就绪状态——指进程随时可以投入运行和运行状态——指进程正在运行。

·可中断(TASK_INTERUPTIBLE):这个时候,进程停止运行,直到它获得满足它继续运行的条件。

·不可中断(TASK_UNINTERUPTIBLE):这个时候,进程也是停止运行,但是,即便它获得满足它继续运行的条件,它也不会马上被激活。

·僵死(TASK_ZOMBIE):进程运行结束,等待父进程销毁它。

·停止(TASK_STOPPED):进程停止运行,当进程收到SIGSTOP、SIGTSTP、SIGTTIN、SIGTTOU等信号,就会停止。在调试期间,进程收到任何信号,也会停止运行。

那么,如何设置进程这些状态呢?使用set_task_state(task,state)函数,就可以改变进程描述符里的进程状态state。

1.4好了,进程之间的关系如何?前面说了,进程队列是一个双向链表,那么,他们具体是如何连接的呢?

首先,系统开启的最后一段时间,系统将创建一个init进程,它是系统的第一个进程,它是所有进程的祖先,任何进程,如果不断的追朔其父进程,那么都会回到这个init进程。

这样,进程队列中的进程就有两种连接方式:一种就是刚才说的双向链表;还有一种就是各进程有父子关系,或者兄弟关系。

1.5关于创建进程。linux创建进程很复杂,首先调用fork(),接着fork()调用clone(),然后clone()调用do_fork(),而do_fork()调用copy_process()。每一步调用都完成不同的任务。

首先是copy_process()的一系列工作:

第一步调用dup_task_struct()复制父进程(没错,这个时候描述符也是一样的!),创建内核栈,检查进程数目有没有超过资源限制。

接着,把进程描述符中的各项设为0或者初始值,并把进程状态设为不可中断(TASK_UNINTERUPTIBLE),保证进程未创建完毕前不要投入运行。

第三步,调用copy_flags(),设置描述符中的flag,表明进程是否有超级权限。

然后调用get_pid()设置进程pid。

第五步,根据clone()传来的参数,拷贝或者打开文件、文件系统信息信号处理函数、进程地址空间和命名空间等等。一般来说,linux其实使用的是写时复用,也就是共享资源。(待会会讨论到)

第六步,让父进程和子进程平分剩余的时间片。

最后,返回一个指向子进程的指针。

如果成功的返回到do_fork(),这个新建的子进程会被唤醒,准备投入运行。当然,一般是马上投入运行,也即调用exec()函数,将程序代码(当然是计算机可以直接识别运行的代码)载入进程地址空间,执行里面的代码!

1.6vfork()——这也是一个创建进程的函数,唯一和fork()不一样的是,再创建子进程时它会使父进程挂起,直到创建结束。

1.7线程的话题。其实linux系统没有线程的概念!很吃惊吧?更准确的说,linux内核中,进程和线程都称为任务(task),只有在用户级层面,才有线程这一说法。即便如此,linux仍然视进程和线程为同样的概念,因为它们都有自己的描述符。

一般来说,我们都说线程是“轻进程”,因为线程共享产生它的进程的地址空间、文件资源等等。但是,linux中,进程的产生很多时候使用写时复用,既和父进程共享资源(如果子进程创建后马上调用exec(),那么连共享资源也免了,进程可以直接投入运行!),这一点和线程一样,使得进程和线程没有太大区别。

1.8终结进程。进程终结也需要做很多繁琐的收尾工作。系统必须保证进程所占用的资源回收,并通知父进程。

这里就只大致说下具体进程结束过程:系统调用exit(),开始析构进程。

然后调用sem__exit()使该进程进程队列。

把进程设置为僵死状态。这个时候,进程无法投入运行了,它的存在只为父进程提供信息——申请死亡。

父进程得到信息后,开始调用wait4(),最终赐死子进程——子进程占用的所有资源被全部释放。

这里很显然有一个问题:如果被杀死的子进程同时是其它进程的父进程,那么该怎么办呢?如果置之不理,那么那些子进程将会变为僵死状态,这不是我们所想看到的。

处理方法是为那些子进程寻找父进程,甚至可以设置init为其父进程。

下次,我们将讨论进程的调度问题,敬请期待!


...全文
3574 18 打赏 收藏 转发到动态 举报
写回复
用AI写文章
18 条回复
切换为时间正序
请发表友善的回复…
发表回复
xmoon1983 2007-01-21
  • 打赏
  • 举报
回复
mark 学习。
wanas 2007-01-17
  • 打赏
  • 举报
回复
Linux Kernel Development
jiura 2006-12-28
  • 打赏
  • 举报
回复
mark
hall919 2006-12-16
  • 打赏
  • 举报
回复
就是啊,和《Linux内核设计与实现》一摸一样的
qianjin558 2006-12-12
  • 打赏
  • 举报
回复
好,期待!
dylin 2006-12-11
  • 打赏
  • 举报
回复
jackzhhuang 2006-12-07
  • 打赏
  • 举报
回复
这个是我的学习《Llinux内核设计与实现》一书的原创总结,当然会有一样的地方了,就象段落大意一样.这篇文章首发我的博客!!
jackzhhuang 2006-12-07
  • 打赏
  • 举报
回复
楼主,你发的这个帖是我的原创文章!!!!!

我参考了书目:《Llinux内核设计与实现》(美)Robert Love著

就是对这本书的总结

frankiemaya 2006-12-06
  • 打赏
  • 举报
回复
最近在看《情景分析》,感觉挺不错的
其实把把别人的东西吸收过来写篇文章也不错阿,反正是为了学习,又不是为出名
f_acme 2006-12-06
  • 打赏
  • 举报
回复
我也觉得跟Linux Kernel Development非常相似
motalelf 2006-12-06
  • 打赏
  • 举报
回复
·可中断(TASK_INTERUPTIBLE):这个时候,进程停止运行,直到它获得满足它继续运行的条件。

·不可中断(TASK_UNINTERUPTIBLE):这个时候,进程也是停止运行,但是,即便它获得满足它继续运行的条件,它也不会马上被激活。

错误

·可中断(TASK_INTERUPTIBLE):这个时候,进程停止运行,直到它获得满足它继续运行的条件,或者!!!!被信号唤醒。

·不可中断(TASK_UNINTERUPTIBLE):这个时候,进程停止运行,直到它获得满足它继续运行的条件










第五步,根据clone()传来的参数,拷贝或者打开文件、文件系统信息信号处理函数、进程地址空间和命名空间等等。一般来说,linux其实使用的是写时复用,也就是共享资源。(待会会讨论到)

错误,

Fork绝对不会发生拷贝或者打开文件这样的事,只是或者共享files_struct,或者新分配一个Files_struct,对所有dentry引用++. Execve的时候才有对已打开的文件的操作.

写时复用发生在不共享资源,既不共享mm_struct时才会发生;共享地址资源时,反而不应该发生写时复用.







1.6vfork()——这也是一个创建进程的函数,唯一和fork()不一样的是,再创建子进程时它会使父进程挂起,直到创建结束。

错误

vfork是指的共享mm_struct,因此会一直阻塞父进程,直到子进程exit或者execve





一般来说,我们都说线程是“轻进程”,因为线程共享产生它的进程的地址空间、文件资源等等。但是,linux中,进程的产生很多时候使用写时复用,既和父进程共享资源(如果子进程创建后马上调用exec(),那么连共享资源也免了,进程可以直接投入运行!),这一点和线程一样,使得进程和线程没有太大区别。

看不懂,子进程刚开始的时候,或者与父进程共享mm_struct; 或者复制父进程mm_struct,同时写时复制. 不论那种情况,子进程刚开始都使用的父进程的用户空间的那些page.






lsof 2006-12-06
  • 打赏
  • 举报
回复
呵呵,还学习总结呢,纯粹是抄袭LKD嘛
linux_wince 2006-12-06
  • 打赏
  • 举报
回复
学习....
frankiemaya 2006-12-06
  • 打赏
  • 举报
回复
waiting for you
Sniper167 2006-12-06
  • 打赏
  • 举报
回复
学习。。。
losedxyz 2006-12-06
  • 打赏
  • 举报
回复
caoze(大漠孤烟)
你的提议很好 支持
frankiemaya 2006-12-06
  • 打赏
  • 举报
回复
caoze(大漠孤烟)
你的提议很好 支持
caoze 2006-12-06
  • 打赏
  • 举报
回复

谁能把它改为小说啊!!!

4,441

社区成员

发帖
与我相关
我的任务
社区描述
Linux/Unix社区 内核源代码研究区
社区管理员
  • 内核源代码研究区社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧