第3章 Unix/Linux进程管理
教材学习内容总结
本章主要讨论Unix/Linux中的进程管理。讲述了多任务处理,以及进程的相关知识:进程的概念;进程创建、终止。
还讲到了Unix/Linux进程管理的系统调用:fork()wait()exec()exit()。
1、多任务处理
多任务处理指的是同时执行几个独立任务的能力,是通过在不同任务之间多路复用CPU的执行时间来实现。
(1)多任务处理系统
type.h文件
type.h文件定义了系统常数和表示进程的简单PROC结构体。
ts.s文件
ts.s在32位GCC汇编代码中可实现进程上下文切换。
queue.c文件
queue.c文件可实现队列和链表操作函数。
enqueue()函数按优先级将PROC输入队列。在优先级队列中,具有相同优先级的进程按先进先出(FIFO).
dequeue()函数可返回从队列或链表中的第一个元素。printlist()函数可打印链表元素。
t.c文件
t.c文件定义MT系统数据结构、系统初始化代码和进程管理函数。
2、进程
(1)进程的概念
操作系统是一个多任务处理系统。在操作系统中,任务也称为进程。在实际应用中,任
务和进程这两个术语可以互换使用。
进程的正式定义:进程是对映像的执行。
(2)进程同步
进程同步是指控制和协调进程交互以确保其正确执行所需的各项规则和机制。
最简单的进程同步工具是休眠和唤醒操作。
睡眠模式:
有时候,进程需要等待直到某个特定的事件发生,例如设备初始化完成、I/O 操作完成或定时器到时等。
在这种情况下,进程则必须从运行队列移出,加入到一个等待队列中,这个时候进程就进入了睡眠状态。
为实现休眠操作,我们可以在 PROC结构体中添加一个event字段,并实现ksleep(int event)函数,使进程进入休眠状态。
进程睡眠状态有两种:
一种是可中断的睡眠状态,其状态标志位TASK_INTERRUPTIBLE
另一种是不可中断的睡眠状态,其状态标志位为TASK_UNINTERRUPTIBLE
唤醒操作:
当某个等待时间发生时,另一个执行实体(可能是某个进程或中断处理程序)将会调用 kwakeup(event)。
唤醒正处于休眠状态等待该事件值的所有程序。
如果没有任何程序休眠等待该程序,kwakeup()就不工作,即不执行任何操作。
(3)进程终止
正常终止:进程调用exit(value),发出_exit(value)系统调用来执行在操作系统内核中的kexit(value)。
异常终止:进程因某个信号而异常终止。
3、I/O重定向
(1)文件流和文件描述符
每个文件流都是指向执行映像堆区中FILE结构体的一个指针。每个文件流对应Linux内核中的一个打开文件。每个打开文件都用一个文件描述符(数字)表示。
(2)文件流I/O和系统调用
当进程执行库函数
scanf("%s",&item);
它会试图从stdin文件输入一个(字符串)项,指向FILE结构体。如果FILE结构体的fbuf[]
4、管道
管道是用于进程交换数据的单向进程间通信通道。管道有一个读取端和一个写入端。可从管道的读取端读取写入管道写入端的数据
(1)管道命令处理
在Unix/Linux中命令行
cmd1 | cmd2
包含一个管道符号 “ | ” 。
(2)命名管道
命名管道又叫做FIFO。它们有“名称”,并在文件系统中以特殊文件的形式存在。
示例:
①在sh中,通过mknod命令创建一个命令管道:
②或者在C语言中发出mknod()系统调用
③进程可像访问普通文件一样发个文命名管道。
5、Unix/Linux进程管理中的系统调用
(1)fork()
函数原型
pid_t fork( void);
(pid_t 是一个宏定义,其实质是int 被定义在#includesys/types.h>中)
返回值: 若成功调用一次则返回两个值,子进程返回0,父进程返回子进程ID;否则,出错返回-1。
在fork函数执行完毕后,如果创建新进程成功,则出现两个进程,一个是子进程,一个是父进程。在子进程中,fork函数返回0,在父进程中,fork返回新创建子进程的进程ID。通过fork返回的值来判断当前进程是子进程还是父进程。
(2)wait()
函数原型
int wait(int *status)
父进程一旦调用了wait就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。
(3)exec()
函数原型
*#include <unistd.h>*
*int execl( const char *pathname, const char *arg0, ... /* (char *)0 */ );*
*int execv( const char *pathname, char *const argv[] );*
*int execle( const char *pathname, const char *arg0, ... /* (char *)0, char *const envp[] */ );*
*int execve( const char *pathname, char *const argv[], char *const envp[] );*
*int execlp( const char *filename, const char *arg0, ... /* (char *)0 */ );*
*int execvp( const char *filename, char *const argv[] );*
返回值:六个函数的返回值,若出错则返回-1,若成功则不返回值
用fork函数创建子进程后,子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程执行的程序完全替换为新程序,而新程序则从其main函数开始执行。因为调用exec并不创建新进程,所以前后的进程ID并未改变。exec只是用一个全新的程序替换了当前进程的正文、数据、堆和栈段。
(4)exit()
函数原型
int atexit(void (*func)(void));
其中参数是一个函数指针, 指向终止处理函数, 该函数无参无返回值。
6、实践
(1)exit()函数
(2)fork()函数
(3)env
参考
https://www.runoob.com/cprogramming/c-standard-library-stdlib-h.html
https://www.cnblogs.com/bastard/archive/2012/08/31/2664896.html