进程控制

进程创建

父子进程,代码共享,数据不修改的话也是共享的

所以子进程刚被创建时,所有数据都是共享的

修改进程之后,发生写时拷贝,父子进程关于该数据指向不同空间(虚拟地址相同,实际地址不同)

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();
            }
        }
    }

}

进程程序替替换

execl接口:

用法:


execl最后一个参数必须nullptr结尾(main中arg以nullptr结尾)

作用
execl用调用的程序替换本条语句(代码和数据段), 会覆盖本进程所用代码段/数据段(此进程此后代码不会执行)

进程替换是创建新的进程吗
不是,只是更改了数据段和代码段PCB其他数据不变

execl返回值


成功时没有返回值(将代码数据都覆盖了),以后的代码也不会执行
失败时会有返回值-1,不会覆盖

要想execl后的其他代码也被执行,就要用子进程执行execl,父进程执行其他代码(execl会将本进程代码&数据全部覆盖)

execl本质是把程序加载到内存

命令行解释器:循环接受用户命令,传递给子进程的execl执行

使用execl执行python/bash脚本
代码

认识全部接口



只有execve是系统调用,其他都是封装execve所得,所以这些接口只是传参方式不同。

execv 和execl唯一区别为将多参数列表放在char*数组中,没有任何本质区别

execlp / execvp


不用带路径,绝对路径替换为文件名即可

execlpe / execvpe
 

关于环境变量

新增 / 追加环境变量:putenv(), 为调用该接口的进程增加指定环境变量

tips:

exec家族中

l / v  必选

p  e 可选

如果不加p, 即使在环境变量目录中, 也要写完整路径。

加p则可自动在环境变量寻找filename, 从而不用绝对路径

板书笔记

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值