[进程控制]模拟实现命令行解释器shell

1.相关知识

1.1字符串切割函数

在这里插入图片描述

1.2chdir(): 进入目标路径

在这里插入图片描述

1.3putenv()添加一个环境变量

在这里插入图片描述

1.4父进程最多能创建几个子进程?

#include <stdio.h>
#include <unistd.h>

int main()
{
    int max_cnt = 0;
    while(1)
    {
        pid_t id = fork();
        if(id < 0)
        {
            printf("fork error: %d\n", max_cnt);
            break;
        }
        else if(id == 0)
        {
            while(1)
            {
                sleep(1);
            }
        }
        max_cnt++;
    }
}
  1. 最后的max_cnt就是创建的子进程的个数
  2. ps axj | grep "a.out" | grep -v grep | wc -l: 双引号目的是不计算head那一行(ps axj默认显示head 这里为了更精确的显示进程数)
  3. 此时的a.out成为了孤儿进程 killall a.out手动杀死

对于进程的认识

之前我们讲过进程是程序运行的实体 如今我们了解到 程序运行前可能就已经创建好了进程 即程序还没运行 也能存在进程

程序替换在Windows下的应用

vs2019运行其上的程序 vs是一个进程 它创建一个子进程进行程序替换来执行用户编写的程序

1.5shell的环境变量来源?

  1. 环境变量是写在配置文件中的
  2. shell启动时会从配置文件中读取获取起始的环境变量

2.模拟实现shell

  1. 模拟实现的shell下删除: ctrl+backspace
  2. 模拟实现下table/上下左右箭头无法使用[demo]

shell执行的命令通常由两种

  1. 子进程执行的第二/三方提供的磁盘中有具体二进制文件的可执行程序(pwd/ls/touch)[用户是第一方 OS是第二方]
  2. 父进程执行的内置命令: shell内部自己实现的方法 如cd/export(这些方法就是让父进程执行来对父进程进行某些操作)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>

#define NUM 1024
#define SIZE 32
#define SEP " "

//获取用户输入的命令行字符串
char cmd_line[NUM];
//将获取的字符串一个个分割开来
char* g_argv[SIZE];

// 环境变量的buffer 目的是新增的环境变量不被下一次memset清空
char g_tmpenv[64];

// shell运行原理: 父进程解析命令 等待子进程执行命令
int main()
{
   // extern char** environ;//debug

    //命令行解释器是一个常驻内存的进程(直到用户退出)
    while (1)
    {
        //显示提示信息
        printf("[root@localhost myshell]# ");
        fflush(stdout);
        memset(cmd_line, '\0', sizeof cmd_line);
        ///获取用户输入的命令行字符串 "ls -a -l -i"
        if (fgets(cmd_line, sizeof cmd_line, stdin) == NULL)
        {
            continue;
        }
        //"ls -a -l -i\n\0"==>//"ls -a -l -i\0\0" 删除换行符以更贴切xshell
        cmd_line[strlen(cmd_line) - 1] = '\0';
        //debug: printf("echo: %s\n", cmd_line);//是否成功获得命令行字符串
        
        //命令行字符串解析:"ls -a -l " -> "ls" "-a" "-l"
        g_argv[0] = strtok(cmd_line, SEP); //第一次调用传入原始字符串

        //一些特殊扩展的选项
        int index = 1;
        if (strcmp(g_argv[0], "ls") == 0)
        {
            g_argv[index++] = "--color=auto";
        }
        if (strcmp(g_argv[0], "ll") == 0)
        {
            g_argv[0] = "ls";
            g_argv[index++] = "-l";
            g_argv[index++] = "--color=auto";
        }

        while (g_argv[index++] = strtok(NULL, SEP)); //后续解析原始字符串,传入NULL

        //debug: 查看是否传给解析命令行字符串
        //for(index = 0; g_argv[index]; index++)
        //    printf("g_argv[%d]: %s\n", index, g_argv[index]);
        
        //内置命令/内建命令: 父进程(shell)自己执行的命令 本质是shell中的一个函数调用
        if (strcmp(g_argv[0], "cd") == 0)
        {
            if (g_argv[1] != NULL) 
                chdir(g_argv[1]); 

            continue;
        }
        if (strcmp(g_argv[0], "export") == 0 && g_argv[1] != NULL)
        {
            strcpy(g_tmpenv, g_argv[1]);
            putenv(g_tmpenv);
            //debug1: 查看是否传给添加env
            //int ret = putenv(g_tmpenv);
            //if (ret == 0) 
            //    printf("%s export success\n", g_argv[1]);
            // 
            //debug2: 显示所有env来查看是否传给添加env
            //for(int i = 0; environ[i]; i++)
            //    printf("%d: %s\n", i, environ[i]);
            continue;
        }

        //子进程执行获取到的命令
        pid_t id = fork();
        if (id == 0) 
        {
            //debug: 子进程被替换前是否拿到env
            //printf("child's TMPENV: %s\n", getenv("TMPENV"));
            //printf("child's PATH: %s\n", getenv("PATH"));
           
            //ls -a -l
            //execvpe(g_argv[0], g_argv, environ);//ok: 显示传env 但没必要
            execvp(g_argv[0], g_argv);//ok test.c成功获取env
            exit(1);
        }
        //father
        int status = 0;
        pid_t ret = waitpid(id, &status, 0);
        if (ret > 0) 
        	printf("exit code: %d\n", WEXITSTATUS(status));
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿猿收手吧!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值