【linux進程控制(三)】進程程序替換--如何自己實現一個bash解釋器?

💓博主CSDN主頁:杭電碼農-NEO💓
?
?專欄分類:Linux從入門到精通?
?
🚚代碼倉庫:NEO的學習日記🚚
?
🌹關注我🫵帶你學更多操作系統知識
? 🔝🔝


在這里插入圖片描述

進程程序替換

  • 1. 前言
  • 2. exec系列函數的認識
  • 3. execl系列函數
  • 4. execv系列函數
  • 5. 程序替換的使用場景
  • 6. 自我實現一個bash解釋器
  • 7. 內建命令的特殊性
  • 8. 總結以及拓展

1. 前言

本篇文章是進程控制的最后一篇文章
有時我們遇見這種場景:子進程被創建
出來后并不想執行父進程的代碼,而是
想去執行其他程序的代碼來完成任務,
于是在這種場景下,程序替換顯得很重要!

本章重點:

本篇文章著重講解進程程序替換
的exec系列函數的用法(一共六個),
并且自主實現一個bash解釋器.
最后拓展如何使用C調用其他語言的程序


2. exec系列函數的認識

在fork之后如果子進程想要執行一個
全新的程序,就需要用到此系列函數!

在這里插入圖片描述

這里一共有六個函數,但是它們都有
一定的規律,并不難記憶!它們都是失敗
返回-1首先先介紹一個最簡單的函數:
execl

它的參數分別代表:要執行的程序的路徑
以及名字,和如何執行此程序

先使用再解釋:

int main()    
{    printf("我要進行程序替換了...\n");    int n = execl("/usr/bin/ls","ls","-a","-l",NULL);                                                                                                                   if(n==-1)    {    perror("execl");    }    printf("程序替換完畢!\n");    return 0;    
}    

現象:

在這里插入圖片描述
可以發現,在打印完:我要進行程序替換
后,就去執行了ls程序了,并且執行完后
并沒有打印"程序替換完成"!

表面execl后,會將當前進程的代碼和數據
進行替換,包括還沒執行的代碼!


3. execl系列函數

首先,execl隸屬于exec系列,加上l,
l就是list,相當于要把執行的程序的路徑
給列舉出來,execl系列中還有
execlp和execle,現在來講解這兩個

在這里插入圖片描述

execlp函數解析:

int main()    
{    printf("我要進行程序替換了...\n");    int n = execlp("ls","-l",NULL);                                                                                                                                     if(n==-1)    {    perror("execl");    }    printf("程序替換完畢!\n");    return 0;    
} 

在這里插入圖片描述

可以發現,使用execlp函數即使不加上
路徑也可以找到程序,并且允許它,但是
這是為什么呢?帶上p,p也就是PATH
環境變量,所以系統會去環境變量PATH
中找路勁,若找到就直接執行它!

execle函數解析:

int main()    
{    const char* _env[]={"MY_ENV=666",NULL};    printf("我要進行程序替換了...\n");    int n = execle("/usr/bin/ls","ls","-l",NULL,_env);//自己定義一個環境變量MY_ENV=666傳遞給要去執行的程序                                                              if(n==-1)    {    perror("execl");    }    printf("程序替換完畢!\n");    return 0;    
}  

execle可以在執行其他程序前,
傳入自己定義的環境變量,方便后續
程序的執行!e也就是env的簡寫


4. execv系列函數

execv系列函數即為將l換成了v,
v就是vector,數組,也就是利用
數組來傳參

在這里插入圖片描述

execv函數解析:

int main()    {    char* const set[]={"ls","-a","-l",NULL};  printf("我要進行程序替換了...\n");    int n = execv("/usr/bin/ls",set);                                                                             if(n==-1)    {    perror("execl");    }    printf("程序替換完畢!\n");    return 0;    } 

將我們要執行程序的方法用數組存起來
如何再把數組傳過去!后面的execvp和
execvpe函數也就很好理解了,加上p無非
就是去環境變量PATH中找路徑,加上e也
就是給要去執行的程序傳入環境變量,僅此而已!

在這里插入圖片描述


5. 程序替換的使用場景

其實一般情況下,程序替換都不是將
自己替換掉,而是創建子進程去替換,
讓子進程去干活,而父進程當"監工"

在這種場景下,我們可以很自然的想到
bash解釋器的工作原理可能就是創建
子進程去執行任務,而bash父進程本身
就需要獲取指令,并傳達命令即可!

首先,bash解釋器一定是一個while
死循環,因為它會不斷給我們打印信息:
當然這個消息你可以自定義處理

在這里插入圖片描述

int main()
{while(1){//打印提示信息printf("[kwy@localhost myshell]# ");fflush(stdout);......}return 0;
}

我們先把bash的整體結構分析一下,
然后在一步一步的實現它:

  1. 首先我們需要定義兩個數組A和B
    A用來存放用戶輸入的所有字符串
    B用來存放以空格打散后的字符串

  2. 第二步,獲取用戶輸入的字符串后,
    將字符串以空格為分割打散

  3. 第三步,創建子進程使用exec系列
    函數去執行用戶輸入的指令
    而bash本身充當監工的角色等待子進程死亡


6. 自我實現一個bash解釋器

首先先創建兩個數組備用
然后再接收用戶的輸入

#define NUM 1000
#define SIZE 16
char cmd_line[NUM];//保存完整的命令行字符串
char* my_argv[SIZE];//保存打散后的字符串
if(fgets(cmd_line,sizeof cmd_line,stdin)==NULL)//用fgets將標準輸入輸入到數組中continue;
cmd_line[strlen(cmd_line)-1] = '\0';//將輸入的換行符給清除掉

接下來就是將字符串以空格為分割打散了
在C語言的學習時有strtok函數可以幫助
我們解決這個問題,它的功能如下:

在這里插入圖片描述

這里默認大家知道這個函數的用法了
所以我直接將分割字符串的代碼寫出來:

//命令行字符串解析:以空格為分割打散
my_argv[0]=strtok(cmd_line," ");//提出第一部分
int index=1;
while(my_argv[index++] = strtok(NULL," "));//第二次調用strtok時若還想解析第一次調用的字符串,則傳NULL

這段代碼寫完后,字符串就已經被我們
分割成了幾個小字符串了,比如用戶輸入
“ls -a -l"就轉換成了"ls”,“-a”,"-l"了,接下來
只需創建子進程完成任務即可!

//shell運行原理:通過子進程執行命令,父進程等待&&解析命令
//命令行解釋器是一個常駐程序
#define NUM 1000
#define SIZE 16
char cmd_line[NUM];//保存完整的命令行字符串
char* my_argv[SIZE];//保存打散后的字符串int main()
{while(1){//打印提示信息printf("[kwy@localhost myshell]# ");fflush(stdout);memset(cmd_line,'\0',sizeof cmd_line);//獲取用戶的鍵盤輸入if(fgets(cmd_line,sizeof cmd_line,stdin)==NULL)continue;cmd_line[strlen(cmd_line)-1] = '\0';//將輸入的換行符給清除掉//命令行字符串解析:以空格為分割打散my_argv[0]=strtok(cmd_line," ");//提出第一部分int index=1;while(my_argv[index++] = strtok(NULL," "));//第二次調用strtok時若還想解析第一次調用的字符串,則傳NULL//fork后子進程去完成任務pid_t id=fork();if(id == 0)//子進程{printf("下面的功能讓子進程執行\n");//當執行cd等命令時,改變的是子進程的路徑,而父進程的路徑沒變execvp(my_argv[0],my_argv);exit(1);//執行失敗就返回1}//父進程的代碼,當監工int status = 0;pid_t ret = waitpid(-1,&status,0);if(ret>0) printf("exit code: %d\n",WEXITSTATUS(status));}return 0;
}

關于代碼的解釋都在注釋中
如果你還有哪個地方不懂,歡迎私信


7. 內建命令的特殊性

在實現bash時,可能會遇見一個問題:
就是cd指令進入某個文件夾似乎沒用

這一點其實很好理解,因為指令cd是
進入某個文件夾,而進入此文件夾當然
是當前進程進入了,如果創建了子進程
去進去文件夾,由于寫時拷貝的原因,父
進程并不會進去,所以對于像cd這樣的
指令我們稱為內建命令,也就是不能讓
子進程來完成的命令,只能父進程親自動手!

if(strcmp(my_argv[0],"cd")==0)
{if(my_argv[1]!=NULL)chdir(my_argv[1]);continue;//直接跳到while(1)處
}

chdir即為切換當前的工作目錄

內建命令不止cd,像export,kill
和history等等也是內建命令!


8. 總結以及拓展

進程程序替換可以幫助我們完成很多
任務,制作一個簡易的bash解釋器只是
眾多應用中的一個,隨著我們學習的深入
你還會發現新大陸!

對于程序替換的拓展:

在Linux下,C語言程序不僅可以替換成
C語言程序去執行,還可以替換成python
或Java甚至是bash等程序去執行它們
語言的代碼:

比如python腳本:

#! /usr/bin/python3.6
print("hello Python/n")

運行命令: python test.py

將進程替換為Python程序:execlp("python", "python", "test.py", NULL);

這樣就可以直接在C程序上執行python代碼了!


🔎 下期預告:基礎IO 🔍

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

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

相關文章

【JMeter接口自動化】第8講 Fiddler抓包Jmeter

1)配置好Fiddler 設置Fiddler-Tools-Options-HTTPS 設置Fiddler-Tools-Options-Connections,設置端口為8888 2)查看IP 在CMD中輸入ipconfig 查看IP地址 3)配置Jmeter Http請求——基本,設置Http請求,使用…

輕量管理內核復雜級別的項目

在嵌入式開發中,管理大型項目(例如Linux內核)往往是一個復雜的過程。常規的版本控制系統如Git在處理小型項目時非常高效,但面對龐大的代碼庫時可能會顯得笨重且占用大量存儲空間。本文將介紹幾種輕量級的方法來管理內核級別的項目…

Python 快速入門

1. 語言基礎 1.1 數據類型與變量 Python 是一門動態類型語言,這意味著你不需要顯式聲明變量的類型。Python 解釋器會根據你賦予變量的值自動推斷其類型。這使得 Python 代碼簡潔易懂,但同時也需要注意一些潛在的問題。 1.1.1 Python 數據類型概述 Py…

408鏈表的創建和初始化

首先第一個頭文件,定義結構體類型 typedef struct LNode {int data;struct LNode* next; }LNode,*LinkList; //可能作為第一次寫c語言的小伙伴看不懂這一段typedef是如何定義的 //基本的解釋如下所示 //typedef struct LNode LNode; //typedef struct LNode* LinkL…

apex代碼發送郵件時進行抄送

在 Salesforce 中使用 Apex 代碼發送電子郵件時,可以通過 ccAddresses 屬性來添加抄送(CC)收件人。以下是一個示例代碼,展示了如何使用 Messaging.SingleEmailMessage 類來發送帶有抄送的電子郵件。 示例代碼 public class Emai…

北航數據結構與程序設計第四次作業選填題復習

首先都是線性的,線性包括順序和鏈式,棧和隊都可以用兩種方式實現。棧只能存于棧頂取于棧頂,隊列先進先出,因此存取點是固定的。 函數棧幀創建原理 畫圖即可。 A.顯然不行,5如果第一個出來說明5是最后一個進的&#xf…

Lambda表達式與函數式工具在Python中的應用詳解

目錄 一、引言 二、Lambda表達式 Lambda表達式的定義 Lambda表達式的使用場景 Lambda表達式的示例 三、函數式工具 map()函數 filter()函數 reduce()函數 itertools模塊 functools模塊 四、Lambda表達式與函數式工具的結合使用 五、Lambda表達式與函數式工具的注意…

C語言面試題(拓展)

1、字符串中獲取最長無重復字符子串。 要在字符串中找到最長的無重復字符的子串,可以使用滑動窗口技術。滑動窗口通過兩個指針來表示當前窗口的起始和結束位置,并且維護一個哈希表來記錄字符及其最后出現的位置,以此來確保字符不重復。 以下…

【云嵐家政】-day00-開發環境配置

文章目錄 1 開發工具版本2 IDEA環境配置2.1 編碼配置2.2 自動導包設置2.3 提示忽略大小寫2.4 設置 Java 編譯級別 3 Maven環境3.1 安裝Maven3.2 配置倉庫3.3 IDEA中配置maven 4 配置虛擬機4.1 導入虛擬機4.2 問題 5 配置數據庫環境5.1 啟動mysql容器5.2 使用MySQL客戶端連接數據…

Java Socket 網絡編程實例(阻塞IO、非阻塞IO、多路復用Selector、AIO)

文章目錄 1. 概述2. TCP 阻塞式IO 網絡編程實例2.1 TCP網絡編程服務端2.2 ByteBufferUtil2.3 客戶端代碼2.4 運行截圖 3. TCP 非阻塞式IO 網絡編程實例3.1 服務端3.2 客戶端3.3 運行截圖 4. 多路復用4.1 服務器端4.2 客戶端4.3 運行截圖 5. AIO5.1 AIO 服務端5.2 客戶端5.3 運行…

C++筆試強訓day39

目錄 1.神奇的字母&#xff08;二&#xff09; 2.字符編碼 3.最少的完全平方數 1.神奇的字母&#xff08;二&#xff09; 鏈接https://ac.nowcoder.com/acm/problem/205832 看輸出描述即可知輸出次數最多的那個字母即可。 哈希表直接秒了&#xff1a; #include <iostre…

一維時間序列突變檢測方法(小波等,MATLAB R2021B)

信號的突變點檢測問題是指在生產實踐中&#xff0c;反映各種系統工作狀態的信號&#xff0c;可能因為受到不同類型的噪聲或外界干擾而發生了信號突變&#xff0c;導致嚴重失真的信號出現&#xff0c;因此必須探測突變出現的起點和終點。研究目的在于設計出檢測方案&#xff0c;…

CPU內部結構窺探·「2」

從一條匯編加法指令出發&#xff0c;分析cpu內部發生了什么&#xff1f; 本文將詳細剖析ARMv8架構中加法指令的執行過程&#xff0c;深入理解其在CPU上的運行機制。 ARMv8匯編基礎 在ARMv8匯編語言中&#xff0c;加法指令ADD的基本格式如下&#xff1a; ADD destination, s…

【python】python租房數據分析可視化(源碼+數據+報告)【獨一無二】

&#x1f449;博__主&#x1f448;&#xff1a;米碼收割機 &#x1f449;技__能&#x1f448;&#xff1a;C/Python語言 &#x1f449;公眾號&#x1f448;&#xff1a;測試開發自動化【獲取源碼商業合作】 &#x1f449;榮__譽&#x1f448;&#xff1a;阿里云博客專家博主、5…

在Go語言中如何使用變量

1. 變量 Go 中的變量是標識符。例如&#xff0c;我們可能需要存儲客戶的電子郵件地址&#xff0c;但還需要確保它是有效的。這種情況下&#xff0c;可以創建一個名為 email 的變量來存儲電子郵件的值。電子郵件地址可以分配給 email 變量。 變量引用一個內存地址&#xff0c;賦…

OpenCV學習(4.3) 圖像閾值

1.目的 在本教程中&#xff1a; 你會學到簡單閾值法&#xff0c;自適應閾值法&#xff0c;以及 Otsu 閾值法(俗稱大津法)等。你會學到如下函數&#xff1a;**cv.threshold&#xff0c;cv.adaptiveThreshold** 等。 2.簡單閾值法 此方法是直截了當的。如果像素值大于閾值&am…

word2016版本中同時顯示多個頁面

為了方便查看word內容&#xff0c;我們會將多個頁面同時顯示。 對于2016版&#xff0c;操作方法如下&#xff1a; 視圖 ---》多頁

Jan任意文件讀取/下載和上傳漏洞

自從ChatGPT橫空出世以來&#xff0c;我一直想找一個可以自己訓練的AI大模型&#xff0c;然而在使用Jan的過程中&#xff0c;數據包中傳遞的參數引起了我的興趣&#xff0c;簡單嘗試后發現了任意文件讀取和任意文件上傳漏洞。 簡介 Jan是ChatGPT的開源替代品&#xff0c;它在…

vuInhub靶場實戰系列--bulldog-1

免責聲明 本文檔僅供學習和研究使用,請勿使用文中的技術源碼用于非法用途,任何人造成的任何負面影響,與本人無關。 目錄 免責聲明前言一、環境配置1.1 靶場信息1.2 靶場配置 二、信息收集2.1 主機發現2.1.1 netdiscover2.1.2 nmap主機掃描2.1.3 arp-scan主機掃描 2.2 端口掃描…

友思特案例 | 自動快速定位:使用波長選擇器測量濾光片的關鍵光學性能指標

導讀 光學濾光片檢測的手動調節校準的傳統方法存在諸多不確定誤差和高昂的成本消耗。友思特全自動可調諧光源檢測解決方案&#xff0c;可全自動調節波長帶寬&#xff0c;快速收集光譜數據&#xff0c;縮短檢測時間、降低質檢成本&#xff0c;實現極高的準確率和快速檢測效率。…