欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Linux程序替換方式

 更新時間:2025年06月27日 10:24:18   作者:南猿北者  
這篇文章主要介紹了Linux程序替換方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教

創(chuàng)建子進(jìn)程的目的?

目的:為了幫助父進(jìn)程完成一些特定的任務(wù);

子進(jìn)程幫助父進(jìn)程完成任務(wù)的方式有那些?

1、執(zhí)行一段父進(jìn)程的代碼;(這是我們初學(xué)者經(jīng)常使用子進(jìn)程的方式):

在這里插入圖片描述

2、讓子進(jìn)程執(zhí)行一段與父進(jìn)程完全不一樣、全新的代碼;

那么如何做到讓子進(jìn)程執(zhí)行一段全新的代碼呢?

對子進(jìn)程實現(xiàn)程序替換;

程序替換

如何實現(xiàn)程序替換?

Linux給我們提供了7個接口:

#include <unistd.h>
int execl(const char *path, const char *arg, …);
int execlp(const char *file, const char *arg, …);
int execle(const char *path, const char *arg, …, char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[], char *const envp[]);
int execve(const char *path, char *const argv[], char *const envp[]);

這些函數(shù)叫做exec函數(shù)組,我們暫且先不詳細(xì)講解著些函數(shù)的具體用法,我們后文在重點(diǎn)講解;

我們現(xiàn)在只需知道通過調(diào)用這7個函數(shù)中的任意一個就可以完成程序替換;

什么是程序替換?

先見一見單進(jìn)程版本的程序替換

為此我們先從最簡單的ececl()函數(shù)講解著走:

execl函數(shù)第一個參數(shù)path,表示我們要替換的程序在哪里,第二個參數(shù)arg表示我們想用怎么樣的方式運(yùn)行我們的程序!寫完記得在傳個參數(shù)NULL結(jié)尾!

test代碼:

在這里插入圖片描述

運(yùn)行結(jié)果如下:

在這里插入圖片描述

現(xiàn)象:我們觀察到了我們的進(jìn)程執(zhí)行了我們begin的打印,然后又立馬執(zhí)行了ls -a -l命令,隨之整個進(jìn)程就運(yùn)行結(jié)束了,我們發(fā)現(xiàn)并沒有像我們想象的那樣接著運(yùn)行我們所寫的printf(“end…\n”);語句;這是為什么?

想要弄清楚這些現(xiàn)象產(chǎn)生的原因我們就必須清楚execl()接口進(jìn)行程序替換的原理;

程序替換原理

首先根據(jù)上面的代碼,我們自己寫的代碼(比如:打印begin的語句)在運(yùn)行起來的時候就已經(jīng)是一個進(jìn)程,那么這時候該進(jìn)程就已經(jīng)有了自己的內(nèi)核數(shù)據(jù)結(jié)構(gòu)了,比如:pcb、進(jìn)程地址空間、頁表等!現(xiàn)在當(dāng)我們的進(jìn)程運(yùn)行到ececl語句的時候,會發(fā)生程

序替換:

在這里插入圖片描述

當(dāng)我們自己寫的程序運(yùn)行到execl()語句時,就會根據(jù)ececl()的path參數(shù),將ls命令從磁盤加載進(jìn)物理內(nèi)存!加載到物理內(nèi)存的那個地方呢?加載到我們自己寫的程序?qū)?yīng)的物理內(nèi)存上的位置!

也就是說用ls命令的數(shù)據(jù)和代碼,替換原來老的程序的代碼和數(shù)據(jù),物理空間還是原來的物理空間,頁表的映射關(guān)系也基本不變,如果ls命令數(shù)據(jù)和代碼太多了,os會在頁表增加一些映射關(guān)系!然后該進(jìn)程開始重新運(yùn)行一段新的程序!

在這里插入圖片描述

明白了上面的原理,我們就能明白為什么我們的老程序在運(yùn)行到execl過后,會執(zhí)行一段新的程序!同時由于新程序的代碼和數(shù)據(jù)替換了老程序的代碼和數(shù)據(jù),當(dāng)新程序運(yùn)行結(jié)束過后,并不會再去運(yùn)行原來老的程序的剩下的語句,比如上面代碼中的打

印end語句,因為打印end語句屬于老程序的代碼,而老程序的代碼在execl接口中就被替換了,新程序運(yùn)行完畢,也就表示這個進(jìn)程終止了!新程序的退出碼也就是該進(jìn)程的退出碼了!

換而言之,就是當(dāng)我們的程序利用execl完成程序替換過后,當(dāng)前進(jìn)程的退出碼就由新程序決定了!

為此我們可以查看一下在程序替換過后,進(jìn)程的退出碼!

測試代碼:

在這里插入圖片描述

在這里插入圖片描述

當(dāng)然我們也可以讓我們的程序執(zhí)行一個錯誤的ls命令,再次來觀察當(dāng)前進(jìn)程的退出碼:

在這里插入圖片描述

在這里插入圖片描述

站在進(jìn)程的角度的角度來看等待程序替換:

現(xiàn)在我是一個進(jìn)程,我的代碼段和數(shù)據(jù)段都在物理內(nèi)存上有一份映射,現(xiàn)在我調(diào)用了execl接口,execl會將我在物理內(nèi)存上的數(shù)據(jù)和代碼用一個新的程序的數(shù)據(jù)和代碼來替換!然后我(當(dāng)前進(jìn)程)開始重新運(yùn)行這段新程序!并且我(當(dāng)前進(jìn)程)的退出碼由這個新程序的main函數(shù)返回值來確定!在整個程序替換期間,我(當(dāng)前進(jìn)程)并沒由被銷毀,我依舊存在!在完成程序替換過后,依舊是我(當(dāng)前進(jìn)程)來運(yùn)行這段新程序!并不會創(chuàng)建一個新的進(jìn)程來運(yùn)行新的代碼!

站在新程序的角度來看待程序替換:

我是一個程序,我安安靜靜的躺在磁盤看“電視”,突然某一天我被execl加載進(jìn)內(nèi)存,讓后某個進(jìn)程就要求我?guī)退k件事;那么我(新程序)被加載進(jìn)內(nèi)存這個動作是由誰完成的? execl!??!

execl就充當(dāng)著這個加載器的角色!

既然我自己寫的程序都能加載新的程序,那么OS?

當(dāng)我們想要運(yùn)行某段程序的時候,OS會首先為我們的程序建立pcb、進(jìn)程地址空間、頁表等內(nèi)核數(shù)據(jù)結(jié)構(gòu),也就是說這時候進(jìn)程已經(jīng)創(chuàng)建好了,然后在讓當(dāng)前進(jìn)程調(diào)用execl()接口將我們的程序加載進(jìn)內(nèi)存,然后再開始運(yùn)行我們程序!而我們的程序是從main函數(shù)開始的,但是我們的進(jìn)程是先調(diào)用的execl過后我們的程序才開始運(yùn)行起來的,那么換而言之在execl內(nèi)部,幫助我們完成程序替換過后,execl會調(diào)用該程序的main函數(shù),然后讓該程序成功運(yùn)行起來!

多進(jìn)程版本的程序替換

上面我們講解了單進(jìn)程版本的程序替換和程序替換的原理,接下來我們來嘗試一下多進(jìn)程版本的程序替換:

也就是說我們讓我們的子進(jìn)程去執(zhí)行一段與父進(jìn)程完全不一樣的代碼:

測試代碼:

在這里插入圖片描述

在這里插入圖片描述

當(dāng)然,我們也可以讓子進(jìn)程去運(yùn)行我們自己寫的程序,無論我們的程序是用什么語言寫的!

比如,現(xiàn)在我用C語言寫一個程序,去運(yùn)行一個C++寫的程序:

測試:

被子進(jìn)程運(yùn)行的程序:

在這里插入圖片描述

主程序:

在這里插入圖片描述

運(yùn)行結(jié)果:

在這里插入圖片描述

接下來我們來講解一下多進(jìn)程進(jìn)行程序替換的原理:

首先我們的父進(jìn)程,也就是mytest利用fork函數(shù)創(chuàng)建了一個子進(jìn)程對吧!

那么剛開使的時候,子進(jìn)程會繼承父進(jìn)程的大多數(shù)信息,包括子進(jìn)程會共享著父進(jìn)程的代碼和數(shù)據(jù),通過前面的學(xué)習(xí)我們知道,當(dāng)我們的子進(jìn)程想要修改與父進(jìn)程共享的數(shù)據(jù)時,會發(fā)生寫時拷貝!在物理內(nèi)存中重新找一塊新空間,讓后將將需要修改

的數(shù)據(jù)拷貝到新空間中去,然后修改子進(jìn)程頁表映射到該物理內(nèi)存的映射關(guān)系,然后再讓子進(jìn)程去修改數(shù)據(jù)!以此達(dá)到進(jìn)程之間的相互獨(dú)立!

那么現(xiàn)在也是這樣:剛開始的時候父子進(jìn)程都共享著同一塊物理內(nèi)存的數(shù)據(jù)和代碼:

在這里插入圖片描述

當(dāng)我們的子進(jìn)程調(diào)用execl函數(shù)進(jìn)行程序替換時,是會用程序的代碼和數(shù)據(jù)來替換子進(jìn)程原來數(shù)據(jù)段和代碼段存的信息的!如果我們直接在“數(shù)據(jù)”和“代碼”這塊空間進(jìn)行替換的話,我們就會將父進(jìn)程的代碼和數(shù)據(jù)也替換掉!從而影響到了進(jìn)程的獨(dú)立性!我們現(xiàn)在的目的是不想影想父進(jìn)程,而讓子進(jìn)程執(zhí)行一段全新的代碼,為此os也會也會觸發(fā)寫時拷貝,當(dāng)我們的子進(jìn)程嘗試修改代碼段和數(shù)據(jù)段的信息時,os也會去重新找一物理內(nèi)存中重新找一塊空間來存儲子進(jìn)程的代碼和數(shù)據(jù),同時修改子進(jìn)程代碼段和數(shù)據(jù)段映射關(guān)系!

重新理解Shell運(yùn)行原理

明白了上面的過程我們就能更好的理解Shell的運(yùn)行原理了,首先shell從命令行接受到我們的命令后會創(chuàng)建一個子進(jìn)程來執(zhí)行我們的命令,然后在讓該子進(jìn)程調(diào)用execl函數(shù)來進(jìn)程程序替換,替換掉子進(jìn)程從Shell哪里繼承下來的代碼和數(shù)據(jù)!然后讓子進(jìn)程開始運(yùn)行這段程序!

execl函數(shù)組

下面我們來正式介紹一下execl函數(shù)組:

int execl(const char *path, const char *arg, …)

參數(shù): path//用于指定我們執(zhí)行的命令在哪里

arg: 可變參數(shù),可以傳任意個參數(shù),該參數(shù)的作用主要是告訴execl()你想怎樣執(zhí)行這段程序,你在命令行是怎么寫的,在arg參數(shù)就怎么寫,注意分割;比如:我們需要讓execl按照ls -a -l的格式執(zhí)行l(wèi)s命令,那么我們喂給execl的參數(shù)就是(從

第二個參數(shù)起):“ls”、“-a”、“-l”,NULL;一個選項一個字符串,注意當(dāng)我們確定完程序運(yùn)行的格式過后,必須以再傳遞一個NULL結(jié)尾!表示我們已經(jīng)傳遞完當(dāng)前程序的執(zhí)行的格式;

比如:

在這里插入圖片描述

返回值

該函數(shù)只會返回-1;由于execl是進(jìn)行程序替換,當(dāng)execl完成程序替換那一刻開始,execl后續(xù)的代碼都被替換成了新程序的代碼和數(shù)據(jù),根本就運(yùn)行不到后續(xù)的代碼和數(shù)據(jù),因此也就無法返回程序替換成功的返回值;當(dāng)我們的程序替換失敗的時候,我們進(jìn)程的老數(shù)據(jù)和代碼并沒由被替換掉,當(dāng)前進(jìn)程依舊按照順序執(zhí)行剩下的代碼,同時才能向上面返回-1來表示程序替換失??!

也就是說,execl程序替換成功是沒有返回值的!如果execl有返回值那么說明程序替換失敗,當(dāng)前進(jìn)程就會執(zhí)行execl后續(xù)的代碼!

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

我們可以發(fā)現(xiàn)execv接口與execl接口十分相似,但是在參數(shù)上卻并不是一樣的!

execl的參數(shù)可以是任意個,而execv的參數(shù)只有2個;

同時execv的功能與execl的功能一樣,只是在使用上有點(diǎn)區(qū)別!

我們可以看一看exec+l就表示execl,這個l(list)就代表著列表的意思!表示execl的程序運(yùn)行格式以列表的形式傳遞!

exec+v表示execv,這個v(vector)表示數(shù)組的意思,就表示程序運(yùn)行的格式以數(shù)組的形式傳遞;

具體演示:

在這里插入圖片描述

程序運(yùn)行結(jié)果:

在這里插入圖片描述

程序依舊正常運(yùn)行;

有了前面的理解后面我們在認(rèn)識其他exec函數(shù)就輕松了:

int execlp(const char *file, const char *arg, …);
  • exec+l+p:l表示以列表的形式傳遞程序如何運(yùn)行這個程序!
  • p:表示path,表示我們只需告訴execlp我們要運(yùn)行的程序的名稱也就是傳遞file參數(shù),execlp會自動去PATH環(huán)境變量下搜索!

具體演示:

在這里插入圖片描述

程序運(yùn)行結(jié)果:

在這里插入圖片描述

int execle(const char *path, const char *arg, …,char *const envp[]);
  • l:如何運(yùn)行程序的參數(shù)以列表的形式傳遞;
  • e:env表示自己維護(hù)環(huán)境變量

比如:我們可以將當(dāng)前進(jìn)程的環(huán)境變量表傳遞給我們的新程序!

我們的新程序就可以使用這張環(huán)境變量表:

子進(jìn)程去替換的程序:

在這里插入圖片描述

主程序:

在這里插入圖片描述

程序運(yùn)行結(jié)果:

在這里插入圖片描述

我們也可以向環(huán)境變量里面加一點(diǎn)東西進(jìn)去:

這里我們就需要使用putenv()這個函數(shù)了,putenv()功能是向環(huán)境變量表中導(dǎo)入一個環(huán)境變量!

在這里插入圖片描述

在這里插入圖片描述

講解到這里,其他的execl函數(shù)也就依此類推了;

只不過我們需要注意一下,在exec函數(shù)組中

只有int execve(const char *filename, char *const argv[])是真正的系統(tǒng)調(diào)用!

其他的exec函數(shù)是基于該系統(tǒng)調(diào)用進(jìn)行的封裝!

簡易版Shell

#include<stdio.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<unistd.h>
#include<string.h>
#include<stdbool.h>

#define COMMOD_NUM 256
#define ARGV_NUM 64


bool Strtok(char*commod,char**argv)
{
  //先跳過空格
  size_t i=0;
  size_t len=strlen(commod);
  size_t k=0;
 while(i<len&&commod[i]==' ')
   i++;
 if(i>=len)
 return false;
 size_t begin=i;
 size_t end=begin;
 while(begin<len)
 {
   while(commod[end]!=' '&&commod[end]!='\0')
     end++;
   commod[end]='\0';
   argv[k++]=commod+begin;
   begin=end+1;
   end=begin;   
 }
 argv[k]=NULL;
 return true;
}
extern char**environ;
int main()
{

while(1)
{
  printf("[cxk@VM-12-16-centos myshell]$ ");
char commod[COMMOD_NUM]={0};//用于接受從命令行輸入的命令
char*argv[ARGV_NUM]={NULL};//用于存儲將commod切割成一個一個字符串的指針
fgets(commod,COMMOD_NUM,stdin);
commod[strlen(commod)-1]='\0';
  //分割字符串
   if(Strtok(commod,argv)==false)
     continue;
//創(chuàng)建子進(jìn)程
pid_t id=fork();
if(id==0)
{
int n= execvp(argv[0],argv);
if(n==-1)
{
  printf("-bash: %s: command not found\n",argv[0]);
  exit(1);
}
}
//父進(jìn)程
waitpid(-1,NULL,0);
}
  return 0;
}

總結(jié)

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

最新評論