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

《Linux 进程控制完全指南》

《Linux 进程控制完全指南》


文章目录

  • 《Linux 进程控制完全指南》
  • 一、进程创建
    • 1.1 fork函数初识
    • 1.2 fork函数返回值
    • 1.3 写时拷贝
    • 1.4 fork常规用法
    • 1.5 fork调用失败的原因
  • 二、程序终止
    • 2.1 进程退出场景
    • 2.2 进程常见退出方法
      • 2.2.1 退出码
      • 2.2.2 _exit函数
      • 2.2.3 exit函数
      • 2.2.4 return退出
  • 三、进程等待
    • 3.1 进程等待必要性
    • 3.2 进程等待的方法
      • 3.2.1 wait方法
      • 3.2.2 waitpid方法
      • 3.2.3 获取子进程status
      • 3.2.4 阻塞与非阻塞等待
        • 进程的阻塞等待方式
        • 进程的非阻塞等待方式
  • 四、进程程序替换
    • 4.1 替换原理
    • 4.2 替换函数
      • 4.2.1 函数解释
      • 4.2.2 命名理解
  • 五、自主Shell命令行解释器


一、进程创建

1.1 fork函数初识

在 linux 中 fork 函数是⾮常重要的函数,它从已存在进程中创建⼀个新进程。新进程为⼦进程,⽽原进程为⽗进程。
在这里插入图片描述


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


在这里插入图片描述


1.2 fork函数返回值

子进程返回0
父进程返回的是子进程的pid


1.3 写时拷贝

通常,父子代码共享,父子在不写入的时候,数据也是共享的,当任意一方试图写入,便以写时拷贝的方式各自一份副本


在这里插入图片描述


因为有写时拷贝计数的存在,所以父子进程得以彻底分离!完成进程独立性的技术保证
写时拷贝,是一种延时申请技术,可以提高整机内存的使用率


1.4 fork常规用法

在这里插入图片描述


1.5 fork调用失败的原因

在这里插入图片描述


二、程序终止

进程终止的本质是释放系统资源,就是释放进程申请的相关内核数据结构和对应的数据和代码


在这里插入图片描述


2.1 进程退出场景

在这里插入图片描述


2.2 进程常见退出方法


2.2.1 退出码

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


2.2.2 _exit函数

在这里插入图片描述


2.2.3 exit函数

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


2.2.4 return退出

return是一种更常见的退出进程方法。
执行return n等同于执行exit(n)
因为调用main的运行时函数会将main的返回值当作exit的参数


三、进程等待

3.1 进程等待必要性

在这里插入图片描述


3.2 进程等待的方法

3.2.1 wait方法

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


3.2.2 waitpid方法

在这里插入图片描述

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


3.2.3 获取子进程status

在这里插入图片描述

代码如下(示例):

#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main(void)
{pid_t pid;if ((pid = fork()) == -1)perror("fork"), exit(1);if (pid == 0) {sleep(20);exit(10);}else {int st;int ret = wait(&st);if (ret > 0 && (st & 0X7F) == 0) { // 正常退出printf("child exit code:%d\n", (st >> 8) & 0XFF);}else if (ret > 0) { // 异常退出printf("sig code : %d\n", st & 0X7F);}}
}
测试结果:
# ./a.out #等20秒退出
child exit code : 10
# ./a.out #在其他终端kill掉
sig code : 9

3.2.4 阻塞与非阻塞等待

进程的阻塞等待方式

代码如下(示例):

int main()
{pid_t pid;pid = fork();if (pid < 0) {printf("%s fork error\n", __FUNCTION__);}else if (pid == 0) { //childprintf("child is run, pid is : %d\n", getpid());sleep(5);exit(257);}else {int status = 0;pid_t ret = waitpid(-1, &status, 0);//阻塞式等待,等待5Sprintf("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;
}

在这里插入图片描述


进程的非阻塞等待方式

代码如下(示例):

include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include <vector>
typedef void (*handler_t)(); // 函数指针类型
std::vector<handler_t> handlers; // 函数指针数组
void fun_one() {printf("这是⼀个临时任务1\n");
}
void fun_two() {printf("这是⼀个临时任务2\n");
}
void Load() {handlers.push_back(fun_one);handlers.push_back(fun_two);
}
void handler() {if (handlers.empty())Load();for (auto iter : handlers)iter();
}
int main() {pid_t pid;pid = fork();if (pid < 0) {printf("%s fork error\n", __FUNCTION__);return 1;}else if (pid == 0) { // childprintf("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");}handler();} 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;
}

四、进程程序替换

在这里插入图片描述

在这里插入图片描述


4.1 替换原理

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


4.2 替换函数

有六种以exec开头的函数,统称exec函数
在这里插入图片描述

4.2.1 函数解释

在这里插入图片描述


4.2.2 命名理解

在这里插入图片描述

代码如下(示例):


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

在这里插入图片描述

五、自主Shell命令行解释器

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


代码如下(示例):

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <ctype.h>
using namespace std;
const int basesize = 1024;
const int argvnum = 64;
const int envnum = 64;
// 全局的命令⾏参数表
char* gargv[argvnum];
int gargc = 0;
// 全局的变量
int lastcode = 0;
// 我的系统的环境变量
char* genv[envnum];
// 全局的当前shell⼯作路径
char pwd[basesize];
char pwdenv[basesize];
// " "file.txt
#define TrimSpace(pos) do{\
while(isspace(*pos)){\
pos++;\
}\
}while(0)
string GetUserName()
{string name = getenv("USER");return name.empty() ? "None" : name;
}
string GetHostName()
{string hostname = getenv("HOSTNAME");return hostname.empty() ? "None" : hostname;
}
string GetPwd()
{if (nullptr == getcwd(pwd, sizeof(pwd))) return "None";snprintf(pwdenv, sizeof(pwdenv), "PWD=%s", pwd);putenv(pwdenv); // PWD=XXXreturn pwd;//string pwd = getenv("PWD");//return pwd.empty() ? "None" : pwd;
}
string LastDir()
{string curr = GetPwd();if (curr == "/" || curr == "None") return curr;// /home/whb/XXXsize_t pos = curr.rfind("/");if (pos == std::string::npos) return curr;return curr.substr(pos + 1);
}
string MakeCommandLine()
{// [whb@bite-alicloud myshell]$char command_line[basesize];snprintf(command_line, basesize, "[%s@%s %s]# ", \GetUserName().c_str(), GetHostName().c_str(), LastDir().c_str());return command_line;
}
void PrintCommandLine() // 1. 命令⾏提⽰符
{printf("%s", MakeCommandLine().c_str());fflush(stdout);
}
bool GetCommandLine(char command_buffer[], int size) // 2. 获取⽤⼾命令
{// 我们认为:我们要将⽤⼾输⼊的命令⾏,当做⼀个完整的字符串// "ls -a -l -n"char* result = fgets(command_buffer, size, stdin);if (!result){return false;}command_buffer[strlen(command_buffer) - 1] = 0;if (strlen(command_buffer) == 0) return false;return true;
}
void ParseCommandLine(char command_buffer[], int len) // 3. 分析命令
{(void)len;memset(gargv, 0, sizeof(gargv));gargc = 0;// "ls -a -l -n"const char* sep = " ";gargv[gargc++] = strtok(command_buffer, sep);// =是刻意写的while ((bool)(gargv[gargc++] = strtok(nullptr, sep)));gargc--;
}
void debug()
{printf("argc: %d\n", gargc);for (int i = 0; gargv[i]; i++){printf("argv[%d]: %s\n", i, gargv[i]);}
}
// 在shell中
// 有些命令,必须由⼦进程来执⾏
// 有些命令,不能由⼦进程执⾏,要由shell⾃⼰执⾏ --- 内建命令 built command
bool ExecuteCommand() // 4. 执⾏命令
{// 让⼦进程进⾏执⾏pid_t id = fork();if (id < 0) return false;if (id == 0){//⼦进程// 1. 执⾏命令execvpe(gargv[0], gargv, genv);// 2. 退出exit(1);}int status = 0;pid_t rid = waitpid(id, &status, 0);if (rid > 0){if (WIFEXITED(status)){lastcode = WEXITSTATUS(status);}else{lastcode = 100;}return true;}return false;
}
void AddEnv(const char* item)
{int index = 0;while (genv[index]){index++;}genv[index] = (char*)malloc(strlen(item) + 1);strncpy(genv[index], item, strlen(item) + 1);genv[++index] = nullptr;
}
// shell⾃⼰执⾏命令,本质是shell调⽤⾃⼰的函数
bool CheckAndExecBuiltCommand()
{if (strcmp(gargv[0], "cd") == 0){// 内建命令if (gargc == 2){chdir(gargv[1]);lastcode = 0;}else{lastcode = 1;}return true;}else if (strcmp(gargv[0], "export") == 0){// export也是内建命令if (gargc == 2){AddEnv(gargv[1]);lastcode = 0;}else{lastcode = 2;}return true;}else if (strcmp(gargv[0], "env") == 0){for (int i = 0; genv[i]; i++){printf("%s\n", genv[i]);}lastcode = 0;return true;}else if (strcmp(gargv[0], "echo") == 0){if (gargc == 2){// echo $?// echo $PATH// echo helloif (gargv[1][0] == '$'){if (gargv[1][1] == '?'){printf("%d\n", lastcode);lastcode = 0;}}else{printf("%s\n", gargv[1]);lastcode = 0;}}else{lastcode = 3;}return true;}return false;
}
// 作为⼀个shell,获取环境变量应该从系统的配置来
// 我们今天就直接从⽗shell中获取环境变量
void InitEnv()
{extern char** environ;int index = 0;while (environ[index]){genv[index] = (char*)malloc(strlen(environ[index]) + 1);strncpy(genv[index], environ[index], strlen(environ[index]) + 1);index++;}genv[index] = nullptr;
}
int main()
{InitEnv();char command_buffer[basesize];while (true){PrintCommandLine(); // 1. 命令⾏提⽰符// command_buffer -> outputif (!GetCommandLine(command_buffer, basesize)) // 2. 获取⽤⼾命令{continue;}//printf("%s\n", command_buffer);ParseCommandLine(command_buffer, strlen(command_buffer)); // 3. 分析命令if (CheckAndExecBuiltCommand()){continue;}ExecuteCommand(); // 4. 执⾏命令}return 0;
}

在这里插入图片描述

在这里插入图片描述


http://www.dtcms.com/a/393441.html

相关文章:

  • GitHub 热榜项目 - 日榜(2025-09-21)
  • 鹿鼎记豪侠传:Rust 重塑 iOS 江湖(上)
  • echarts监听dataZoom拖动缩放事件
  • Chrome学习小记3:基于Chrome Views框架创建最小示例窗口A(从Example分析开始)
  • Chrome学习小记2:GN构建系统小记
  • Chrome性能优化指南大纲
  • 【iOS】AFNetworking学习
  • Kafka 分层存储(Tiered Storage)原理、配置、快速上手与生产落地
  • 多元函数微分学核心概念辨析:连续、偏导与可微
  • 9.21 快选|倍增|栈+贡献法
  • AI.工作助手.工作提效率.AI应用开发平台
  • 【名人简历】鲁迅
  • linux文件系统基本管理
  • 2.1 进程与线程 (答案见原书 P57)
  • SDL2 开发详解
  • c++ 深拷贝之 std::string 与 char*
  • [数理逻辑] 决定性公理与勒贝格可测性(II) 一维情况
  • [Tongyi] DeepResearch Model | MODEL_PATH
  • 儿童对话玩具模型设计与实现
  • 生成器迁移的偏差消除条件
  • LeetCode 刷题【86. 分隔链表】
  • 回溯.专题
  • QML学习笔记(五)QML新手入门其三:使用Row和Colunm进行简单布局
  • 【视图功能11】视图权限控制与协作场景实践
  • YOLOv5至YOLOv12升级:交通标志识别系统的设计与实现(完整代码+界面+数据集项目)
  • 双指针算法案例:有序顺序表的交并差
  • syn和quote实现派生宏Builder
  • MQTT消息质量等级——QoS
  • 【OpenGL】shader 着色器
  • 给AI装上“眼睛”:Schema标记和技术性GEO实战部署