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。


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

如有错误,随时纠正喵~

相关推荐
秋名山小桃子3 分钟前
Kunlun 2280服务器(ARM)Raid卡磁盘盘符漂移问题解决
运维·服务器
与君共勉121383 分钟前
Nginx 负载均衡的实现
运维·服务器·nginx·负载均衡
岑梓铭10 分钟前
(CentOs系统虚拟机)Standalone模式下安装部署“基于Python编写”的Spark框架
linux·python·spark·centos
努力学习的小廉10 分钟前
深入了解Linux —— make和makefile自动化构建工具
linux·服务器·自动化
MZWeiei14 分钟前
Zookeeper基本命令解析
大数据·linux·运维·服务器·zookeeper
7yewh29 分钟前
嵌入式Linux QT+OpenCV基于人脸识别的考勤系统 项目
linux·开发语言·arm开发·驱动开发·qt·opencv·嵌入式linux
Arenaschi33 分钟前
在Tomcat中部署应用时,如何通过域名访问而不加端口号
运维·服务器
小张认为的测试33 分钟前
Linux性能监控命令_nmon 安装与使用以及生成分析Excel图表
linux·服务器·测试工具·自动化·php·excel·压力测试
waicsdn_haha41 分钟前
Java/JDK下载、安装及环境配置超详细教程【Windows10、macOS和Linux图文详解】
java·运维·服务器·开发语言·windows·后端·jdk
打鱼又晒网41 分钟前
linux网络套接字 | 深度解析守护进程 | 实现tcp服务守护进程化
linux·网络协议·计算机网络·tcp