Linux中进程的控制

一、进程的创建

1、知识储备

进程的创建要调用系统接口,头文件 #include<unistd.h> 函数fork()

由于之前的铺垫我们现在可以更新一个概念

进程 = 内核数据结构(task_struct, mm_struct, 页表....)+ 代码 + 数据

所以如何理解进程的独立性呢?

就是上述组成进程的结构都是独立的,互不影响的。

那为什么 fork() 返回的是子进程的pid呢?

是为了让父进程对子进程进行表示和管理。

2、理解fork()

(1)fork() 常见用法

a、在一个代码中通过 fork() 不同的返回值用 if else分流,以达到不同进程执行不同代码的目的。

b、可以让父子进程独立进行完全不同的代码。

(2)fork() 失败的原因

a、操作系统中进程太多。

b、实际用户创建的进程太多,超出规定个数。

二、进程的终止

首先我们要知道进程是先创建内核数据结构,再加载代码和数据。

1、终止的表现

会释放代码和数据所占据的空间,释放内核数据结构(task_struct 会延期释放)

2、终止的三种情况

(1)观察现象

首先我们先观察一个现象

当我们以前在写c语言代码时,我们都是 return 0; 结尾,当我们今天 return 100; 时,用 echo $? 命令获取进程的退出码时,就会发现退出码和 return 后面的数字是一样的。

(2)解释 echo $?

echo:内建命令,打印bash内部变量数据。

$:访问变量内容。

?:父进程bash获取最近一个子进程的退出码。

(3)退出码

退出码0是成功,非0是失败。

每一个非0数都有不同的失败原因。

头文件 #include<string.h>    函数 char* strerror(int num) 可以查看退出码。

上图只截了一部分退出码。

退出码的作用就是告诉父进程,子进程把任务完成的怎么样。

结论:当代码跑完时,结果的正确与否是由退出码反馈给用户的。

(4)退出信号

与退出码不同,退出信号是代码还没跑完,系统就崩溃了(操作系统发现进程做了不该做的事,比如访问野指针等等),操作系统就会杀死进程。

所以一旦出现异常,退出码也就没有意义了,但是返回的退出信号是有意义的。

例如:Segmentation fault 就是段错误,代码段是有错误的。

退出信号的本质:操作系统给进程发信号并终止进程。

之前我们说kill -9 可以直接杀死进程也就是这个原因,而我们上面提到的段错误 Segmentation fault 就是操作系统发送了 kill -11 信号。

(5)总结

衡量一个进程的退出,只要看退出码和退出信号。

先确认是否异常,若不是才看退出码。

所以在子进程的 task_struct 中就会有 exit_code(退出码)  exit_signal(退出信号),在执行完代码后写入退出码和退出信号供父进程读取。

3、如何终止

(1)main()函数中的 return 代表进程终止(普通函数 return 表示函数结束)

(2)调用头文件#include<stdlib.h> 函数 void exit(int status) status相当于退出码,exit等价于return

(3)调用头文件#include<unistd.h> 函数 void _exit(int status),基本与exit函数相同,一个是c库函数,一个是系统调用。

区别

exit 执行时会冲刷缓冲区,但是 _exit不会,所以其实这里所说的缓冲区只是c库层面的,不是系统调用层面的缓冲区。

exit本质就是底层调用 _exit

三、进程等待

任何子进程在退出的情况下必须要被父进程等待。如果父进程不管,子进程就会处于僵尸状态,导致内存泄漏。

三、进程的等待

1、为什么父进程要等待子进程

(1)解决僵尸问题,回收系统资源。(必须)

(2)获取子进程退出信息,知道子进程为什么退出。(可选)

2、怎么等待

(1)wait 函数

头文件 #include<sys/types.h> #include<sys/wait.h>

函数 pid_t wait(int* status)

等待成功返回子进程的pid,wait(NULL)表示父进程等待任意一个子进程退出。

若子进程一直不退出,父进程就进入阻塞等待。

阻塞等待的本质:把父进程设为非运行状态(S),链入到子进程队列中,子进程退出,父进程唤醒。

(2)waitpid 函数

头文件 #include<sys/types.h> #include<sys/wait.h>

函数 pid_t waitpid(pid_t id, int* status, int options)

waitpid(-1, NULL, 0)等价于wait(NULL)

a、理解参数 id

id 表示要等待哪个子进程

所以理解代码:

pid_t id = fork();

waitpid(id, NULL, 0);

在父进程中fork()返回子进程pid,所以就指定了要返回哪个子进程了。

b、理解参数 status

status是输出型参数(例如 scanf(),就是把数值写入到一个变量中),输出的是退出信息(包括退出码和退出信号)

理解额代码:

我们知道int有32位bit位,status只考虑低16位

获取退出码:(status>>8) & 0xFF

获取退出信息:status & 0x7F

c、获取退出码的宏

WIFEXITED(status)     代码正常走完就返回真

WEXITSTATUS(status)    若WIFEXITED为真就提取退出码

(3)非阻塞等待

上述父进程都是等子进程跑完之后才工作的,这种都是阻塞等待,调用waitpid默认也是阻塞等待,但是我们想让父进程在等待时做其他事情,就要让 waitpid 中 option 设成 WNOHANG

此时返回值 < 0:等待失败。

返回值 = 0:检测成功,但是子进程未退出,等待下一次检测。

返回值 > 0:等待成功,并且父进程回收成功。

非阻塞等待 + 循环 = 非阻塞轮询

达到父进程能做其他事情。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值