(1)进程
进程是程序在某个数据集上的执行过程,它包括一个地址空间和至少一个控制点,进程在这个地址空间上执行单一的指令序列。 进程地址空间是指包括可以访问或引用的内存单元的集合,进程控制点通过一个称为程序计数器(PC,program counter)的硬件寄存器控制和跟踪进程指令序列。
在Linux 中,每个进程都由一个task_struct 数据结构来描述,它占据一定的内存空间。 当系统创建一个进程时,Linux 为新的进程分配一个task_struct 结构。 同时指向该结构的指针将被加入到task 数组中,用来记录系统中进程数目的全局变量会自加“1”;进程结束时又回收task_struct 结构,进程也随之消失,全局变量自减“1”。
进程的创建有两种方式:一种是系统创建,另一种是由父进程创建。 Linux 系统提供了系统调用fork,可拷贝现行进程的内容,以产生新的进程,调用fork 的进程称为父进程,而调用后所产生的新进程称为子进程。 子进程会承袭父进程的一切特性,但是它有自己的数据段;也就是说,尽管子进程改变了所属的变量,却不会影响到父进程的变量值。 父进程和子进程共享一个程序段,但是各自拥有自己的堆栈、数据段、用户空间以及进程控制块。 换言之,两个进程执行的程序代码是一样的,但是各有各的程序计数器与自己的私人数据。
当内核收到fork 请求时,它首先检查存储器是不是够用,其次检查进程表是否仍有空缺,最后检查用户是否建立了太多的子进程。 如果满足上述三个条件,则操作系统为子进程分配一个进程识别码,并且设定CPU 时间,接着设定与父进程共享的程序段,同时将父进程的inode 拷贝一份给子进程使用,最终子进程会返回,数值“0”表示它是子进程,至于父进程,它可能等待子进程的执行结束,或与子进程各做各的。
exec 系统调用,提供一个进程去执行另一个进程的能力,exec 系统调用的是采用覆盖旧有进程存储器内容的方式,原来程序的堆栈、数据段与程序段都会被修改,只有用户区维持不变。
由于在使用fork 时,内核会将父进程拷贝一份给子进程,但是这样的做法相当浪费时间,因为大多数情形都是程序在调用fork 后就立即调用exec,这样刚拷贝来的进程区域又立即被新的数据覆盖掉。 为了提高调用效率,Linux 系统提供了一个系统调用vfork,vfork 假定系统在调用完成vfork 后会马上执行exec,因此vfork 不拷贝父进程的页面,只是初始化的数据结构与准备足够的分页表。 这样实际在vfork 调用完成后父子进程事实上共享同一块存储器(在子进程调用exec 或是exit 之前),子进程可以更改父进程的数据及堆栈信息。 因此,vfork系统调用完成后,父进程进入睡眠状态,直到子进程执行exec。 当子进程执行exec 时,由于exec 要使用被执行程序的数据,代码覆盖子进程的存储区域,这样将产生写保护错误(do_wp_page)(这时子进程写的实际上是父进程的存储区域),这个错误将导致内核为子进程重新分配存储空间。 当子进程开始正确执行后,将唤醒父进程,使得父进程继续往后执行。
(2)进程状态
①运行:进程或者正在运行(是系统中的当前进程)或者已经准备好运行(等待被分配系统的CPU)。 处于该状态的进程实际参与了进程调度。
②等待:进程正在等待一个事件或一个资源。 等待进程又分为可中断的和不可中断的。可中断等待进程可以由其他进程通过信号中断和唤醒,而不可中断等待进程直接等待硬件条件,并且在任何环境下都不会被中断。
③停止:进程被暂停执行,通过接收一个信号才能被唤醒。 一个正在被调试的进程可以处于停止状态。(www.xing528.com)
④死亡:进程已终止,是进程结束运行前的一个过渡状态(僵死状态),等待父进程将它彻底释放。
⑤交换:进程页面被交换出内存的进程。
(3)进程调度
进程实际是某特定应用程序的一个运行实体。 在Linux 系统中,能够同时运行多个进程,Linux 通过在短的时间间隔内轮流运行这些进程而实现“多任务”。 这一短的时间间隔称为“时间片”,让进程轮流运行的方法称为“调度”,完成调度的程序称为“调度程序”。 通过多务机制,每个进程可认为只有自己独占计算机,从而简化程序的编写。 每个进程有自己单独的地址空间,并且只能由这一进程访问,这样,操作系统避免了进程之间的互相干扰以及“坏”程序对系统可能造成的危害。
常用的进程调度算法有:先来先服务(FIFO)、时间片轮转(RR)、优先权调度算法、多级反馈队列调度算法等。 Linux 进程调度大致可分为两种情况:一种是通过schedule()启动一次调度,另一种是强制性调度。 当进程运行于用户空间时,可能产生强制性调度,运行于内核空间则不会发生。 对于普通进程,Linux 采用动态优先级调度。 对于实时进程,Linux 采用FIFO 和RR 两种调度策略。
(4)进程通信
为了完成某特定任务,有时需要综合两个程序的功能。 例如,一个程序输出文本,而另一程序对文本进行排序。 为此,操作系统还提供进程间的通信机制来帮助完成这样的任务。Linux 中常见的进程间通信机制有信号、消息、管道、共享内存、信号量和套接字等。
信号是UNIX 系统使用最早的进程间通信机制,主要用于向一个或多个进程发异步事件信号。 Linux 使用存储在每个进程task struct 结构中的信息实现信号机制。 它支持的信号数量受限于处理器的字长,32 位字长的处理器有32 种信号,64 位字长的处理器有64 种信号。
管道用于连接一个读进程和一个写进程,以实现它们之间通信的共享文件,又称“pipe 文件”。 在Linux 系统中,将两个file 结构指向同一个临时VFS 索引节点,并且此VFS 索引节点又指向内存中的一个物理页,这样管道就实现了。 这两个file 数据结构所定义的文件操作例程地址不同:一个是向管道中写入数据的例程地址,另一个是从管道中读出数据的例程地址。管道的写过程是将字节拷贝到共享数据页面中,而读过程则是将数据从共享数据页面中读出来。 由于任意的两个进程无法共享同一个管道,除非是同一“祖先”创建的管理。 因此,Linux提供了命名管道(又名FIFO 管道),FIFO 可被任何进程存取。
Linux 支持UNIX 系统V 版本中的三种进程间通信机制(PC):消息队列、信号量和共享内存。 系统将消息队列、信号量和共享内存定义为system V 的PC 对象,每个PC 对象都有一个唯一的标识号。 这些System V 的进程间通信机制使用相同的认证方法,即通过系统调用向内核传递这些资源的全局唯一标识来访问它们,Linux 用ipc_perm 数据结构描述system V 对象,定义在include/linux/ipc.h 中。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。