目录
一、进程控制
1. 进程退出
2. 进程等待
2.1 阻塞等待
2.2 status位图结构
2.3 非阻塞等待
二、进程替换
1. exec*系列函数
2. 进程替换高级语言可执行程序
一、进程控制
1. 进程退出
进程退出会进入僵尸态,把自己的退出结果写入到自己的task_struct中
- exit() 库函数:终止进程时主动刷新缓冲区
- _exit() 系统调用:终止进程时不刷新缓冲区
#include <stdio.h>
int main() 
{
    printf("hello world");
    sleep(2);
    //exit(1);        //两秒后打印
    //_exit(1);       //两秒后不打印
    return 0;
}代码退出的情况:
- 代码跑完,结果正确 ---- return 0;
- 代码跑完,结果错误 ---- return !0; 退出码有意义
- 代码未跑完,报异常 ---- 退出码无意义
进程退出的时候,有对应的退出码,标定进程执行的结果是否正确。
退出码:
- return 0, 进程退出码为 0,标定进程执行的结果正确
- 如何设置返回值退出码?
- 如果不关心进程退出码,直接return 0 即可
- 如果关心进程退出码,返回 0 表示正确,返回非0用特定的数据表明特定的错误
- echo $? 查看上一个进程的退出码,? 是一个变量,永远存放上一个进程退出的退出码
打印错误码:
int main() 
{
    for (int i = 0; i < 100; ++i) 
    {
        printf("%d : %s\n", i, strerror(i));
    }
    return 0;
}2. 进程等待
- 去除子进程僵尸态,获取子进程退出结果
- pid_t wait(int* status):成功则返回被等待进程的pid,失败则返回-1
- pid_t waitpid(pid_t id, int* status, int option):option为0代表阻塞等待
- wait/waitpid 是一个系统调用 ---> os有能力和资格去读取进程的task_struct
2.1 阻塞等待
子进程未退出,父进程只能阻塞在此等待子进程结束
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main() 
{
    pid_t id = fork();
    if (id == 0) 
    {
        int cnt = 5;
        while (cnt) 
        {
            printf("子进程:%d, 父进程:%d, cnt = %d\n", getpid(), getppid(), cnt--);
            sleep(1);
        }
        exit(10);
    }
    //父进程
    int status = 0;    // 保存退出码
    pid_t ret = waitpid(id, &status, 0);    //option为 0 代表阻塞等待
    if (id > 0) 
    {
        printf("wait success: %d, sign: %d, exit code: %d\n", ret, (status & 0x7F), (status>>8) & 0xFF);
    }
    return 0;
}
2.2 status位图结构

- status退出信号是一个32bit位的位图,只有低16个bit位存储退出信息
- (status & 0x7F) 为终止信号, (status>>8) & 0xFF) 为退出状态
- 异常退出,被信号杀死
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main() 
{
    pid_t id = fork();
    if (id == 0) 
    {
        int cnt = 5;
        while (cnt) 
        {
            printf("子进程:%d, 父进程:%d, cnt = %d\n", getpid(), getppid(), cnt--);
            sleep(1);
        }
        int a = 10;
        a /= 0;         //除 0 异常
        exit(10);
    }
    //父进程
    int status = 0;
    pid_t ret = waitpid(id, &status, 0);    //option为 0 代表阻塞等待
    if (id > 0) 
    {
        printf("wait success: %d, sign: %d, exit code: %d\n", ret, (status & 0x7F), (status>>8) & 0xFF);
    }
    return 0;
}8号终止信号代表浮点数异常
2.3 非阻塞等待
- 非阻塞等待:子进程未退出,父进程检测之后立即返回
- 非阻塞等待的意义:不用占用父进程的所有精力,可以在等待期间执行自己的任务
- WIFEXITED(status):是否正常退出,若正常退出则返回值非零
- WEXITSTATUS(status):提取子进程退出码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#define NUM 10
typedef void (*func_t)();   //函数指针
func_t handlerTask[NUM];
//测试样例
void Task1() 
{
    printf("handler task1\n");
}
void Task2() 
{
    printf("handler task2\n");
}
void Task3() 
{
    printf("handler task3\n");
}
//给父进程装载任务 
void LoadTask() 
{
    memset(handlerTask, 0, sizeof(handlerTask));
    handlerTask[0] = Task1;
    handlerTask[1] = Task2;
    handlerTask[2] = Task3;
}
int main() 
{
    pid_t id = fork();  //返回的本质就是写入, 写入时发生写时拷贝
    if (id < 0) 
    {
        printf("folk error\n");
        return 1;
    }
    else if (id == 0) 
    {
        int cnt = 5;
        while (cnt) 
        {
            printf("我是子进程: %d, 父进程: %d, cnt = %d\n", getpid(), getppid(), cnt--);
            sleep(1);
            // int* p = NULL;
            // *p = 100;       //野指针报错, 退出信号为11
        }
        exit(10);
    }
    LoadTask();
    int status = 0;     //不是被整体使用的, 有自己的位图结构
    //非阻塞轮旋方式等待
    while (1) 
    {
        pid_t ret = waitpid(id, &status, WNOHANG);  //非阻塞等待: 子进程未退出, 父进程检测之后立即返回
        if (ret == 0) 
        {
            //子进程未退出, waitpid等待失败, 仅仅是检测到子进程状态未退出
            for (int i = 0; handlerTask[i] != NULL; ++i)
                handlerTask[i]();
        }
        //等待子进程退出成功
        else if (ret > 0) 
        {
            //是否正常退出
            if (WIFEXITED(status)) 
            {
                //正常退出, WIFEXITED()返回值为!0
                //判断子进程运行结果是否正确
                printf("exit_code: %d\n", WEXITSTATUS(status));    
            }
            else 
            {
                //异常退出,被信号杀死
                printf("child process not normal\n");
            }
            break;
        }
        else 
        {
            // waitpid调用失败
            printf("waitpid call failed\n");
            break;
        }
        sleep(1);
    }
    return 0;       
}
二、进程替换
进程替换:将指定进程的代码加载到指定位置,覆盖自己的代码和数据
1. exec*系列函数
加载器的底层接口,可替换任何后端语言的可执行程序
- int execl(const char* path, const char* arg, ...),可变参数列表以NULL结尾
- int execlp(const char* file, const char* arg, ...),可自动在环境变量中寻找路径
- int execv(const char* path, char* const arg[]),可变参数数组以NULL结尾
- int execvp(const char* file, char* const arg[])
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
int main() 
{
    printf("process is running\n");
    pid_t id = fork();
    if (id == 0) 
    {
        sleep(1);
        //execl("/usr/bin/ls", "ls", "-a", "-l", "--color=auto", NULL);
        //execlp("ls", "ls", "-a", "-l", "--color=auto", NULL);
        char* const arg[] = {"ls", "-a", "-l", "--color=auto", NULL};
        //execv("/usr/bin/ls", arg);
        execvp("ls", arg);
        exit(10);   //若exc*调用成功,则此句代码被替换
    }
    int status = 0;
    pid_t ret = waitpid(id, &status, 0);
    if (ret > 0) 
    {
        printf("wait success, sig = %d, exit code = %d\n", (status & 0x7F), (status>>8) & 0xFF);
    }
    printf("process is running done\n");
    return 0;       
}
execve是系统调用,其他都是封装
- int execle(const char* path, const char* arg, ... , char* const envp[]),可传环境变量
- int execve(const char* path, char* const arg[], char* const envp[])
- int execvpe(const char* file, char* const arg[], char* const envp[])
2. 进程替换高级语言可执行程序
创建一个test.c文件并编译 gcc -o test test.c
#include <stdio.h>
#include <stdlib.h>
int main() 
{
    printf("C语言程序\n");
    printf("C语言程序\n");
    printf("PATH: %s\n", getenv("PATH"));
    printf("PWD: %s\n", getenv("PWD"));
    printf("MYENV: %s\n", getenv("MYENV"));
    printf("C语言程序\n");
    printf("C语言程序\n");
    exit(15);
}创建一个py_test.py文件,chmod +x py_test.py
用进程替换这个程序,并打印环境变量
#!/bin/python
print('python process')
print('python process')
print('python process')
print('python process')
print('python process')进程替换程序:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
int main() 
{
    printf("process is running\n");
    pid_t id = fork();
    if (id == 0) 
    {
        sleep(1);
        putenv((char*)"MYENV=123456");     //添加自定义环境变量
        extern char** environ;
        execle("./test", "test", NULL, environ);
        exit(10);   //若exc*调用成功,则此句代码被替换
    }
    int status = 0;
    pid_t ret = waitpid(id, &status, 0);
    if (ret > 0) 
    {
        printf("wait success, sig = %d, exit code = %d\n", (status & 0x7F), (status>>8) & 0xFF);
    }
    execl("./py_test.py", "py_test.py", NULL);
    printf("process is running done\n");
    return 0;       
}运行结果:




















