【Linux】进程控制,手搓简洁版shell

头像
⭐️个人主页:@小羊
⭐️所属专栏:Linux
很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~

动图描述

目录

    • 1、进程创建
    • 2、进程终止
    • 3、进程等待
    • 4、进程程序替换
    • 5、手写简洁版shell


1、进程创建

fork函数:从已经存在的进程中创建一个新进程。新进程为子进程,原进程为父进程。

进程调用fork,当控制转移到内核中的fork代码后,内核做:

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

写时拷贝 (懒拷贝,时间换空间)

在这里插入图片描述

数据在默认不修改的情况下是共享的,不各自拷贝一份是因为父子进程间的数据大部分是重复的,一般只有少量数据需要修改,因为各自拷贝一份浪费空间。

更新父进程页表项为只读—子进程继承—子进程写入—触发系统错误—系统触发缺页中断—系统检测—判定是否要写时拷贝—拷贝,修改,恢复权限。

创建出子进程,让子进程执行一些任务:

#include <iostream>
#include <vector>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>using namespace std;enum 
{OK,OPEN_FILE_ERROR
};vector<int> data;int savebegin()
{string name = to_string((unsigned int)time(nullptr));name += ".backup";FILE *pf = fopen(name.c_str(), "w");if (pf == nullptr){return OPEN_FILE_ERROR;}string datastr;for (auto d : data){datastr += to_string(d);datastr += " ";}fputs(datastr.c_str(), pf);//将拿到的数据备份到文件中fclose(pf);return OK;
}void save()
{pid_t id = fork();if (id == 0){//子进程备份数据int code = savebegin();exit(code);}int status = 0;pid_t rid = waitpid(id, &status, 0);if (rid > 0){int code = WEXITSTATUS(status);//进程退出码if (code == 0)cout << "备份成功, exit code:" << code << endl;elsecout << "备份失败,exit code:" << code << endl;}else{perror("waitpid");}
}int main()
{int cnt = 1;while (true){data.push_back(cnt++);sleep(1);if (cnt % 10 == 0){save();}}return 0;
}

上面的代码中子进程每隔10秒备份一份数据。


2、进程终止

main函数的返回值->返回给父进程或系统。

在这里插入图片描述
进程终止的方式:

  1. main函数中的return:只有main函数中的return才能终止进程
  2. exit(库函数):在代码的任何地方,结束进程

在这里插入图片描述

  1. _exit(系统调用接口):

在这里插入图片描述
这是因为我们所说的缓冲区是语言级别的缓冲区(C/C++),所以_exit(系统调用接口)接触不到。


3、进程等待

  • 等待的时候,如果子进程不退出,父进程就会阻塞在wait函数内部。

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

我们只能通过系统调用获取退出信息。
在这里插入图片描述

  • waitwaitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。
  • 如果传递NULL,表示不关心子进程的退出状态信息。
  • 否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。
  • status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究status低16比特位):

在这里插入图片描述

进程退出:

  1. 代码跑完,结果对,return 0
  2. 代码跑完,结果不对,return !0
  3. 进程异常,OS提前用信号终止进程,进程退出信息中也会记录退出信号

如果我们想看一个进程结果是否正确,前提这个进程退出信号为0,说明这个进程是正常跑完的,但结果是对还是不对需要看退出码来判断。
在这里插入图片描述
除了上面的位操作获取退出码,还可以使用系统提供的相关宏:

  • WIFEXITED(status): 若为正常终止子进程返回的状态,则为真(查看进程是否是正常退出)
  • WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码(查看进程的退出码)

阻塞和非阻塞
非阻塞等待即让父进程在等待子进程的过程中去做一些自己的事。

typedef function<void()> task_t;void LoadTask(vector<task_t>& tasks)
{tasks.push_back(PrintLog);tasks.push_back(DownLoad);tasks.push_back(BackUp);
}int main()
{vector<task_t> tasks;LoadTask(tasks);//加载任务pid_t id = fork();if (id == 0){while (true){cout << "我是子进程,pid:" << getpid() << endl;sleep(1);}exit(0);}while (true)//阻塞循环等待{sleep(1);pid_t rid = waitpid(id, nullptr, WNOHANG);if (rid > 0){cout << "等待子进程" << rid << "成功" << endl;break;}else if (rid < 0){cout << "等待子进程失败" << endl;break;}else{cout << "子进程尚未退出" << endl;//父进程做一些自己的事for (auto& task : tasks){task();}}}
}

4、进程程序替换

上面的子进程执行的都是父进程的部分代码,如果我们想让子进程执行一个全新的程序呢?
在这里插入图片描述

替换原理:
用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数来执行另一个程序,当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,开始执行新程序。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
如果给上面的代码加上死循环,再让运行时读取指令,不就是一个简单的命令解释器吗?

程序替换不影响命令行参数和环境变量。


5、手写简洁版shell

现在写的shell没有维护自己的环境变量表,是继承自父shell,我们当然也可以维护自己shell的环境变量表。

#include <iostream>
#include <cstring>
#include <string>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.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;//全局的shell工作路径
char pwd[basesize];
char pwdenv[basesize];//myshell的环境变量表
char *genv[envnum];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 (getcwd(pwd, sizeof(pwd)) == NULL){return "None";}snprintf(pwdenv, sizeof(pwdenv), "PWD=%s", pwd);putenv(pwdenv);//PWD=xxx  更新环境变量return pwd;
//	string pwd = getenv("PWD");
//	return pwd.empty() ? "None" : pwd;
}string LastDir()
{//  /home/yjz/code/xxxstring cur = GetPwd();if (cur == "/" || cur == "None")return cur;size_t pos1 = cur.rfind("/");if (pos1 == string::npos)return cur;size_t pos2 = pos1 == 0 ? pos1 : pos1 - 1;size_t pos3 = cur.rfind("/", pos2);if (pos3 == string::npos)return cur;return cur.substr(pos3);
}string MakeCmdLine()
{char cmd_line[basesize];snprintf(cmd_line, basesize, "%s@%s:~%s$ ", GetUserName().c_str(), GetHostName().c_str(), LastDir().c_str());return cmd_line;
}void PrintCmdLine()
{printf("%s", MakeCmdLine().c_str());fflush(stdout);
}bool GetCmdLine(char buffer[], int size)
{char *result = fgets(buffer, size, stdin);//从标准输入流中读取输入指令if (result){buffer[strlen(buffer) - 1] = '\0';//去掉输入指令时的回车键if (strlen(buffer) == 0){return false;//如果是空串直接返回}return true;}return false;
}void ParseCmdLine(char buffer[], int len)
{//初始化memset(gargv, 0, sizeof(gargv));gargc = 0;const char *delim = " ";gargv[gargc++] = strtok(buffer, delim);while (gargv[gargc++] = strtok(NULL, delim));gargc--;
}//void debug()
//{
//	printf("argc: %d\n", gargc);
//	for (int i = 0; gargv[i]; i++)
//	{
//		printf("agrv[%d}: %s\n", i, gargv[i]);
//	}
//}//执行解析好的命令,为了防止程序崩溃挂掉,让子进程执行
bool ExeCmd()
{pid_t id = fork();if (id < 0){return false;}if (id == 0){//将myshell的环境变量表传给子进程execvpe(gargv[0], gargv, genv); 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(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] = NULL;
}//在shell中,有些命令,必须由子进程执行
//有些命令,只能shell自己执行——内建命令
//shell自己执行命令,本质是shell调用自己的函数
bool CheckAndExeBuiltCmd()
{if (strcmp(gargv[0], "cd") == 0){//内建命令if (gargc == 2){chdir(gargv[1]);}else{lastcode = 1;}return true;}else if (strcmp(gargv[0], "export") == 0){if (gargc == 2){AddEnv(gargv[1]);}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){if (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,获取环境变量应该从系I统的配置文件中读取
//这里直接从父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] = NULL;
}int main()
{InitEnv();char cmd_buffer[basesize];//获取指令缓冲区while (true){	PrintCmdLine();//1、命令行提示符if(!GetCmdLine(cmd_buffer, basesize))//2、获取用户命令{continue;}//printf("%s\n", cmd_buffer);ParseCmdLine(cmd_buffer, strlen(cmd_buffer));//3、分析命令//debug(); if (CheckAndExeBuiltCmd()){continue;}ExeCmd();//4、执行命令}return 0;
}

本篇文章的分享就到这里了,如果您觉得在本文有所收获,还请留下您的三连支持哦~

头像

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/482049.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

逆向攻防世界CTF系列42-reverse_re3

逆向攻防世界CTF系列42-reverse_re3 参考&#xff1a;CTF-reverse-reverse_re3&#xff08;全网最详细wp&#xff0c;超4000字有效解析&#xff09;_ctfreverse题目-CSDN博客 64位无壳 _int64 __fastcall main(__int64 a1, char **a2, char **a3) {int v4; // [rsp4h] [rbp-…

安装 RabbitMQ 服务

安装 RabbitMQ 服务 一. RabbitMQ 需要依赖 Erlang/OTP 环境 (1) 先去 RabbitMQ 官网&#xff0c;查看 RabbitMQ 需要的 Erlang 支持&#xff1a;https://www.rabbitmq.com/ 进入官网&#xff0c;在 Docs -> Install and Upgrade -> Erlang Version Requirements (2) …

ECharts柱状图-交错正负轴标签,附视频讲解与代码下载

引言&#xff1a; 在数据可视化的世界里&#xff0c;ECharts凭借其丰富的图表类型和强大的配置能力&#xff0c;成为了众多开发者的首选。今天&#xff0c;我将带大家一起实现一个柱状图图表&#xff0c;通过该图表我们可以直观地展示和分析数据。此外&#xff0c;我还将提供…

Scala关于成绩的常规操作

score.txt中的数据&#xff1a; 姓名&#xff0c;语文&#xff0c;数学&#xff0c;英语 张伟&#xff0c;87&#xff0c;92&#xff0c;88 李娜&#xff0c;90&#xff0c;85&#xff0c;95 王强&#xff0c;78&#xff0c;90&#xff0c;82 赵敏&#xff0c;92&#xff0c;8…

【机器学习】入门机器学习:从理论到代码实践

我的个人主页 我的领域&#xff1a;人工智能篇&#xff0c;希望能帮助到大家&#xff01;&#xff01;&#xff01;点赞❤ 收藏❤ 机器学习&#xff08;Machine Learning&#xff09;是人工智能的一个分支&#xff0c;它通过算法从数据中学习规律&#xff0c;并基于这些规律进行…

Spring Web开发(请求)获取JOSN对象| 获取数据(Header)

大家好&#xff0c;我叫小帅今天我们来继续Spring Boot的内容。 文章目录 1. 获取JSON对象2. 获取URL中参数PathVariable3.上传⽂件RequestPart3. 获取Cookie/Session3.1 获取和设置Cookie3.1.1传统获取Cookie3.1.2简洁获取Cookie 3. 2 获取和存储Session3.2.1获取Session&…

[Deep Learning] 深度学习中常用函数的整理与介绍(pytorch为例)

文章目录 深度学习中常用函数的整理与介绍常见损失函数1. L2_loss | nn.MSELoss()公式表示&#xff1a;特点&#xff1a;应用&#xff1a;缺点&#xff1a;主要参数&#xff1a;示例用法&#xff1a;注意事项&#xff1a; 2. L1 Loss | nn.L1Loss数学定义&#xff1a;特点&…

0017. shell命令--tac

目录 17. shell命令--tac 功能说明 语法格式 选项说明 实践操作 注意事项 17. shell命令--tac 功能说明 Linux 的 tac 命令用于按行反向输出文件内容&#xff0c;与 cat 命令的输出顺序相反。非常有趣&#xff0c;好记。也就是说&#xff0c;当我们使用tac命令查看文件内…

SpringBoot整合Retry详细教程

问题背景 在现代的分布式系统中&#xff0c;服务间的调用往往需要处理各种网络异常、超时等问题。重试机制是一种常见的解决策略&#xff0c;它允许应用程序在网络故障或临时性错误后自动重新尝试失败的操作。Spring Boot 提供了灵活的方式来集成重试机制&#xff0c;这可以通过…

爬取boss直聘上海市人工智能招聘信息+LDA主题建模

爬取boss直聘上海市人工智能招聘信息 import time import tqdm import random import requests import json import pandas as pd import os from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriv…

项目快过:知识蒸馏 | 目标检测 |FGD | Focal and Global Knowledge Distillation for Detectors

公开时间&#xff1a;2022年3月9号 项目地址&#xff1a;https://github.com/yzd-v/FGD 论文地址&#xff1a;https://arxiv.org/pdf/2111.11837 知识蒸馏已成功地应用于图像分类。然而&#xff0c;目标检测要复杂得多&#xff0c;大多数知识蒸馏方法都失败了。本文指出&#…

【Linux】匿名管道通信场景——进程池

&#x1f525; 个人主页&#xff1a;大耳朵土土垚 &#x1f525; 所属专栏&#xff1a;Linux系统编程 这里将会不定期更新有关Linux的内容&#xff0c;欢迎大家点赞&#xff0c;收藏&#xff0c;评论&#x1f973;&#x1f973;&#x1f389;&#x1f389;&#x1f389; 文章目…

Sybase数据恢复—Sybase数据库无法启动,Sybase Central连接报错的处理案例

Sybase数据库数据恢复环境&#xff1a; Sybase数据库版本&#xff1a;SQL Anywhere 8.0。 Sybase数据库故障&分析&#xff1a; Sybase数据库无法启动。 错误提示&#xff1a; 使用Sybase Central连接报错。 数据库数据恢复工程师经过检测&#xff0c;发现Sybase数据库出现…

分布式FastDFS存储的同步方式

目录 一&#xff1a;FatsDFS的结构图 二&#xff1a;FatsDFS文件同步 前言&#xff1a; 1&#xff1a;同步日志所在目录 2&#xff1a;binlog格式 3&#xff1a;同步规则 4&#xff1a;binlog同步过程 1 &#xff1a;获取组内的其他Storage信息 tracker_report_thread_e…

【绘图】数据可视化(python)

对于数据绝对值差异较大&#xff08;数据离散&#xff09; 1. 对数坐标直方图&#xff08;Histogram with Log Scale&#xff09; import pandas as pd import matplotlib.pyplot as plt import numpy as np# 示例数据 data {count: [10, 20, 55, 90, 15, 5, 45, 80, 1000, …

使用Dify与BGE-M3搭建RAG(检索增强生成)应用-改进一,使用工作流代替Agnet

文章目录 前言Agent vs 工作流编写工作流 前言 在上一篇中&#xff0c;我们实现了一个基本的基于Dify的RAG的示范。 使用Dify与BGE-M3搭建RAG&#xff08;检索增强生成&#xff09;应用 这个效果确实很差。 我们一起来看看&#xff0c;该怎么改进。 今天我们就尝试一下&…

【Linux课程学习】:文件第二弹---理解一切皆文件,缓存区

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;Linux课程学习 &#x1f337;追光的人&#xff0c;终会万丈光芒 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 Linux学习笔记&#xff1a; https://blog.csdn.net/d…

【iOS】《Effective Objective-C 2.0》阅读笔记(一)

文章目录 前言了解OC语言的起源在类的头文件中尽量少引入其他头文件多用字面量语法&#xff0c;少用与之等价的方法字面量数值字面量数组字面量字典 多用类型常量&#xff0c;少用#define预处理指令用枚举法表示状态、选项、状态码 总结 前言 最近开始阅读一些iOS开发的相关书籍…

猫狗分类调试过程

一&#xff0c;下载名称为archive数据集 下载方式&#xff1a;机房共享文件夹 二、打开CatDogProject项目 配置环境&#xff1a;选择你所建的环境 三、调试运行 1&#xff0c;报错一&#xff1a;Traceback (most recent call last): File "G:/AI_Project/CatDogPro…

探索Python WebSocket新境界:picows库揭秘

文章目录 探索Python WebSocket新境界&#xff1a;picows库揭秘第一部分&#xff1a;背景介绍第二部分&#xff1a;picows库概述第三部分&#xff1a;安装picows库第四部分&#xff1a;简单库函数使用方法第五部分&#xff1a;场景应用第六部分&#xff1a;常见Bug及解决方案第…