操作系統之shell實現(下)

🌟?各位看官好,我是maomi_9526

🌍?種一棵樹最好是十年前,其次是現在!

🚀?今天來學習C語言的相關知識。

👍?如果覺得這篇文章有幫助,歡迎您一鍵三連,分享給更多人哦

目錄

1. 進程程序替換

2.exec函數

2.1 execl?

2.2 execlp?

2.3 execle?

2.4 execv?

2.5 execvp?

2.6 execvpe?

?2.7execve

2.8命名理解

3.進程替換

3.1進程替換原理

4. 自主Shell命令行解釋器

4.1獲取當前環境信息

4.2輸出命令行提示符

4.3獲取命令行輸入

4.4執行命令行

4.4.1執行內建命令?

4.4.2執行外部命令

4.5更新環境變量

3. Shell 實現完整代碼


1. 進程程序替換

  • fork() 系統調用創建一個子進程,父子進程開始執行相同的程序代碼。若子進程要執行一個不同的程序,可以使用 exec 系列函數來實現程序的替換。

  • 這些 exec 函數會加載一個全新的程序(包括代碼和數據)到子進程的地址空間中,并從新程序的入口點開始執行,原有的程序代碼被替換掉。exec 函數系列中最常用的是 execve,其他的 execl, execlp, execv, execvp, execle 等只是 execve 的不同封裝。

2.exec函數

頭文件:#include<unistd.h>

返回值:當失敗時返回-1

2.1 execl?

int execl(const char *path, const char *arg, ...);

execl("/usr/bin/ls","ls","-l",NULL);
2.2 execlp?

int execlp(const char *file, const char *arg, ...);

execlp("ls","ls","-l",NULL);
2.3 execle?

int execle(const char *path, const char *arg, ..., char * const envp[]);

extern char**environ;//聲明全局環境變量
execle("/usr/bin/ls","1s","-l","-a",NULL,environ};
2.4 execv?

int execv(const char *path, char *const argv[]);

char*argv[]={"1s","-l","-a",NULL};execv("/usr/bin/ls",argv);
2.5 execvp?

int execvp(const char *file, char *const argv[]);

char*argv[]={"1s","-l","-a",NULL};execvp("ls",argv);
2.6 execvpe?

int execvpe(const char *file, char *const argv[],char *const envp[]);


char*argv[]={"ls","-a","-l",NULL};
execvpe("ls",argv,environ);
    ?2.7execve

    系統調用函數execve

    上面的exec系列函數本質上都不是系統級別的調用,都是對execve的語言級別的封裝

    int execve(const char *filename, char *const argv[],?char *const envp[]);

    ?

    2.8命名理解
    • l(list) : 表示參數采用列表
    • v(vector) : 參數用數組
    • p(path) : 有p自動搜索環境變量PATH
    • e(env) : 表示自己維護環境變量
    函數級別函數名列表傳參是否帶路徑是否使用當前環境變量
    語言級別execl列表
    execlp列表
    execle列表
    execv數組
    execvp數組
    execvpe數組
    系統級別execve數組

    3.進程替換

    3.1進程替換原理

    當進程執行了代碼替換操作后,原先加載的代碼會被新的代碼所替換。

    此時,原有的代碼不再存在于進程的地址空間中,執行流轉向新的代碼。具體來說,在進程替換時,原代碼的內存空間被新的代碼段覆蓋,新的代碼開始運行。此過程的本質是將進程的代碼區域替換為新的內容,從而導致原有代碼失效并不可再訪問。

    所以原來代碼我的進程執行完畢并不會出現。?

    4. 自主Shell命令行解釋器

    • 通過實現一個自定義的 shell,可以處理命令行輸入,并根據輸入執行對應的命令。Shell 需要有以下功能:

    4.1獲取當前環境信息

    getenv() 是一個 C 標準庫函數,用于從環境變量中獲取指定名稱的值。環境變量是系統級的變量,它們存儲了操作系統和程序運行時需要的配置信息,比如系統路徑、用戶設置等。getenv() 函數通過讀取這些環境變量,允許程序動態地獲取環境設置。

    頭文件:#include<stdlib.h>

    函數:char *getenv(const char *name);

    返回值:

    • 成功:如果找到了指定名稱的環境變量,getenv() 會返回該變量的值(一個指向字符數組的指針,代表該環境變量的值)。

    • 失敗:如果未找到指定的環境變量,getenv() 返回 NULL

    代碼實現:

    //獲取當前環境信息
    const char* GETPWD()
    {char *pwd=getenv("PWD");return pwd==NULL?"None":pwd;
    }//獲取用戶信息
    const char*GETUSER()
    {char*user=getenv("USER");return user==NULL?"None":user;
    }//獲取系統信息
    const char*GETHOSTNAME()
    {char*hostname=getenv("HOSTNAME");return hostname==NULL?"None":hostname;
    }
    
    4.2輸出命令行提示符

    snprintf 是 C 語言標準庫中的一個函數,屬于 stdio.h 頭文件。它的作用是將格式化的數據輸出到一個字符數組中,并且保證不會發生緩沖區溢出。snprintf 函數是對 sprintf 的一種改進,主要是增加了一個最大字符數的限制,避免了 sprintf 在沒有足夠空間時造成內存溢出的風險。?

    頭文件:#include<stdio.h>

    int snprintf(char *str, size_t size, const char *format, ...);

    返回值:

    • 成功:返回寫入字節數(當被寫入內容超過寫入大小,發生截斷)

    • 失敗:返回負數?

    #define COMMAND_SIZE 1024
    #define FORMAT "[%s@%s %s]#"
    void MakeCMDPrompt(char cmdprompt[],size_t size)//制作命令行提示符
    {snprintf(cmdprompt,size,FORMAT,GETUSER(),GETHOSTNAME(),GETPWD());
    }
    void PrintCMDPrompt()//打印命令行提示符
    {char prompt[COMMAND_SIZE];MakeCMDPrompt(prompt,sizeof(prompt));printf("%s",prompt);
    }
    
    4.3獲取命令行輸入

    fgets 是 C 語言標準庫中的一個函數,屬于 stdio.h 頭文件。它的作用是從指定的文件流中讀取一行字符串,并將讀取的內容存儲到一個字符數組中。與 gets 不同,fgets 可以避免緩沖區溢出的問題,因為它會限制讀取的字符數。?

    頭文件:#include<stdio.h>

    char *fgets(char *s, int size, FILE *stream);

    返回值:

    • 成功?:返回寫入的s的位置
    • 失敗:返回NULL

    ?代碼實現:

    //接受命令行
    bool MakeCMDLine(char*out,size_t size)
    {char*line=fgets(out,size,stdin);if(line==NULL) return false;//返回值為空,寫入失敗out[strlen(out)-1]=0;//去除輸入的換行符if(strlen(out)==0) return false;return true;
    }

    4.2解析命令行

    將用戶輸入的命令解析成可執行的命令和參數。

    strtok 是 C 語言標準庫中的一個函數,屬于 string.h 頭文件。它用于將一個字符串分割成一系列子字符串(tokens),根據指定的分隔符。該函數通常用于處理由空格、逗號、換行符等字符分隔的文本數據。

    char *strtok(char *str, const char *delim);

    • str:待分割的字符串。如果是第一次調用 strtok,該參數應為需要分割的字符串;如果是后續調用,應該傳遞 NULL,以繼續分割上一次傳入的字符串。

    • delim:分隔符字符串,定義了用于分割字符串的字符集合。可以是單個字符,也可以是多個字符,strtok 會將字符串中的任何一個分隔符都視為分隔點。

    //分割字符串
    bool CMDLinePrase(char *line)
    {
    #define ADC " "g_argc=0;//每次初始化為0,確保每個命令都是從首位開始g_argv[g_argc++]=strtok(line,ADC);while(g_argv[g_argc++]=strtok(nullptr,ADC));g_argc--;return true;
    }
    
    4.4執行命令行
    4.4.1執行內建命令?

    通過父進程本身來進行執行:(cd命令)

    頭文件:#include<unistd.h>

    ?int chdir(const char *path);

    bool CheckBuiltIn()
    {std::string cmd=g_argv[0];if(cmd=="cd"){if(g_argc==1){chdir(GETHOME());return true;}else{std::string pwd=g_argv[1];chdir(pwd.c_str());}return true;}return false;
    }
    
    4.4.2執行外部命令

    ?通過子進程來進行執行:

    //子程序進行進程替換執行命令
    int Execute()
    {int id=fork();if(id==0){//chileexecvp(g_argv[0],g_argv);exit(1);}//fatherint idd=waitpid(id,NULL,0);//阻塞等待(void)idd;//使用避免報錯return 0;
    }
    
    4.5更新環境變量

    getcwdunistd.h 頭文件中的一個函數,用于獲取當前工作目錄。?

    ?#include<unistd.h>

    char *getcwd(char *buf, size_t size);

    • buf:一個字符數組的指針,用來存儲獲取的當前工作目錄的路徑。你需要在調用 getcwd 之前分配足夠的內存空間來存儲路徑。

    • sizebuf 指針指向的字符數組的大小。它指定了 buf 能夠存儲的最大字符數。

    char g_env[1024];
    char g_cwd[1024];void ChangEnv()
    {const char*cwd=getcwd(g_cwd,sizeof(g_cwd));if(cwd!=nullptr){snprintf(g_env,sizeof(g_env),"PWD=%s",g_cwd);putenv(g_env);}
    }
    

    3. Shell 實現完整代碼

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cstdlib>
    #include<unistd.h>
    #include<sys/types.h>
    #include<sys/wait.h>
    #define COMMAND_SIZE 1024
    #define FORMAT "[%s@%s %s]#"
    #define MAXARGC 128
    char g_env[1024];
    char g_cwd[1024];
    char* g_argv[MAXARGC];
    int g_argc=0;
    const char* GETPWD()
    {char *pwd=getenv("PWD");return pwd==NULL?"None":pwd;
    }
    const char*GETUSER()
    {char*user=getenv("USER");return user==NULL?"None":user;
    }
    const char*GETHOSTNAME()
    {char*hostname=getenv("HOSTNAME");return hostname==NULL?"None":hostname;
    }
    const char*GETHOME()
    {char*home=getenv("HOME");return home==NULL?"None":home;
    }
    void ChangEnv()
    {const char*cwd=getcwd(g_cwd,sizeof(g_cwd));if(cwd!=nullptr){snprintf(g_env,sizeof(g_env),"PWD=%s",g_cwd);putenv(g_env);}
    }
    bool CheckBuiltIn()
    {std::string cmd=g_argv[0];if(cmd=="cd"){if(g_argc==1){chdir(GETHOME());return true;}else{std::string pwd=g_argv[1];chdir(pwd.c_str());}ChangEnv();return true;}return false;
    }
    std::string DirName(const char* pwd)
    {
    #define SLASH "/"std::string dir=pwd;auto pose=dir.rfind(SLASH);if(pose==std::string::npos) return "BUG?";return dir.substr(pose+1);
    }
    void MakeCMDPrompt(char cmdprompt[],size_t size)
    {//snprintf(cmdprompt,size,FORMAT,GETUSER(),GETHOSTNAME(),GETPWD());snprintf(cmdprompt,size,FORMAT,GETUSER(),GETHOSTNAME(),DirName(GETPWD()).c_str());
    }
    void PrintCMDPrompt()
    {char prompt[COMMAND_SIZE];MakeCMDPrompt(prompt,sizeof(prompt));printf("%s",prompt);
    }
    bool MakeCMDLine(char*out,size_t size)
    {char*line=fgets(out,size,stdin);if(line==NULL) return false;out[strlen(out)-1]=0;if(strlen(out)==0) return false;return true;
    }
    bool CMDLinePrase(char *line)
    {
    #define ADC " "g_argc=0;g_argv[g_argc++]=strtok(line,ADC);while(g_argv[g_argc++]=strtok(nullptr,ADC));g_argc--;return g_argc==0?false:true;
    }
    void PrintCMDLinePrase()
    {for(int i=0;g_argv[i];i++){printf("argv[%d]->%s\n",i,g_argv[i]);}printf("argc :%d\n",g_argc);
    }
    void Print()
    {char cmdline[COMMAND_SIZE];if( MakeCMDLine(cmdline,sizeof(cmdline))){printf("%s",cmdline);}
    }
    int Execute()
    {int id=fork();if(id==0){//chileexecvp(g_argv[0],g_argv);exit(1);}//fatherint idd=waitpid(id,NULL,0);//阻塞等待(void)idd;//使用避免報錯return 0;
    }
    int main()
    {while(true){PrintCMDPrompt();char cmdline[COMMAND_SIZE];if(! MakeCMDLine(cmdline,sizeof(cmdline))){continue;}if(!CMDLinePrase(cmdline)){continue;}if(CheckBuiltIn()){continue;}Execute();}return 0;
    }
    

    本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
    如若轉載,請注明出處:http://www.pswp.cn/web/76833.shtml
    繁體地址,請注明出處:http://hk.pswp.cn/web/76833.shtml
    英文地址,請注明出處:http://en.pswp.cn/web/76833.shtml

    如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

    相關文章

    Spark,流量統計案例

    提前創好一個文件夾分為四個類 FlowBean中的代碼內容為&#xff1a;package org.example.flow; import org.apache.hadoop.io.Writable; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; //hadoop 序列化 //三個屬性&#xff1a;手機…

    下載油管視頻 - yt-dlp

    文章目錄 1. yt-dlp與you-get介紹1.1 主要功能對比1.2 使用場景1.3 安裝 2. 基本命令介紹2.1 默認下載視頻2.2 指定畫質和格式規則2.3 下載播放列表2.4 備注 3. 參考資料 之前只使用you-get下載b站視頻&#xff0c;當時了解you-get也可下載油管視頻&#xff0c;但之前無此需求&…

    基于javaweb的SSM+Maven教材管理系統設計與實現(源碼+文檔+部署講解)

    技術范圍&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬蟲、數據可視化、小程序、安卓app、大數據、物聯網、機器學習等設計與開發。 主要內容&#xff1a;免費功能設計、開題報告、任務書、中期檢查PPT、系統功能實現、代碼編寫、論文編寫和輔導、論文…

    VS2022+QT環境配置及基本操作

    參考文章 2025最新&#xff01;Visual Studio 2022 QT6.7 環境配置全攻略&#xff1a;一鍵搞定安裝與亂碼問題&#xff0c;開發效率翻倍&#xff01;&#xff08;全網最詳細教程&#xff0c;手把手教你搭建完美開發環境&#xff01;&#xff09;_vs2022 qt-CSDN博客 下載QT …

    使用percona-toolkit同步mysql表數據

    背景 做了主備mysql的配置以后&#xff0c;可能因為切換過程造成不一致的情況&#xff0c;這個時候可以處理的方式是全量導入再導出&#xff0c;這個有個問題就是操作的數據太多了 我們只需要數據補全同步即可 mysql的同步是基于binlog的&#xff0c;如果沒有記錄的部分的數據…

    MDG 實現后端主數據變更后快照自動刷新的相關設置

    文章目錄 前言實現過程BGRFC期初配置&#xff08;可選&#xff09;設置 MDG快照 BGRFC維護BP出站功能模塊 監控 前言 眾所周知&#xff0c;在MDG變更請求創建的同時&#xff0c;所有reuse模型實體對應的快照snapshot數據都會記錄下來。隨后在CR中&#xff0c;用戶可以修改這些…

    重裝系統 之 Dell戴爾服務器 PowerEdge R750xs + window server2012r2 || 2016

    因要求需要給新服務器裝個 win server2012或者2016系統 XXX使用U盤制作PE系統U盤安裝系統不行&#xff0c;適合普通win8&#xff0c;win10&#xff0c;win11U盤制作PE系統U盤安裝win10系統教程U盤制作PE系統U盤安裝win10系統教程https://mp.weixin.qq.com/s/t0W8aNJaHPAU8T78nh…

    基于Spring Security 6的OAuth2 系列之二十六 - 終章

    之所以想寫這一系列&#xff0c;是因為之前工作過程中使用Spring Security OAuth2搭建了網關和授權服務器&#xff0c;但當時基于spring-boot 2.3.x&#xff0c;其默認的Spring Security是5.3.x。之后新項目升級到了spring-boot 3.3.0&#xff0c;結果一看Spring Security也升級…

    一鍵配置多用戶VNC遠程桌面:自動化腳本詳解

    在當今遠程工作盛行的時代,高效且安全地管理多用戶遠程桌面訪問變得至關重要。本文將介紹一個強大的自動化腳本,該腳本能夠快速創建用戶并配置VNC遠程桌面環境,大大簡化了系統管理員的工作。 一、背景介紹 在Linux系統中,手動配置VNC服務器通常需要執行多個步驟,包括創建…

    IOT項目——雙軸追光系統

    雙軸太陽能追光系統 - ESP32實現 系統概述 這個系統使用&#xff1a; ESP32開發板2個舵機&#xff08;水平方向和垂直方向&#xff09;4個光敏電阻&#xff08;用于檢測光照方向&#xff09;適當的電阻&#xff08;用于光敏電阻分壓&#xff09; 接線示意圖 --------------…

    Maven集成模塊打包使用

    文章目錄 1.問題思考&#xff08;如何對集成模塊進行打包&#xff09;2.問題解決 &#xff08;如何對集成模塊進行打包&#xff09;3.使用者使用該jar包(jar包安裝本地倉庫和使用) 1.問題思考&#xff08;如何對集成模塊進行打包&#xff09; 思考&#xff1a;假設有這么一個場…

    OpenVINO教程(二):圖片目標檢測推理應用

    YOLO模型物體檢測 下面是一個簡單的python程序,他的功能是使用yolo11n模型對coco_bike.jpg照片進行檢測,并顯示檢測結果 代碼步驟如下: coco_bike.jpg照片加載yolo模型使用模型進行detect推理顯示推理結果 下面是完整的代碼 from pathlib import Pathimport urllib.request…

    聚類算法(K-means、DBSCAN)

    聚類算法 K-means 算法 算法原理 K-means 是一種基于類內距離最小化的劃分式聚類算法&#xff0c;其核心思想是通過迭代優化將數據劃分為 K 個簇。目標函數為最小化平方誤差&#xff08;SSE&#xff09;&#xff1a; S S E ∑ i 1 K ∑ x ∈ C i ∣ ∣ x ? μ i ∣ ∣ 2…

    Oracle在ERP市場擊敗SAP

    2024年&#xff0c;甲骨文&#xff08;Oracle&#xff09;以87億美元的ERP收入和6.63%的市場份額&#xff0c;首次超越SAP&#xff0c;成為全球最大的ERP應用軟件供應商&#xff0c;結束了SAP自上世紀80年代以來在該領域的長期霸主地位。據APPS RUN THE WORLD的市場調研&#x…

    嵌入式面試高頻筆試題目解析

    一、基礎概念與 C 語言核心題 1. 指針與內存操作 典型題目: char str[] = "hello"; char *ptr = "world"; str[0] = H; // 合法嗎? ptr[0] = W; // 合法嗎?為什么?解析: str 是棧上數組,可修改內容,str[0]=H 合法。ptr 指向常量字符串區,修改會…

    【Python】Selenium切換網頁的標簽頁的寫法(全!!!)

    在使用selenium做網站爬取測試的時候&#xff0c;我們經常會遇到一些需要點擊的元素&#xff0c;才能點擊到我們想要進入的頁面&#xff0c; 于是我們就要模擬 不斷地 點點點擊 鼠標的樣子。 這個時候網頁上就會有很多的標簽頁&#xff0c;你的瀏覽器網頁標簽欄 be like: 那…

    MySQL GTID模式主從同步配置全指南:從配置到故障轉移

    前言 MySQL主從復制是企業級數據庫架構的基礎&#xff0c;而GTID(Global Transaction Identifier)模式則是MySQL 5.6版本后推出的革命性復制技術。本文將詳細介紹如何配置基于GTID的主從同步&#xff0c;并包含實用的故障轉移操作指南。 一、GTID模式核心優勢 相比傳統基于…

    MAC系統下完全卸載Android Studio

    刪除以下文件 /Applications/Android Studio.app /Users/用戶名/Library/Application Support/Google/AndroidStudio2024.2 /Users/用戶名/Library/Google/AndroidStudio /Users/用戶名/Library/Preferences/com.google.android.studio.plist /Users/用戶名/Library/Cache…

    <C#>.NET WebAPI 的 FromBody ,FromForm ,FromServices等詳細解釋

    在 .NET 8 Web API 中&#xff0c;[FromBody]、[FromForm]、[FromHeader]、[FromKeyedServices]、[FromQuery]、[FromRoute] 和 [FromServices] 這些都是用于綁定控制器動作方法參數的特性&#xff0c;下面為你詳細解釋這些特性。 1. [FromBody] 作用&#xff1a;從 HTTP 請求…

    # 透視 Linux 內核:Socket 機制的底層架構與運行邏輯深度解析

    在由 Linux 操作系統構建的龐大網絡生態中&#xff0c;Socket 作為網絡通信的核心樞紐&#xff0c;承載著不同主機間應用進程的數據交互重任。無論是日常的網頁瀏覽、在線游戲&#xff0c;還是復雜的分布式系統通信&#xff0c;Socket 都在幕后扮演著關鍵角色。盡管多數開發者對…