IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 从头实现Linux指令(一)实现自己的shell -> 正文阅读

[系统运维]从头实现Linux指令(一)实现自己的shell

本文从零实现Linux指令系列文章,其他系列文章:

从头实现Linux指令(零)序言
从头实现Linux指令(一)实现自己的shell

shell概述

熟悉linux的人应该都对shell有一个感性的认识,大概知道它是什么~我们还可以用一些更加精确的语言给它一个定义:命令行解释器( CLI) 或shell是代表其用户运行其他程序的程序。shell 反复打印提示例如$,等待用户输入,然后执行用户的操作。不用shell的linux是没有灵魂的
用户输入的命令行是由空格分隔的 ASCII 单词序列。命令行中的第一个单词要么是内置命令的名称,要么是可执行文件的名称。剩下的词是命令行参数。
linux发行版例如ubuntu自带的shell一般是bash,除了bash以外,还有一些很好用的shell工具,这里强推zsh。

本项目希望能够实现一个迷你的shell,功能也许不强大,但应该具有基本的shell功能,例如ls, cd, pwd, path, 并发执行以及重定向等功能。本文首先介绍各模块的实现,最后说明如何运行并调试本项目。

完全掌握整个项目代码的时间:30h

开始使用 Shell 项目

如果有厉害的大手子想直接审查一下笔者写的代码,可以直接访问github:https://github.com/SFUMECJF/linux-command

0 练手任务

斐波那契数列大家都知道是什么,不过我们这里使用c语言中的fork() / wait()以递归方式编写。每个子进程将其结果返回给父进程,父进程一直等到子进程完成。那么通过这个练习要掌握父子进程通信的方式。
如果不限制必须使用多进程 的方式的话,实现是很简单的:

int helper(int n) {
    if (n == 0)
       return 0;
    else if (n == 1)
        return 1;
    else {
        return helper(n - 1) + helper(n - 2);
    }
}

那么用进程怎么办呢?
实现思路如下:

  1. 每次递归调用函数的时候,都开一个新进程调用递归函数。
  2. 父进程给子进程可以直接传参,子进程给父进程的返回值这里利用exit()函数,也即如果子进程调用 exit(0)结束,则父进程可以通过wait(&return_value)等待子进程结束,然后再利用sum += WEXITSTATUS(return_value);将相应子进程的返回值收集到我们想要的结果上。

实现结果:

make fib #编译
unix> fib 3
2
unix> fib 10
55

1 shell 骨架

本节练习系统调用,strtok、strcmp和execv,完成的目标是解析输入的指令以及实现内部指令。

1.1 REPL

任何 shell 的核心都是 REPL,即read-evaluate-print loop。这是一个重复执行以下三个动作的循环:

  1. 从用户(或从用户指定的脚本)读取输入。
  2. 评估输入,弄清楚用户想要做什么并去做。
  3. 打印相关的任何输出。

本项目在行首循环打印utcsh>,然后使用getline读取用户的输入到c语言的字符串数组。读取数组之后,使用strtok对数组按照空格拆分,就可以获取每个单独的单词了~也就是命令以及参数。

1.2 内部命令

  1. exit:该指令退出shell程序。使用系统调用exit(0)完成

  2. cd:cd总是只接受一个参数。使用系统调用chdir()完成。

  3. path:path命令接受零个或多个参数,每个参数由空格分隔。典型的用法可能如下所示:utcsh> path /bin /usr/bin需要在代码内部维护一个二维字符串数组,每次更新的路径放到数组中。路径是为了在执行外部命令时能够找到相应的可执行程序。

1.3 外部命令

如果给出的命令不是三个内置命令之一,则应将其视为外部可执行程序。

对于这些外部命令,使用 fork-and-exec 方法执行程序。每一个外部指令,都利用系统调用execv()调用一个子进程来执行。这里execv如何能够找到外部可执行程序呢,要靠我们上面维护的路径二维数组。

对于父进程:父进程应该使用wait()或waitpid()等待子进程。

1.4 读入shell脚本文件

除了手动输入命令之外,还需要能够处理文件中的大量脚本。这里练习的文件操作,打开文件,读取一行,注意这里不是从标准输入读取,而是从文件中读取。

1.5 重定向

很多时候,shell 用户更喜欢将程序的输出发送到文件而不是屏幕。shell支持使用 > 以及 < 进行输入和输出的重定向。这里使用open()以及dup1和dup2完成重定向到文件的功能。

1.6 并发执行命令

utcsh> cmd1 & cmd2 & cmd3 args1
输入以上命令时, shell 应该并发执行cmd1,cmd2和cmd3(无论传递了什么参数) ,然后等待它们中的所有命令完成。
然后,一旦所有子进程都已启动,父进程必须使用wait()或waitpid() 确保所有进程都已完成,然后再继续。

1.7 测试与运行

运行:make
调试:make debug之后使用gdb或者vscode配置好环境用ide调试
测试代码功能是否正确,总共有32个测试。make check搞定所有测试,make testcase id=3单独测试第3条。

1.8 收获与想法

很久之前我做robomaster,学习了CMake以及c++调用Open CV库的基本操作。但当我去字节实习面试的时候,只记得面试官很诧异地问我:啊,你的代码里都没有系统调用吗?(那时我只会打开文件,而且并不知道文件操作也属于系统调用的一部分)~
希望大家Linux开发的知识都多多的~

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-03-12 18:00:11  更:2022-03-12 18:00:34 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/16 1:55:03-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码