进程创建
父子进程,代码共享,数据不修改的话也是共享的
所以子进程刚被创建时,所有数据都是共享的
修改进程之后,发生写时拷贝,父子进程关于该数据指向不同空间(虚拟地址相同,实际地址不同)
OS如何判断什么时候该写时拷贝
更新所有权限为只读--子进程写入--触发系统错误--缺页中断--系统检测 分情况处理(代码段-- 错误/数据区 --写时拷贝)
为什么要拷贝,而不是只开空间
因为修改数据不一定要覆盖原数据,如count++
进程终止
main函数返回值是返回给父进程
这个退出码可表明错误原因
echo $? error strerror
echo $? 最近一个程序退出码
error 是 全局变量
strerror(error) 错误码转字符串
进程终止的方式
main函数return
进程调用exit/_exit
exit & _exit
exit在退出前会把缓存区输出
_exit不会刷新缓冲区
exit封装了_exit
我们常说的缓冲区(printf)在什么位置
缓冲区一定不在操作系统内部
这个缓冲区叫做语言级缓冲区,是C/C++自己的缓冲区,和系统没关系
进程异常退出
进程异常:比如int a = 1 / 0; 程序还没运行完就遇到错误被终止
退出信号
退出信号:必须退出信号是零才表示正常退出,
所有退出信号:
我们可以使用kill 加 退出信号模仿各种错误, 使进程退出
进程等待
wait waitpid
声明
返回值
作用
wait回收子进程,结束子进程Z状态
返回值>0成功,小于零失败
waitpid pid==-1任意一个
status是输出型参数,本质是32bit位的位图,不仅包含正常退出码,还有异常退出码等信息,所以status和exit值可能不一样
status中退出码在[8, 15]位(索引)
由status求退出码, 退出信号:
由status返回值获取真正退出码:(status>>8) & oxff
阻塞等待 & 非阻塞等待
option为0时为阻塞等待, 等待时父进程不可进行其他任务
option为WNOHANG时为非阻塞等待, 此时要循环调用waitpid进行非阻塞等待,允许父进程在等待时进行其他任务
WNOHANG及Wait NO HANG, 非阻塞等待
非阻塞等待使用方法
#include <iostream>
#include <vector>
#include <cstdio>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <functional>
#include "task.h"
typedef std::function<void()> task_t;
void LoadTask(std::vector<task_t> &tasks)
{
tasks.push_back(PrintLog);
tasks.push_back(Download);
tasks.push_back(Backup);
}
int main()
{
std::vector<task_t> tasks;
LoadTask(tasks);
pid_t id = fork();
if(id == 0)
{
// child
while(true)
{
printf("我是子进程, pid : %d\n", getpid());
sleep(1);
}
exit(0);
}
// father
while(true)
{
sleep(1);
pid_t rid = waitpid(id, nullptr, WNOHANG);
if(rid > 0)
{
printf("等待子进程%d 成功\n", rid);
break;
}
else if(rid < 0)
{
printf("等待子进程失败\n");
break;
}
else
{
printf("子进程尚未退出\n");
// 做自己的事情
for(auto &task : tasks)
{
task();
}
}
}
}