................................................................进程控制.................................................................
一.操作系统: 操作系统的主要任务是借助进程来管理计算机的软、硬件资源。
1.基本特征有:
*并发性:两个或者多个事件在同一时刻发生,为了使程序能够并发执行,系统必须分别为每个程序建立进程。
*共享:系统中的资源可供内存中多个并发执行的进程共同使用,由于资源的属性不同,多个进程对资源的共享方式也不同,分为:互斥共享方式和同时访问方式。
*虚拟:通过技术把一个物理实体变成若干个逻辑上的对应物,在操作系统中虚拟的实现主要是通过分时的方法。
*异步:异步性使得操作系统运行在一种随机的环境下,可能导致进程产生与时间有关的错误 (就像对全局变量的访问顺序不当会导致程序出错一样)。但是只要运行环境相同,操作系统必须保证多次运行进程,都获得相同的结果。
二:linux操作系统:linux是一个多用户多任务的操作系统。
其中多用户是指多个用户可以在同一时间使用计算机。
多任务是指linux可以同时执行几个任务,可以在还未执行完一个任务时又执行另一个任务。注:****linux可以同时启动多个进程。
**三:进程:是并发执行的程序在执行过程中分配和管理资源的基本单位,是一个动态的实体。
1.进程和程序的区别:
进程是动态的,程序是静态的;
进程是运行中的程序,程序是一些保存在硬盘上的可执行的代码。
2.进程标识:每个进程都是通过唯一的进程ID标识的,进程ID是一个非负数。
相关函数:
pid_t getpid(id):获得进程ID,
pid_t getppid(id):获得父进程ID,
pid_t getuid():获得进程的实际用户ID(即:运行该进程的用户),
pid_t geteuid():获得进程的有效用户ID(即:以什么用户身份来运行进程),
pid_t getgid():获得进程的实际组ID,
3.linux进程的结构:
由三部分组成:代码段,数据段,堆栈段。
代码段:存放程序的可执行代码。
数据段:存放程序的全局变量,常量,静态变量。
堆栈段:堆用于存放动态分配的内存变量,栈用于函数调用,存放函数定义的参数,函数内部定义的局部变量。
4.linux进程状态:
运行状态(R):进程正在运行火灾运行队列中等待运行。
可中断等待状态(S):进程正在等待某个事件完成,等待过程中可以被信号或者定时器唤醒。
不可中断等待状态(D):进程正在等待某个事件完成,等待过程中不可以被信号或者定时器唤醒,直到等待的事件发生。
僵死状态(Z):进程已终止,但进程描述符依然存在,直到父进程调用wait()函数后释放。
停止状态(T):进程已停止运行或者该进程正在被追踪。
5.进程控制:
fork():用于创建一个新进程。
exec():用于执行一个应用程序。
wait():将父进程挂起,等待子进程执行完毕后再继续执行。
exit():用于终止进程。
6.C程序的生成分为4个阶段:预编译->编译->汇编->链接
其中编译器通过预编译,编译,汇编将源程序文件转换为目标文件。如果程序中有多个目标文件或者程序中使用了库函数,编译器将所有的目标文件或所需的库链接起来,最后生成可执行程序。
7.进程的内存映像:是指内核在内存中如何存放可执行程序文件。
**在将程序转化为进程的过程中,操作系统将可执行程序由硬盘复制到内存中。
代码段:二进制机器代码,只读,可被多个进程共享。
数据段:存储已被初始化的变量,包括全局变量和静态变量。
未初始化数据段:存储未被初始化的静态变量。
堆:存放程序运行中动态分配的变量。
栈:用于函数调用,保存函数的返回地址,函数的参数,函数内部定义的局部变量。
8.进程的操作:
(1) 创建进程:进程被创建时系统会为其分配一个唯一的进程ID。
进程的创建有两种方式:一是由操作系统创建,二是由父进程创建(称为子进程)。
二者区别:由操作系统创建的进程之间是平等的,不存在资源继承关系,而父进程和子进程之间存在隶属关系,子进程又可以创建进程,子进程可以继承其父进程几乎所有的资源。进程调用fork()函数创建了一个子进程,fork():调用一次,返回两次(前提是进程创建成功)。
父子进程在调用fork函数的时候分开,其中父进程返回新创建子进程的ID,子进程返回值为0。
**:fork之后是子进程先执行还是父进程先执行是不确定的,这取决于内核所使用的调度算法。
***fork()与vfork():***:二者都是创建一个新线程,
:使用fork创建一个子进程时,子进程只是完全复制父进程的资源,即子进程独立于父进程,具有良好的并发性。
:使用vfork创建一个子进程共享父进程的地址空间,即子进程完全运行在父进程的地址空间上,子进程对该地址空间中的数据的修改同样为父进程所见。
:fork创建子进程后,哪个进程先运行取决于系统的调度算法,vfork创建子进程后,保证子进程先运行,调用exec或exit后,父进程才可能被调度运行。想要父进程等待子进程执行完毕之后再继续执行,在fork()操作之后调用wait(),
(2)执行新程序:子进程调用exec函数来执行另外一段程序。
(3)等待进程结束:wait()使父进程暂停执行,直到它的一个子进程结束为止。返回终止运行的子进程的PID,
注:waitpid():用来等待某个特定的进程结束。
(4)进程退出:exit()使进程正常退出。
**正常退出: ~在main函数中执行return ~调用exit() ~调用_exit()
注:exit()与_exit()的区别:
~exit()在头文件stdlib.h,_exit()在头文件unistd.h中,
~_exit()执行后立即返回给内核,exit()要先执行一些清除操作,再将控制权交给内核。
**异常退出: ~调用abort函数 ~进程收到某个信号,该信号使程序终止。
***子进程与父进程的联系与区别:
*** ~子进程会继承父进程的很多属性,包括用户ID,组ID,当前工作目录,根目录,创建文件时使用的屏蔽字,信号屏蔽字,上下文环境,共享的存储段,资源限制等。
~子进程有它自己唯一的进程ID, ~fork返回值不同,父进程返回子进程的ID,子进程返回0,
~子进程不继承父进程设置的文件锁,
~子进程不继承父进程设置的警告,
***孤儿进程***:一个子进程的父进程先于子进程结束,子进程就成为一个孤儿进程,由init进程收养。
***init进程***:1号进程,pid为1的进程,又称init进程。
linux系统启动后,第一个被创建的用户态进程就是init进程。它有两项使命:
1、执行系统初始化脚本,创建一系列的进程(它们都是init进程的子孙);
2、在一个死循环中等待其子进程的退出事件,并调用waitid系统调用来完成“收尸”工作;
init进程不会被暂停、也不会被杀死(这是由内核来保证的)。它在等待子进程退出的过程中处于可中断的等待状态,“收尸”过程中则处于运行状态。
这个进程可以是很多进程的父进程,当一个进程结束的时候,操作系统会访问所有的进程,看看进程是不是这个结束进程的子进程,如果是就把这个进程的父进程设置成init进程(pid=1),(ps命令查看init的父进程号是0,但是史蒂文森说可以把init的父进程看成它本身)。init进程还有一点特殊的,当它的一个子进程结束后,它立刻的调用wait函数接受子进程返回的信息,回收剩余的资源,防止产生僵尸进程。
***僵尸进程***:如果一个进程结束,但是这个进程的父进程没有调用wait函数来做善后处理(获取信息,释放资源),即子进程先于父进程结束,那么这个进程就是僵尸进程(这个进程已经结束,但是还是占用着进程号)。
***守护进程:***也就是通常说的 Daemon 进程(精灵进程),是 Linux 中的后台服务进程。它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程是个特殊的孤儿进程,这种进程脱离终端,为什么要脱离终端呢?之所以脱离于终端是为了避免进程被任何终端所产生的信息所打断,其在执行过程中的信息也不在任何终端上显示。由于在 linux 中,每一个系统与用户进行交流的界面称为终端,每一个从此终端开始运行的进程都会依附于这个终端,这个终端就称为这些进程的控制终端,当控制终端被关闭时,相应的进程都会自动关闭。
进程的三种基本状态
进程在运行中不断地改变其运行状态。通常,一个运行进程必须具有以下三种基本状态。
就绪(Ready)状态
当进程已分配到除CPU以外的所有必要的资源,只要获得处理机便可立即执行,这时的进程状态称为就绪状态。
执行(Running)状态
当进程已获得处理机,其程序正在处理机上执行,此时的进程状态称为执行状态。
阻塞(Blocked)状态
正在执行的进程,由于等待某个事件发生而无法执行时,便放弃处理机而处于阻塞状态。引起进程阻塞的事件可有多种,例如,等待I/O完成、申请缓冲区不能满足、等待信件(信号)等。
2.进程三种状态间的转换
一个进程在运行期间,不断地从一种状态转换到另一种状态,它可以多次处于就绪状态和执行状态,也可以多次处于阻塞状态。图3_4描述了进程的三种基本状态及其转换。
(1) 就绪→执行
处于就绪状态的进程,当进程调度程序为之分配了处理机后,该进程便由就绪状态转变成执行状态。
(2) 执行→就绪
处于执行状态的进程在其执行过程中,因分配给它的一个时间片已用完而不得不让出处理机,于是进程从执行状态转变成就绪状态。
(3) 执行→阻塞
正在执行的进程因等待某种事件发生而无法继续执行时,便从执行状态变成阻塞状态。
(4) 阻塞→就绪
处于阻塞状态的进程,若其等待的事件已经发生,于是进程由阻塞状态转变为就绪状态。