当前位置: 首页 > news >正文

Linux系统编程基础:进程控制

在这里插入图片描述

文章目录

  • 一.子进程的创建
      • 操作系统内核视角下的父子进程存在形式
      • 验证子进程对父进程数据的写时拷贝
  • 二.进程等待
      • 进程非阻塞等待示例:
  • 三.进程替换
      • 内核视角下的进程替换过程:
      • 综合利用进程控制系统接口实现简单的shell进程

进程控制主要分为三个方面,分别是:子进程的创建,进程等待,进程替换
在这里插入图片描述

一.子进程的创建

  • 父进程调用fork()系统接口创建子进程后,操作系统会为子进程创建独立的PCB结构体虚拟地址空间mm_struct,因此父子进程之间具有互相独立性

操作系统内核视角下的父子进程存在形式

  • 父进程调用fork()函数之后:在这里插入图片描述

验证子进程对父进程数据的写时拷贝

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int g_val = 0;
int main(int argc,const char * argv[])
{//创建子进程pid_t id = fork();if(id < 0){//创建子进程失败,退出主函数perror("fork");return 0;}else if(id == 0)  {//子进程执行流//子进程对变量g_val进行修改,引发写时拷贝g_val=100;printf("childProcess[%d]: %d : %p\n", getpid(), g_val, &g_val);}else{//父进程的执行流//父进程先休眠3秒,让子进程先完成g_val变量的写入sleep(3);printf("parentProcess[%d]: %d : %p\n", getpid(), g_val, &g_val);}sleep(1);return 0;
}
  • 执行结果:在这里插入图片描述
  • 内核视角下,代码段中的写时拷贝图解:在这里插入图片描述
    在这里插入图片描述
  • 写时拷贝保证了父子进程之间互相独立的同时提高了计算机整机的内存使用效率

二.进程等待

  • 子进程退出后会进入僵尸状态,僵尸状态进程的内核数据结构对象会保留在操作系统内核空间中,在父进程读取子进程的退出信息后,操作系统才会释放掉子进程所占用的所有系统资源
    • 若父进程比子进程先退出,那么操作系统就会接管子进程(成为孤儿进程),子进程退出后,操作系统会自动读取子进程的退出信息.
  • 父进程读取子进程的退出信息这一过程称为进程等待
  • 进程等待常用系统接口:int waitpid(int pid, int *status, int options);
    • 形参int pid:在这里插入图片描述

    • 形参int *status:用于记录进程退出码和退出信号的输出型参数

    • 形参int options表示进程等待的方式:主要分为阻塞等待非阻塞等待两大类:

      • option0时:进程执行阻塞等待,此时进程会进入阻塞状态(PCB退出运行队列,进入阻塞队列),直到其所等待的子进程退出,该进程才会重新进入运行状态读取子进程的退出信息
      • option为非零时(比如使用系统宏WNOHANG):进程执行非阻塞等待, 此时进程会尝试读取其子进程的退出信息,若没能读取到子进程的退出信息,则waitpid系统接口立即返回0
    • 返回值int:若waitpid系统接口读取到子进程的退出信息,返回子进程的pid,若waitpid执行过程中出现错误,则返回-1

进程非阻塞等待示例:

#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
#include <stdlib.h>int main()
{int pid = 0;//创建子进程pid = fork();if(pid == -1){//子进程创建失败printf("fork failed\n");return -1;}else if(pid == 0){//子进程的代码执行流int cnt = 5;while(cnt--){sleep(1);printf("child process running\n");}exit(11);}else{//父进程的代码执行流//非阻塞等待子进程int status = 0;bool quit = 0;while(quit == 0){//循环非阻塞等待子进程int res =  waitpid(pid,&status,WNOHANG);if(res > 0){//子进程已退出printf("waiting pid success,chilProc exit,退出码:%d\n",WEXITSTATUS(status));quit = 1;}else if(res == 0){//子进程还未退出printf("waiting pid success,childProc still running\n");}else{//等待发生错误printf("waiting pid failed\n");}sleep(1);}}return 0;
}

在这里插入图片描述

三.进程替换

  • 进程替换的概念:通过特定的系统调用接口,操作系统可以将进程当前执行的代码段替换成指定系统路径下其他可执行程序的代码段,然后根据进程从头开始执行新的代码段(因此需要通过进程替换系统接口为新代码段传入main函数命令行参数)

内核视角下的进程替换过程:

在这里插入图片描述
在这里插入图片描述

  • 可见进程替换并不是创建新的进程(进程的PCB虚拟内存结构体不变)
  • 进程替换系统接口:
    在这里插入图片描述
  • 形参和返回值统一解释:
    • 参数const char*path:表示将要替换现有代码段的目标可执行程序的完整路径
    • 参数const char*file:表示将要替换现有代码段的目标可执行程序的文件名,其系统路径由环境变量PATH决定
    • 参数const char*arg,...:表示传给新代码段main函数命令行参数的字符串,...表示可变参数列表,可以传入多个字符串,最后一个字符串需传入NULL
    • 参数char *const argv[]:表示传给新代码段main函数命令行参数的字符串数组,数组中最后一个字符串需传入NULL
    • 参数cahr*const envp[]:表示传递给新代码段的环境变量字符串数组
    • 当进程替换失败时,exec系列系统接口会返回-1

综合利用进程控制系统接口实现简单的shell进程

  • shell进程的运行原理:shell进程接收到用户输入的指令后,对指令进行格式化处理,然后创建子进程,子进程通过exec系列系统接口将自身替换成系统命令并执行以响应用户需求,shell进程的这种运行机制保证了自身的进程安全(子进程出现错误不会影响到父进程的运行)
    在这里插入图片描述
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
#include <memory.h>//用户输入的命令行的最大长度
#define CStrLen 1024
//解析命令行得到的格式化字符串数组
#define SStrLen 50
//命令行字符串
char CommStr[CStrLen];
//格式化命令行字符串数组
char*  StdStr[SStrLen];//命令行分隔符
#define Sep " "
//操作系统配置的环境变量
extern char ** environ;
int main()
{while(1) {printf("[我的命令行解释器 myshell]$ ");fflush(stdout);memset(CommStr,'\0',sizeof StdStr);//获取用户输入命令if(fgets(CommStr,sizeof CommStr,stdin)== NULL){continue;}CommStr[strlen(CommStr)-1] = '\0';StdStr[0] = strtok(CommStr,Sep);//根据空格对用户输入的字符串进行分割并存入StdStr字符串数组中int i = 1;while(StdStr[i++] = strtok(NULL,Sep));//部分命令(比如cd命令)需要由shell进程自己来执行if(strcmp(StdStr[0],"cd")== 0){//用chdir函数改变shell进程的工作路径if(StdStr[1] != NULL){chdir(StdStr[1]);}continue;}//shell创建子进程来执行系统命令int pid = fork();if(pid == 0){//printf("shell的子进程执行系统命令:\n");execvp(StdStr[0],StdStr);printf("-mybash: %s: command execute failed\n",StdStr[0]);exit(-1);}else{int status;//父进程进行阻塞等待,等待子进程执行完系统命令结束并获取其退出码int waitres = waitpid(pid,&status,0);if(waitres == -1){printf("waitchild process failed\n");}}}return 0;
}

在这里插入图片描述
在这里插入图片描述

http://www.lryc.cn/news/180822.html

相关文章:

  • 选择和操作元素
  • 消息中间件(二)——kafka
  • 量化交易全流程(四)
  • idea 如何在命令行快速打开项目
  • YOLOV8-DET转ONNX和RKNN
  • 数量关系 --- 方程
  • 【C语言 模拟实现strlen函数的三种方法】
  • MySQL数据库与表管理《三国志》为例
  • D. Jellyfish and Mex - DP
  • 奥斯卡·王尔德
  • IDEA常用快捷键大全
  • Java之多线程的综合练习二
  • selenium下载安装 -- 使用谷歌驱动碰到的问题
  • 开放式耳机怎么选择、300之内最好的耳机推荐
  • git密码提交切换SSH提交
  • 数字乡村包括哪些方面?数字乡村应用介绍
  • 弹性资源组件elastic-resource设计(一)-架构
  • C/C++笔试面试真题
  • 【Vue3】兄弟组件传参
  • 【CSS 中 link 和@import 的区别】
  • 笔记二:odoo搜索、筛选和分组
  • Ubuntu Zookeeper开机自启动服务
  • 关于Matlab与Python中日期转时间戳不一致的问题
  • 【Django 笔记】第一个demo
  • 算法通过村第十一关-位运算|白银笔记|高频题目
  • 04、EL和JSTL核心技术
  • 【LeetCode热题100】--148.排序链表
  • 分布式并行训练(DP、DDP、DeepSpeed)
  • Linux- fg命令 bg命令
  • leetcode第362场周赛