【進程控制二】進程替換和bash解釋器

【進程控制二】進程替換

  • 1.exec系列接口
  • 2.execl系列
    • 2.1execl接口
    • 2.2execlp接口
    • 2.3execle
  • 3.execv系列
    • 3.1execv
    • 3.2總結
  • 4.實現一個bash解釋器
    • 4.1內建命令

通過fork創建的子進程,會繼承父進程的代碼和數據,因此本質上還是在執行父進程的代碼
進程替換可以將別的進程的代碼替換到自己的代碼區,讓自己去執行別人的代碼
進程替換是通過exec系列系統調用接口實現的

1.exec系列接口

先看看man手冊中的exec接口:
在這里插入圖片描述
這些接口健壯度很高,就算錯誤地使用了接口,結果也不容易出錯

2.execl系列

execl隸屬于exec系列,加上l代表list,表示參數采用列表

2.1execl接口

int execl(const char *pathname, const char *arg, ...);
  • pathname:指定用于替換的進程的路徑
  • arg:以何種方式運行進程
  • ...:以何種方式運行該進程
  • NULL:當參數列表list結束,必須以NULL結尾
  • 返回值:如果調用成功,該函數不會返回,因為當前進程的映像被替換

我們現在要替換ls指令到自己的進程中,ls指令在/usr/bin/ls中
我們希望以ls -l -a的形式來調用這個進程,因此我們的三個參數 “ls”, “-l”, "-a"就是這個指令拆分出來的三個字符串
最后以NULL結尾


#include<unistd.h>
#include<stdio.h>int main()
{printf("程序替換前\n");execl("/usr/bin/ls", "ls", "-l", "-a", NULL);//執行ls -l并替代當前進程printf("程序替換后\n");     return 0;
}

輸出結果:
在這里插入圖片描述
我們成功在當前進程中替換成了ls指令,并以ls -l -a的形式調用
但沒有打印“程序替換后”,因為進程替換是用別的進程的代碼區覆蓋掉自己原先的代碼區,所以execl一旦執行,整個進程的代碼都被替換了,那么printf(“程序替換后\n”);就會被覆蓋掉,最后不輸出

2.2execlp接口

int execlp(const char* file, const char* arg, ... );
  • file:指定替換的進程名稱(不用指明路徑,會自動去環境變量PATH指定的路徑中查找)
  • arg:以何種方式運行進程
  • ...: 運行該進程的選項
  • 最后以NULL結尾
  • 返回值:如果調用成功,該函數不會返回,因為當前進程的映像被替換
int main()    
{    printf("程序替換前\n");    execlp("ls","-ls""-l","-a",NULL);  printf("程序替換后\n");        return 0;    
} 

2.3execle

int execle(const char *pathname, const char *arg, ... ,char *const envp[] );
  • pathname:指定用于替換的進程的路徑
  • arg:以何種方式運行進程
  • ...:以何種方式運行該進程
  • NULL:當參數列表list結束,必須以NULL結尾
  • envp:指針數組存儲環境變量,用于設置新程序的環境變量,數組必須以 NULL 結束
  • 返回值:如果調用成功,該函數不會返回,因為當前進程的映像被替換
int main()    
{    const char* _env[] = {"My_env = 666666666666666666666",NULL};printf("程序替換前\n");    execlp("/usr/bin/ls","-ls","-l","-a",NULL,_env);  printf("程序替換后\n");        return 0;    
} 

execle可以給替換后的進程指定環境變量表
在這里插入圖片描述

3.execv系列

v就是vector,以數組的形式,把選項都存在數組中,將整個數組傳入

3.1execv

int execv(const char *pathname, char *const argv[]):
  • pathname:指定用于替換的進程的路徑
  • argv:指定以何種方式調用進程,將這些選項存儲在一個數組中
int main()    
{    char* set[] = {"ls","-a","-l",NULL};printf("程序替換前\n");    execv("/usr/bin/ls",set);  printf("程序替換后\n");        return 0;    
} 

將我們要執行程序的方法用數組存起來再把數組傳過去
在這里插入圖片描述

3.2總結

在這里插入圖片描述
其他接口就不一一演示了
健壯度演示:

int main()    
{    char* set[] = {"ls","-a","-l",NULL};printf("程序替換前\n");    execvp("/usr/bin/ls",set);  //自動查找可執行文件并執行,但我們主動傳遞了文件路徑也不會出錯printf("程序替換后\n");        return 0;    
} 

雖然使用的是execvp,但我們主動傳遞了文件路徑也不會出錯
在這里插入圖片描述

4.實現一個bash解釋器

在這里插入圖片描述
接下來要把字符串以空格為分割進行打散,strtok函數可以幫助我們實現
在這里插入圖片描述
代碼如下:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>#define NUM 1024//輸入命令行字符串
#define SIZE 64 //打散后的命令行字符串
#define SEP " " //字符串分隔符int lastcode = 0;//上個進程的退出碼const char* getUsername()
{const char* name = getenv("USER");if(name) return name;else return "none";
}const char* getHostname()
{const char* hostname = getenv("HOSTNAME");if(hostname) return hostname;else return "none";
}const char* getCwd()
{const char* cwd = getenv("PWD");if(cwd) return cwd;else return "none";
}int GetUserCommand(char* command,int num)
{printf("[%s@%s %s]#",getUsername(),getHostname(),getCwd());fgets(command,num,stdin);//在fgets()函數的眼里,換行符’\n’也是它要讀取的一個普通字符而已。在讀取鍵盤輸入的時候會把最后輸入的回車符也存進數組里面,即會把’\n’也存進數組里面command[strlen(command) - 1] = '\0';//將輸入的\n清除掉return strlen(command);
}void CommandSplit(char* in,char* out[])
{int argc = 0;out[argc++] = strtok(in,SEP);while(out[argc++] = strtok(NULL,SEP));
}int execute(char* argv[])//執行命令
{pid_t id = fork();if(id < 0) return -1;else if(id == 0)//child process{execvp(argv[0],argv);//程序替換}else//father process{int status = 0;pid_t rid = waitpid(id,&status,0);if(rid > 0){lastcode = WEXITSTATUS(status);//刷新退出碼}}return 0;
}int main()
{	while(1){char UserCommand[NUM];//用于保存即將輸入的命令行字符串char* argv[SIZE];//保存將會被打散的字符串//GetUserCommand(UserCommand,sizeof(UserCommand));//打印提示符&&獲取用戶命令字符串CommandSplit(UserCommand,argv);//分割字符串execute(argv);//執行命令}return 0;
}

4.1內建命令

我們實現bash后,可能會遇見一個問題:cd指令進入某個文件夾似乎沒用
在這里插入圖片描述

因為指令cd是進入某個文件夾,而進入此文件夾當然是由當前的父進程進入
如果由子進程去執行,由于寫時拷貝的原因父進程并不會進去
對于像cd這樣的指令我們稱為內建命令,也就是不能讓子進程來完成的命令,只能父進程親自執行

我們需要主動添加內建命令的判斷

char cwd[1024];//父進程要進入的文件路徑char* homepath()
{char* home = getenv("HOME");if(home) return home;else return (char*)".";
}
void cd(const char* path)
{chdir(path);//切換當前的工作目錄char tmp[1024];getcwd(tmp,sizeof(tmp));sprintf(cwd,"PWD=%s",tmp);putenv(cwd);
}
int doBuildin(char* argv[])
{if(strcmp(argv[0], "cd") == 0){char *path = NULL;if(argv[1] == NULL) path = homepath();else path = argv[1];cd(path);return 1;}return 0;
}

在這里插入圖片描述
內建命令不止cd,像export,kill和history等等也是內建命令

完整代碼

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>#define NUM 1024//輸入命令行字符串
#define SIZE 64 //打散后的命令行字符串
#define SEP " " //字符串分隔符int lastcode = 0;//上個進程的退出碼
char cwd[1024];//父進程要進入的文件路徑const char* getUsername()
{const char* name = getenv("USER");if(name) return name;else return "none";
}const char* getHostname()
{const char* hostname = getenv("HOSTNAME");if(hostname) return hostname;else return "none";
}const char* getCwd()
{const char* cwd = getenv("PWD");if(cwd) return cwd;else return "none";
}int GetUserCommand(char* command,int num)
{printf("[%s@%s %s]#",getUsername(),getHostname(),getCwd());fgets(command,num,stdin);//在fgets()函數的眼里,換行符’\n’也是它要讀取的一個普通字符而已。在讀取鍵盤輸入的時候會把最后輸入的回車符也存進數組里面,即會把’\n’也存進數組里面command[strlen(command) - 1] = '\0';//將輸入的\n清除掉return strlen(command);
}void CommandSplit(char* in,char* out[])
{int argc = 0;out[argc++] = strtok(in,SEP);while(out[argc++] = strtok(NULL,SEP));
}char* homepath()
{char* home = getenv("HOME");if(home) return home;else return (char*)".";
}
void cd(const char* path)
{chdir(path);//切換當前的工作目錄char tmp[1024];getcwd(tmp,sizeof(tmp));sprintf(cwd,"PWD=%s",tmp);putenv(cwd);
}
int doBuildin(char* argv[])
{if(strcmp(argv[0], "cd") == 0){char *path = NULL;if(argv[1] == NULL) path = homepath();else path = argv[1];cd(path);return 1;}return 0;
}int execute(char* argv[])//執行命令
{pid_t id = fork();if(id < 0) return -1;else if(id == 0)//child process{execvp(argv[0],argv);//程序替換}else//father process{int status = 0;pid_t rid = waitpid(id,&status,0);if(rid > 0){lastcode = WEXITSTATUS(status);//刷新退出碼}}return 0;
}int main()
{	while(1){char UserCommand[NUM];//用于保存即將輸入的命令行字符串char* argv[SIZE];//保存將會被打散的字符串//GetUserCommand(UserCommand,sizeof(UserCommand));//打印提示符&&獲取用戶命令字符串CommandSplit(UserCommand,argv);//分割字符串int n = doBuildin(argv);//判斷是否是內建命令并執行if(n) continue;execute(argv);//執行命令}return 0;
}

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

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

相關文章

Java基礎語法之數組

數組 一、認識數組 1.什么是數組 數組就是一個容器&#xff0c;用來存一批同種類型的數據。 舉例 20, 10, 80, 60, 90 int[] arr {20, 10, 80, 60, 90};張三, 李四, 王五 String[] names {"張三", "李四", "王五"};2.為什么要使用數組 假設…

Java死鎖排查:線上救火實戰指南

想象一下&#xff0c;你正在值班&#xff0c;突然監控告警紅成一片&#xff0c;用戶反饋雪花般飄來&#xff1a;“系統卡死了&#xff01;用不了了&#xff01;” —— 這很可能就是Java應用遭遇了“死鎖”這個大魔王。這時候&#xff0c;你就是救火隊長&#xff0c;首要任務不…

006-nlohmann/json 結構轉換-C++開源庫108杰

絕大多數情況下&#xff0c;程序和外部交換的數據&#xff0c;都是結構化的數據。 1. 手工實現——必須掌握的基本功 在的業務類型的同一名字空間下&#xff0c;實現 from_json 和 to_json 兩個自由函數&#xff08;必要時&#xff0c;也可定義為類型的友元函數&#xff09;&a…

白盒測試——基本路徑測試法

一、實驗名稱 白盒測試——基本路徑測試法 二、實驗目的 白盒測試是結構測試&#xff0c;是依據被測程序的內部邏輯結構設計測試用例&#xff0c;驅動被測程序運行完成的測試&#xff0c;通過本實驗希望&#xff1a; 1、掌握基本路徑測試法的基本概念&#xff0c;用具體的例子…

【嵌入模型與向量數據庫】

目錄 一、什么是向量&#xff1f; 二、為什么需要向量數據庫&#xff1f; 三、向量數據庫的特點 四、常見的向量數據庫產品 FAISS 支持的索引類型 vs 相似度 五、常見向量相似度方法對比 六、應該用哪種 七、向量數據庫的核心邏輯 &#x1f50d; 示例任務&#xff1a;…

matlab中和三角函數相關的內容

和三角相關的函數 以下內容為Ai輸出 函數描述示例sin(x)正弦函數&#xff0c;返回x的正弦值&#xff0c;x單位為弧度sin(pi/2)cos(x)余弦函數&#xff0c;返回x的余弦值&#xff0c;x單位為弧度cos(pi)tan(x)正切函數&#xff0c;返回x的正切值&#xff0c;x單位為弧度tan(pi…

scratch基礎-外觀模塊

一、本次任務 二、內容詳解 1、模塊介紹 1、說[你好] (2)秒&#xff1a;臨時對話框&#xff0c;短暫對話 2、說[你好]&#xff1a;持續顯示對話框&#xff0c;長文本顯示 3、思考[嗯…] (2)秒&#xff1a;臨時顯示思考氣泡&#xff0c;用于角色思考 4、思考[嗯…] &#xff1a…

TAOCMS漏洞代碼學習及分析

路由規則 分為前臺和后臺&#xff0c; 前臺在api.php中 <?php session_start(); include(config.php); include(SYS_ROOT.INC.common.php); $ctrl$_REQUEST[ctrl]; $action$_REQUEST[action]; $mucfirst($action); if(!in_array($m,array(Api,Comment)))d…

Spring @Scheduled注解詳解

文章目錄 1.Scheduled注解定義2.配置 Scheduled2.1 開啟定時任務支持2.2 創建定時任務 3. 常用屬性3.1 fixedRate3.2 fixedDelay3.3 cron 4.工作原理4.1 基于TaskScheduler4.2 使用 ThreadPoolTaskScheduler4.3 定時任務的執行流程 5. 延時執行的定時任務5.1 創建定時任務類5.2…

理解計算機系統_并發編程(5)_基于線程的并發(二):線程api和基于線程的并發服務器

前言 以<深入理解計算機系統>(以下稱“本書”)內容為基礎&#xff0c;對程序的整個過程進行梳理。本書內容對整個計算機系統做了系統性導引,每部分內容都是單獨的一門課.學習深度根據自己需要來定 引入 接續上一篇理解計算機系統_并發編程(4)_基于線程的并發(一…

使用PhpStudy搭建Web測試服務器

一、安裝PhpStudy 從以下目錄下載PhpStudy安裝文件 Windows版phpstudy下載 - 小皮面板(phpstudy) (xp.cn) 安裝成功之后打開如下界面 點擊啟動Apache 查看網站地址 在瀏覽器中輸入localhost:88,出現如下頁面就ok了 二、與Unity交互 1.配置下載文件路徑&#xff0c;點擊…

cocos creator 3.8 下的 2D 改動

在B站找到的系統性cocos視頻教程,純2D開發入門,鏈接如下: zzehz黑馬程序員6天實戰游戲開發微信小程序&#xff08;Cocos2d的升級版 CocosCreator JavaScript&#xff09;_嗶哩嗶哩_bilibili黑馬程序員6天實戰游戲開發微信小程序&#xff08;Cocos2d的升級版 CocosCreator Ja…

【Hot 100】208. 實現 Trie (前綴樹)

目錄 引言實現 Trie (前綴樹)我的解題代碼解析代碼思路分析優化建議1. 內存泄漏問題2. 使用智能指針優化內存管理3. 輸入合法性校驗&#xff08;可選&#xff09;4. 其他優化 總結 &#x1f64b;?♂? 作者&#xff1a;海碼007&#x1f4dc; 專欄&#xff1a;算法專欄&#x1…

Unity3D仿星露谷物語開發42之粒子系統

1、目標 使用例子系統&#xff0c;實現割草后草掉落的特效。 通過PoolManager獲取特效預制體&#xff0c;通過VFXManager來觸發特效。 2、配置例子特效 在Hierarchy -> PersistentScene下創建新物體命名為Reaping。 給該物體添加Particle System組件。 配置例子系統參數…

視覺-語言基礎模型作為高效的機器人模仿學習范式

摘要 近期&#xff0c;視覺語言基礎模型領域取得的進展彰顯了其在理解多模態數據以及解決復雜視覺語言任務&#xff08;包括機器人操作任務&#xff09;方面的能力。我們致力于探尋一種簡便的方法&#xff0c;利用現有的視覺語言模型&#xff08;VLMs&#xff09;&#xff0c;僅…

zst-2001 上午題-歷年真題 算法(5個內容)

回溯 算法 - 第1題 找合適的位置&#xff0c;如果沒有位置就按B回家 d 分治 算法 - 第2題 b 算法 - 第3題 a 算法 - 第4題 劃分一般就是分治 a 算法 - 第5題 分治 a 0-1背包 算法 - 第6題 c 算法 - 第7題 最小的為c 3100 c 算法 - 第8題 …

淺論3DGS濺射模型在VR眼鏡上的應用

擺爛仙君小課堂開課了&#xff0c;本期將介紹如何手搓VR眼鏡&#xff0c;并將隨手拍的電影變成3D視頻。 一、3DGS模型介紹 3D 高斯模型是基于高斯函數構建的用于描述三維空間中數據分布概率的模型&#xff0c;高斯函數在數學和物理領域有著廣泛應用&#xff0c;其在 3D 情境下…

2025年中期大語言模型實力深度剖析

I. 引言&#xff1a;解讀2025年動態LLM競技場中的“實力” 用戶提出的“如今哪個大語言模型最強”這一問題&#xff0c;精準地反映了業界對飛速發展的人工智能&#xff08;AI&#xff09;領域的高度關注。本報告基于截至2025年5月的最新數據&#xff0c;旨在對這一問題進行全面…

Spark緩存-cache

一、RDD持久化 1.什么時候該使用持久化&#xff08;緩存&#xff09; 2. RDD cache & persist 緩存 3. RDD CheckPoint 檢查點 4. cache & persist & checkpoint 的特點和區別 特點 區別 二、cache & persist 的持久化級別及策略選擇 Spark的幾種持久化…

嵌入式開發學習日志(數據結構--順序結構單鏈表)Day19

一、順序結構 安裝軟件命令&#xff1a; sudo apt-get install (軟件名) 安裝格式化對齊&#xff1a;sudo apt-get install clang-format 內存泄漏檢測工具&#xff1a; sudo apt-get install valgrind 編譯后&#xff0c;使用命令 valgrind ./a.out 即可看內…