Linux 进程控制

一.进程的创建和终止

1.1 fork函数

1.1.1 fork函数的使用
cpp 复制代码
pid_t fork(void);

在之前, 曾经讲过fork函数的使用方式, 这里不再过多赘述。

1.1.2 fork函数的原理

当fork函数被调用时候, 进程切换到内核态进行以下操作:

  1. 分配新的内存块和内核数据结构给子进程
  2. 将父进程部分数据结构拷贝给子进程
  3. 添加子进程到系统进程列表中
  4. fork返回, 开始调度器的调度
1.1.3 写时拷贝

在子进程中, 父子代码通常是共享的, 父和子进程都不再写入时, 数据也是共享的, 但是当任何一方出访时写入, 便以写诗拷贝的方式各自持有一份副本。

1.2 exit和_exit函数

1.2.1 exit和_exit函数的用法
cpp 复制代码
void exit(int status);

void _exit(int status);
  • exit和_exit函数常用于进程终止退出。

参数:status 定义了进程的终止状态,父进程通过wait来获取该值

1.2.2 exit和_exit的区别
  • exit函数通常会执行用户定义的清理函数, 刷新缓冲区, 关闭流等操作, 然后再调用_exit函数
  • _exit则是直接终止该进程(类似于直接拔电源)

1.3 wait和waitpid函数

1.3.1 wait和waitpid函数的用法
cpp 复制代码
pid_t wait(int*status);

返回值:

  • 成功返回被等待进程pid,失败返回-1。

参数:

  • 输出型参数,获取子进程退出状态,不关心则可以设置成为NULL
cpp 复制代码
pid_ t waitpid(pid_t pid, int *status, int options);

返回值:

  • 当正常返回的时候waitpid返回收集到的子进程的进程ID;
  • 如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
  • 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;

参数:

  pid:
    Pid=-1,等待任一个子进程。与wait等效。
    Pid>0.等待其进程ID与pid相等的子进程。
  status:
    WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
    WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
  options:
    WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。
1.3.2 阻塞等待和非阻塞等待

阻塞等待

cpp 复制代码
#include <sys/wait.h>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cerrno>

int main()
{
    pid_t pid;
    pid = fork();
    if(pid < 0){
        return 1;
    }
    else if( pid == 0 ){ //child
        printf("child is run, pid is : %d\n",getpid());
        sleep(5);
        exit(257);
    }
    else{
        int status = 0;
        pid_t ret = waitpid(-1, &status, 0);//阻塞式等待,等待5S
        printf("this is test for wait\n");
        if( WIFEXITED(status) && ret == pid ){
            printf("wait child 5s success, child return code is :%d.\n",WEXITSTATUS(status));
        }
        else{
            printf("wait child failed, return.\n");
            return 1;
        } 
    }
    return 0;
}


非阻塞等待

cpp 复制代码
#include <sys/wait.h>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cerrno>

int main()
{
    pid_t pid;

    pid = fork();
    if(pid < 0){
        return 1;
    }
    else if( pid == 0 ){ //child
        printf("child is run, pid is : %d\n",getpid());
        sleep(5);
        exit(1);
    }
    else{
        int status = 0;
        pid_t ret = 0;
        do
        {
            ret = waitpid(-1, &status, WNOHANG);//非阻塞式等待
            if( ret == 0 ){
                printf("child is running\n");
            }
            sleep(1);
        } while(ret == 0);

        if( WIFEXITED(status) && ret == pid ){
            printf("wait child 5s success, child return code is :%d.\n",WEXITSTATUS(status));
        }
        else{
            printf("wait child failed, return.\n");
            return 1;
        }
    }
    return 0;
}

二.进程替换

2.1 什么是进程替换

新的程序替换当前正在运行的进程。这个过程通常涉及到终止当前进程的执行,并加载新的程序到内存中,然后开始执行新程序。

2.2 进程替换的原理

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

2.3 进程替换函数

cpp 复制代码
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[]);

函数解释:

  • l(list) : 表示参数采用列表
  • v(vector) : 参数用数组
  • p(path) : 有p自动搜索环境变量PATH
  • e(env) : 表示自己维护环境变量
cpp 复制代码
#include <sys/wait.h>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cerrno>
#include <unistd.h>
int main()
{
    char *const argv[] = {"ps", "-ef", NULL};
    char *const envp[] = {"PATH=/bin:/usr/bin", "TERM=console", NULL};    execl("/bin/ps", "ps", "-ef", NULL);
    // 带p的,可以使用环境变量PATH,无需写全路径
    execlp("ps", "ps", "-ef", NULL);
    // 带e的,需要自己组装环境变量
    execle("ps", "ps", "-ef", NULL, envp);
    execv("/bin/ps", argv);
    // 带p的,可以使用环境变量PATH,无需写全路径
    execvp("ps", argv);
    // 带e的,需要自己组装环境变量
    execve("/bin/ps", argv, envp);
    exit(0);
}

事实上,只有execve是真正的系统调用,其它五个函数最终都调用 execve。


感谢大家观看,不妨点赞支持一下吧喵~

如有错误,随时纠正喵~

相关推荐
Hcoco_me28 分钟前
密码学科普
服务器·网络·密码学
锦瑟居士42 分钟前
交叉编译--目标平台aarch64 ubuntu 22.04
linux·运维·ubuntu·机器人
luoganttcc1 小时前
ubuntu下route命令详解
linux·运维·ubuntu
HappyAcmen1 小时前
第六章:数据工程(6.5数据分析及应用--6.6数据脱敏和分类分级)
服务器·分类·数据分析
黄交大彭于晏1 小时前
韦东山老师linux快速入门otg检测不到方案总结
linux·运维·服务器
rjszcb3 小时前
Linux 内核态,用户态,以及如何从内核态到用户态,交互方式有哪些
linux·运维·服务器
Elihuss3 小时前
linux 串口调试工具和源码
linux·运维·算法
奶芙c3 小时前
【Linux】解锁软硬链接奥秘,高效动静态库管理的实战技巧
linux·运维·服务器·c++
IOT.FIVE.NO.14 小时前
Linux学习笔记9 文件系统的基础
linux·笔记·学习
IPdodo全球网络4 小时前
Facebook直播障碍的多重原因及解决方案
运维·服务器·facebook