Linux系统编程---进程

1.程序和进程

程序:指编译好的二进制文件,在磁盘上,不占用系统资源(CPU、内存、打开文件、设备、锁······)

进程:进程是活跃的程序,占用系统资源,是一个抽象的概念,与操作系统原理联系紧密。在内存中执。

程序是静态的概念,进程是动态的概念。程序运行起来,产生一个进程。

2.并发和并行

并发:同一时刻,只能有一条指令执行,多个进程指令被快速的轮换执行。

宏观:有多个进程同时运行的效果。

微观:只是把时间分成若干段,使多个进程快速交替的执行。

并行:同一时刻,有多条指令在多个处理器上同时执行。

异同点

相同点:并发和并行的目标都是最大化CPU的使用率,将CPU的性能充分压榨出来。

不同点:(1) 并行在多处理器系统中存在,而并发可以在单处理器和多处理器系统中都存在

(2)并行要求程序能同时执行多个操作,而并发只是要求程序“看着像是”同时执行多个操作,其本质是交替执行。

3.进程控制块PCB

每个进程在内核中都有一个进程控制块(PCB)来维护进程的相关信息,Linux内核的进程控制块是task_struct结构体。

/user/src/linux-headers-3.16.0-30/include/linux /sched.h文件中可以查看struct task_struct结构体定义。重点掌握以下:

* 进程id。系统中每个进程有唯一的id,在C语言中用pid_t类型表示,其实就是一个非负整数。

* 进程的状态,初始态、就绪态、运行态、挂起态、终止态。

* 进程切换时需要保存和恢复的一些CPU寄存器。

* 描述虚拟地址空间的信息。

* 描述控制终端的信息。

* 当前工作目录(Current Working Directory)。

* umask掩码。

* 文件描述符表,包含很多指向file结构体的指针。

* 和信号相关的信息。

* 用户id和组id。

* 会话(Session)和进程组。

* 进程可以使用的资源上限(Resource Limit)。

4.进程的状态

进程的基本状态有5种:

创建状态:进程在创建时需要申请一个空白的进程控制块(PCB),向其中填写控制和管理进程的信息,完成资源分配。如果创建失败,比如资源无法满足,就无法被调度运行,此时的状态成为创建状态。

就绪状态:进程已经准备好,已分配到所需资源,只要分配到CPU就能立即运行。

执行状态:进程处于就绪状态被调度后,进程进入执行状态。

阻塞状态:正在执行的进程由于某些事件(I/O请求,申请缓存区失败)而暂时无法运行,进程受到阻塞。在满足请求时进入就绪状态等待系统调用。

终止状态:进程结束或出现错误,或被系统终止,进入终止状态,无法再执行。

进程的五种状态及转换

5.环境变量

环境变量:指在操作系统中用来指定操作系统运行环境的一些参数。常具备以下特征:

  1. 字符串(本质)

  1. 有统一的格式: 名= 值 [:值]

  1. 值用来描述进程环境信息。

6.常用进程控制函数

fork函数

创建一个子进程

pid_t fork(void) 失败返回-1,成功返回非负整数。

父进程返回子进程的id,子进程返回0

pid_t 类型表示进程ID,是int类型。

getpid函数

获取当前进程id。

getppid函数

获取当前进程的父进程id。

7.进程共享

使用fork后,父子进程的异同点

相同点:全局变量、.data、.text 、堆 、栈 、环境变量......

不同点:进程id 、 fork返回值 、 进程运行时间......

8. exec族函数

fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。

将当前进程的.text、.data替换为所要加载的程序的.text、.data,然后让进程从新的.text第一条指令开始执行,但进程ID不变,换核不换壳。

其实有六种以exec开头的函数,统称exec函数:

int execl(const char *path, const char *arg, ...);

int execlp(const char *file, const char *arg, ...);

int execle(const char *path, const char *arg, ..., char *const envp[]);

int execv(const char *path, char *const argv[]);

int execvp(const char *file, char *const argv[]);

int execve(const char *path, char *const argv[], char *const envp[]);

execlp函数

加载一个进程,借助PATH环境变量

int execlp(const char *file, const char *arg, ...); 成功:无返回;失败:-1

参数1:要加载的程序的名字。该函数需要配合PATH环境变量来使用,当PATH中所有目录搜索后没有参数1则出错返回。

该函数通常用来调用系统程序。如:ls、date、cp、cat等命令。

execl函数

加载一个进程,通过路径+程序名 来加载。

int execl(const char *path, const char *arg, ...); 成功:无返回;失败:-1

对比execlp,如加载"ls"命令带有-l,-F参数

execlp("ls", "ls", "-l", "-F", NULL); 使用程序名在PATH中搜索。

execl("/bin/ls", "ls", "-l", "-F", NULL); 使用参数1给出的绝对路径搜索。

execvp函数

加载一个进程,使用自定义环境变量env

int execvp(const char *file, const char *argv[]);

变参形式:①... ② argv[] (main函数也是变参函数,形式上等同于 int main(int argc, char *argv0, ...))

变参终止条件:① NULL结尾 ② 固参指定

execvp与execlp参数形式不同,原理一致。

练习:将当前系统中的进程信息,打印到文件中。 【exec_ps.c】

exec函数族一般规律

exec函数一旦调用成功即执行新的程序,不返回。只有失败才返回,错误值-1。所以通常我们直接在exec函数调用后直接调用perror()和exit(),无需if判断。

l (list) 命令行参数列表

p (path) 搜素file时使用path变量

v (vector) 使用命令行参数数组

e (environment) 使用环境变量数组,不使用进程原有的环境变量,设置新加载程序运行的环境变量

事实上,只有execve是真正的系统调用,其它五个函数最终都调用execve,所以execve在man手册第2节,其它函数在man手册第3节。这些函数之间的关系如下图所示。

exec函数族

9.回收子进程

孤儿进程

孤儿进程:父进程先于子进程结束,则子进程为孤儿进程,子进程别init进程领养。

僵尸进程

僵尸进程:进程终止,父进程尚未回收,子进程残留资源(PCB)存放于内核中,变成僵尸进程。

僵尸进程不能用kill命令清除掉,因为kill命令只能用来终止进程,而僵尸进程已经终止。

wait函数

一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的PCB还保留着,内核在其中保存了一些信息:如果是正常终止则保存着退出状态,如果是异常终止则保存着导致该进程终止的信号是哪个。这个进程的父进程可以调用wait或waitpid获取这些信息,然后彻底清除掉这个进程。我们知道一个进程的退出状态可以在Shell中用特殊变量$?查看,因为Shell是它的父进程,当它终止时Shell调用wait或waitpid得到它的退出状态同时彻底清除掉这个进程。

父进程调用wait函数可以回收子进程终止信息。该函数有三个功能:

① 阻塞等待子进程退出

② 回收子进程残留资源

③ 获取子进程结束状态(退出原因)。

pid_t wait(int *status); 成功:清理掉的子进程ID;失败:-1 (没有子进程)

当进程终止时,操作系统的隐式回收机制会:1.关闭所有文件描述符 2. 释放用户空间分配的内存。内核的PCB仍存在。其中保存该进程的退出状态。(正常终止→退出值;异常终止→终止信号)

可使用wait函数传出参数status来保存进程的退出状态。借助宏函数来进一步判断进程终止的具体原因。宏函数可分为如下三组:

1. WIFEXITED(status) 为非0 → 进程正常结束

WEXITSTATUS(status) 如上宏为真,使用此宏 → 获取进程退出状态 (exit的参数)

2. WIFSIGNALED(status) 为非0 → 进程异常终止

WTERMSIG(status) 如上宏为真,使用此宏 → 取得使进程终止的那个信号的编号。

*3. WIFSTOPPED(status) 为非0 → 进程处于暂停状态

WSTOPSIG(status) 如上宏为真,使用此宏 → 取得使进程暂停的那个信号的编号。

WIFCONTINUED(status) 为真 → 进程暂停后已经继续运行

waitpid函数

作用同wait,但可指定pid进程清理,可以不阻塞。

pid_t waitpid(pid_t pid, int *status, in options); 成功:返回清理掉的子进程ID;失败:-1(无子进程)

特殊参数和返回情况:

参数pid:

> 0 回收指定ID的子进程

-1 回收任意子进程(相当于wait)

0 回收和当前调用waitpid一个组的所有子进程

< -1 回收指定进程组内的任意子进程

返回0:参3为WNOHANG,且子进程正在运行。

注意:一次wait或waitpid调用只能清理一个子进程,清理多个子进程应使用循环。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值