Chinaunix首页 | 论坛 | 博客
  • 博客访问: 405012
  • 博文数量: 128
  • 博客积分: 2247
  • 博客等级: 大尉
  • 技术积分: 767
  • 用 户 组: 普通用户
  • 注册时间: 2010-06-17 09:30
文章分类

全部博文(128)

文章存档

2011年(4)

2010年(124)

我的朋友

分类:

2010-07-28 12:22:19

进程是Unix操作系统最基本的抽象之一(另一个基本 的抽象是文件)。一个进程就是出于执行期的程序(目标码存放在某种存储介质上)。但进程并不仅仅局限于一段可执行程序代码。通常进程还要包含其他资源,像 打开的文件、挂起的信号、内核内部数据、处理器状态、地址空间及一个或多个执行线程(thread of execution),当然还包括用来存放全局变量的数据段等。进程就是正在执行的程序代码的活标本。(姥姥的,抽象的东西就是不好理解)。

    在现在的操作系统中,进程提供两种虚拟机制: 虚拟处理器和虚拟内存。虽然实际上可能是许多进程正在分享一个处理器,但虚拟处理器给进程一种假象,让这些进程觉得自己在独享处理器。下一章将详细描述这 一机制。而虚拟内存让进程在获取和使用内存时觉得自己拥有整个系统的所有内存资源。

    通常,创建新的进程都是为了立即执行新的、不同的程序,而接着调用exec*()这族函数就可以创建新的地址空间,并把新的程序载入。最终,程序通过exit()系统调用退出执行。这个函数会终结进程并将其占用的资源释放。        

进程的另一个名字是任务。

3.1 进程描述符及任务结构

    内核把进程存放在叫做任务队列的双向循环链表中。链表的每一项都是类型为task_struct(定义在)、称为进程描述符(process descriptor)的结构,进程描述符中包含一个具体进程的所有信息。

3.1.1 进程描述符的分配????????????

3.1.2 进程描述符的存放

    进程通过一个唯一的进程标识值(PID)来标识每个进程。PID是一个数,表示为pid_t隐含类型,实际上就是一个int类型。PID的最大默认设置为32768

    在内核中,访问任务通常需要获得指向其task_struct指针。实际上,内核中大部分处理进程的代码都是通过task_struct进行的。通过current宏查找当前正在运行的进程的进程描述符的速度就显得尤为重要。该宏的实现取决于硬件体系结构。

3.1.3 进程的状态

进程描述符中的state域描述了进程的当前状态。系统中的每个进程都必然处于五个进程状态中的一种。该域的值也必为下列五种状态标志之一:

(1)TASK_RUNNING——进程是可执行的;它或者正在执行,或者在运行队列中等待执行。

(2)TASK_INTERRUPTIBLE(可中断)——进程正在睡眠(被阻塞),等待某些条件达成。

(3)TASK_UNINTERRUPTIBLE(不可中断)——除了不会因为接收到信号而被唤醒从而投入运行外,这个状态与可中断状态相同。

(4)TASK_ZOMBIE(僵死)——该进程已经结束,但是父进程还没有调用wait4()系统调用。wait4()起到查询作用,僵死状态(进程描述符仍然被保留着)做两件事,为父进程提供必要的消息,等待wait4()信号,以释放进程描述符。

(5)TASK_STOPPED(停止)——进程停止执行;进程没有投入运行也不能投入运行。

3.1.4 设置当前进程状态

三个常用函数:set_task_state(task,state); set_current_state(state)<=>set_task_state(current,state)。

3.1.5 进程上下文

可执行程序代码是程序 的重要组成部分。这些代码从可执行文件载入到进程的地址空间执行。一般程序在用户空间执行。当一个程序执行了系统调用或者触发了某个异常,它就陷入内核空 间。此时,我们称内核“代表进程执行”并处于进程上下文中。系统调用和异常处理程序是对内核明确定义的接口。进程只有通过这些接口才能陷入内核执行——对 内核的所有访问都必须通过这些接口。

3.1.6 进程家族树

3.2 fork()

    linux通过clone()系统调用来实现fork()。clone()调用do_fork()。do_fork()再调用copy_process()函数,然后让程序开始运行。下面我们来看一下copy_process()都做了哪些工作:

(1)调用dup_task_struct()为新进程创建一个内核栈、thread_info和task_struct,这些值与当前进程的值相同。此时,子进程与父进程的描述符完全相同。

(2)检查新创建的这个子进程,当前用户所拥有的进程数目没有超出给他分配的资源的限制。然后子进程开始着手使自己与父进程区别开来,进程描述符内的许多成员都要清0或设为初始值。

(3)子进程的状态被设置为TASK_UNINTERRUPTIBLE以保证它不会投入运行。

(4)copy_process()调用copy_flags()以更新task_struct的flags成员。表明进程是否拥有超级用户权限的PF_SUPERPRIV标志被清0。表明函数还没有调用exec()函数的PF_FORKNOEXEC标志被设置。

(5)调用get_pid()为新的进程获取一个有效的PID。

(6)根据传递给clone()的参数标志,copy_process()拷贝或共享打开的文件,文件系统信息、信号处理函数、进程地址空间和命名空间等。

(7)让父进程和子进程平分剩余的时间片。

(8)最后,copy_process()作扫尾i工作并返回一个指向子进程的指针。

最后再回到do_fork()函数,如果copy_process()函数返回成功,新创建的子进程被唤醒并被投入运行,内核有意选择子进程首先执行。

3.3 线程在linux中的实现

   线程机制是现代编程技术中常用的一种抽象。该机制提供了在同一程序内共享内存地址空间的运行的一组线程(可以理解为轻量级进程)。这些线程可以共享打开的 文件和其他资源。Linux实现线程的机制非常独特,从内核的角度出发,它没有线程这个概念。Linux把所有线程作为进程来实现。线程仅仅被视为一个与 其它进程共享某些资源的进程,它看起来像一个普通的进程一样,只是该进程和其他一些进程共享了某些资源,如地址空间)。因此,线程的创建和普通进程类似, 只不过在调用clone()的时候需要传递一些参数标志来指明需要共享的资源:clone(CLONE_VM| CLONE_FS | CLONE_FILES | CLONE_SIGHAND,0)。

3.4 进程的终结

阅读(731) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~