【Linux系统】进程替换 && 自主实现shell(简易版)

1.先看代码 && 现象

我们用exec*函数执行新的程序,

exec*系列的函数,执行完毕后,后续的代码不见了,因为被替换了。

execl的返回值可以不关心了,只要替换成功,就不会向后继续运行,只要继续运行了,一定是替换失败了!

2.解释原理

进程 = 内核数据结构 + 代码数据

进程的程序替换,有没有创建新的进程?? 没有的

站在被替换进程的角度:本质就是这个程序被加载到内存了!

怎么加载?exec*类似于Linux上的加载函数

3.将代码改成多进程版

fork创建子进程,让子进程自己去替换,wait等待

创建子进程,让子进程完成任务:

  1. 让子进程执行父进程代码的一部分
  2. 让子进程执行一个全新的程序

4.使用所有的替换方法,并且认识函数参数的含义

下面我们查的是这些替换方法,用man 3 execl

3手册是c语言的标准库GNU标准,所以我们实际查的是几个内含系统调用的c语言函数。

这是6个exec*系列函数。

第一个execl函数:

path:我们要执行的程序,需要带路径(怎么找到程序你得告诉我)

exec后面的l -- list:就是要存放的选项列表,在命令行中怎么执行,你就怎么传参!ls -a -l

execl("/usr/bin/ls", "ls", "-a", "-l", NULL);

路径代表你想执行谁,选项代表你想怎么执行!!!

带l的是我们要传入一个列表选项,那么带v的呢?

v:有动态数组的意思。

明显是要我们传入一个数组,这个数组就包含我们想要的选项,

带p的exec函数:

用户可以不传要执行的文件的路径(但是文件名要传),直接告诉exec*,我要执行谁就行

p:查找这个程序,系统会自动在环境变量PATH中进行查找。

e:environment环境变量。

envp[]:整体替换所有的环境变量!

  1. 用全新的给子进程。
  2. 用老的环境变量给子进程,environ。
  3. 老的环境变量稍微修改,给子进程。

上面的程序替换 ,我们替换的都是系统命令,可不可以替换我们自己写的程序呢?

支持不同的应用场景!!!

当然可以。 而且可以替换用任何语言写的无论是c++,java,python等语言都可进行替换,替换之后原程序pid不会改变,创建的子进程也不会改变,因为进程的替换,只是代码和数据的替换,不影响原程序的pcb。

系统调用接口execve。

上面的函数最终都将会走到系统调用接口。

5.写一个自己的Shell(简易版)

cpp 复制代码
#include<stdio.h>    
#include<stdlib.h>    
#include<string.h>    
#include<ctype.h>    
#include<unistd.h>    
#include<errno.h>    
#include<sys/types.h>    
#include<sys/wait.h>    
    
#define SIZE 512    
#define ZERO '\0'    
#define SEP " "    
#define NUM 32    
#define SkipPath(p) do{p += (strlen(p)-1);while(*p != '/')--p;}while(0)    
    
char cwd[SIZE * 2];    
char* gArgv[NUM];    
int lastcode = 0;    
    
void Die()    
{    
    exit(1);    
}    
const char* GetHome()    
{    
    const char* home = getenv("HOME");                                                                                                                                                   
    if(home == NULL) return "None";    
    return home;    
}    
const char* GetUserName()
{    
    const char* username = getenv("USER");
    if(username == NULL) return "None";
    return username;
}
                                                                                                                                                                                         
const char* GetHostName()
{
    const char* hostname = getenv("HOSTNAME");
    if(hostname == NULL) return "None";
    return hostname;
}

const char* GetCwd()
{
    const char* cwd = getenv("PWD");
    if(cwd == NULL) return "None";
    return cwd;
}
//commandline:output
void MakeCommandLineAndPrint()
{
    char line[SIZE];
    const char* username = GetUserName();
    const char* hostname = GetHostName();
    const char* cwd = GetCwd();

const char* GetCwd()
{
    const char* cwd = getenv("PWD");
    if(cwd == NULL) return "None";
    return cwd;                                                                                                                                                                          
}
//commandline:output
void MakeCommandLineAndPrint()
{
    char line[SIZE];
    const char* username = GetUserName();
    const char* hostname = GetHostName();
    const char* cwd = GetCwd();
    
    SkipPath(cwd);
    snprintf(line, sizeof(line), "[%s@%s %s]>", username, hostname, strlen(cwd) == 1 ? "/" : cwd + 1);
    printf("%s", line);
    fflush(stdout);
}
int GetUserCommand(char command[], size_t n)
{
    char* s = fgets(command, n, stdin);
    if(s == NULL) return -1;
    command[strlen(command) - 1] = ZERO;
    return strlen(command);
}
void SplitCommand(char command[], size_t n)
{
    (void)n;
    //"ls -a -l" ->  "ls", "-a", "-l"
    gArgv[0] = strtok(command, SEP);
    int index = 1;                                                                                                                                                                       
    while((gArgv[index] = strtok(NULL, SEP)))index++;// done, 故意写成=,表示先赋值,在判断,分割之后,strtok会返回NULL,刚好让gArgv最后一个元素是NULL,并且while判断结束
}

void ExecuteCommand()
{
    pid_t id = fork();
    if(id < 0) Die();
    else if(id == 0)
    {
        //child
        execvp(gArgv[0], gArgv);
        exit(errno);
    }
    else 
    {
        //father
        int status = 0;
        pid_t rid = waitpid(id, &status, 0);
        if(rid > 0)
        {
            lastcode = WEXITSTATUS(status);
            if(lastcode != 0) printf("%s:%s:%d\n", gArgv[0], strerror(lastcode), lastcode);
        }
    }
}
void Cd()
{
    const char* path = gArgv[1];
    if(path == NULL) path = GetHome();

    //path一定存在
    chdir(path);
                                                                                                                                                                                         
    //刷新环境变量
    char temp[SIZE * 2];
    getcwd(temp, sizeof(temp));
    snprintf(cwd, sizeof(cwd), "PWD=%s", temp);
    putenv(cwd);//ok
}
int CheckBuildin()
{
    int yes = 0;
    const char* enter_cmd = gArgv[0];
    if(strcmp(enter_cmd, "cd") == 0)
    {
        yes = 1;
        Cd();
    }
    else if(strcmp(enter_cmd, "echo") == 0 && strcmp(gArgv[1], "$?") == 0)
    {
        yes = 1;
        printf("%d\n", lastcode);
        lastcode = 0;
    }
    return yes;
}
int main()
{
    int quit = 0;
    while(!quit)                                                                                                                                                                         
    {
      //1.我们需要输出一个命令行
      MakeCommandLineAndPrint();
      //2.获取用户命令行字符串
      char usercommand[SIZE];
      int n = GetUserCommand(usercommand, sizeof(usercommand));
      if(n <= 0) return 1;

      //3.命令行字符串分割
      SplitCommand(usercommand, sizeof(usercommand));
      //4.检测命令是否是内键命令
      n = CheckBuildin();
      if(n) continue;
      //5,执行命令
      ExecuteCommand();
    }
    return 0;
}
相关推荐
Cachel wood13 分钟前
python round四舍五入和decimal库精确四舍五入
java·linux·前端·数据库·vue.js·python·前端框架
Youkiup21 分钟前
【linux 常用命令】
linux·运维·服务器
qq_2975046124 分钟前
【解决】Linux更新系统内核后Nvidia-smi has failed...
linux·运维·服务器
_oP_i30 分钟前
.NET Core 项目配置到 Jenkins
运维·jenkins·.netcore
weixin_4373982138 分钟前
Linux扩展——shell编程
linux·运维·服务器·bash
小燚~40 分钟前
ubuntu开机进入initramfs状态
linux·运维·ubuntu
小林熬夜学编程1 小时前
【Linux网络编程】第十四弹---构建功能丰富的HTTP服务器:从状态码处理到服务函数扩展
linux·运维·服务器·c语言·网络·c++·http
炫彩@之星1 小时前
Windows和Linux安全配置和加固
linux·windows·安全·系统安全配置和加固
上海运维Q先生1 小时前
面试题整理15----K8s常见的网络插件有哪些
运维·网络·kubernetes
hhhhhhh_hhhhhh_1 小时前
ubuntu18.04连接不上网络问题
linux·运维·ubuntu