Linux系統【二】exec族函數及應用

文件描述符

文件描述符表是一個指針數組,文件描述符是一個整數。
文件描述符表對應的指針是一個結構體,名字為file_struct,里面保存的是已經打開文件的信息

需要注意的是父子進程之間讀時共享,寫時復制的原則是針對物理地址而言的,通過程序操控虛擬地址的我們是無法查別到這個問題的,其中的機理由MMU進行控制(詳見我的上一篇博客:Linux系統【一】CPU+MMU+fork函數創建進程)

測試程序:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<sys/types.h>int main()
{int *t = (int*)malloc(sizeof(int));*t = 0;printf("parent process:&t=%p\n",t);pid_t pid;pid = fork();if(0 == pid){printf("child process:&t=%p\n",t);(*t)++;printf("child process:&t=%p\n",t);printf("child process:t=%d\n",*t);}else{wait(NULL);printf("parent process:&t=%p\n",t);printf("parent process:t=%d\n",*t);}return 0;
}

運行結果:
在這里插入圖片描述

exec函數族

fork創建子進程后執行的是和父進程相同的程序(有可能執行不同的代碼分支),子進程往往要調用一種exec函數來執行另一個程序,當進程調用一種exec函數時,該進程的用戶空間代碼和數據完全被新程序替換,從新程序的啟動例程(簡單來講就是main函數,實際上是一個由匯編和C混合編寫的程序)。調用exec并不創建新進程,所以調用exec前后進程的id并未改變

命令行執行程序的實質其實就是fork+exec

#include <unistd.h>
extern char **environ;
int execl(const char *path, const char *arg, .../* (char  *) NULL */);
int execlp(const char *file, const char *arg, ...
//第一個參數為可執行文件的文件名,后面的參數是命令行參數/* (char  *) NULL */);
int execle(const char *path, const char *arg, .../*, (char *) NULL, 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[]);
  • l list 命令行參數列表
  • p PATH 搜索file時使用PATH變量
  • v vector 使用命令行參數數組
  • e environment 使用環境變量數組,不適用進程原有的環境變量,設置新加載程序運行的環境變量

execlp

調用程序的時候會搜索一遍環境變量,如果在環境變量中可以找到程序就會執行,否則按照路徑運行程序。

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>int main()
{pid_t pid;pid = fork();if(-1 == pid){perror("fork error:");exit(1);}else if(0 == pid){execlp("ls","ls","-l","-a",(char *)NULL);}else{sleep(1);printf("Parent");}return 0;
}

這里解釋一下上述程序:execlp函數第一個參數是調用的程序,從第二個參數開始是命令行參數,最后一定要以(char*)NULL結尾才能正常運行。其中比較特殊的是命令行參數的第一個參數是什么不重要,我上面雖然寫的是ls,但其實隨便寫什么都可以。

為什么會有這種寫什么都可以的情況出現呢?實際原因是:我們在執行程序的時候系統會默認帶有一個參數(雖然這個參數大多數情況下都與程序名相同,但是某些shell會將此參數設置為完全的路徑名),所以我們第一個參數是不會有作用的,真正有作用的參數是從參數數組中第一個元素開始的。

例如:

//test.c
#include<stdio.h>int main(int argc,char* args[])
{for(int i=0;i<=argc;++i){printf("%d=[%s]\n",i,args[i]);}return 0;
}

在這里插入圖片描述

將上面兩個程序組合:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>int main()
{pid_t pid;pid = fork();if(-1 == pid){perror("fork error:");exit(1);}else if(0 == pid){execlp("./test","first","second","third",(char *)NULL);printf("Child\n");}else{sleep(1);printf("Parent\n");}return 0;
}

在這里插入圖片描述
我們可以發現一旦運行execlp將不會再運行原來進程后面的代碼,原本進程的代碼塊完全被新的文件代替

execl

不會查找環境變量,直接按照路徑運行程序

execle

需要借助環境變量表char ** environ

execv

傳入的需要是一個字符串數組,而不能直接傳命令行參數

例題:將當前系統中的進程信息打印到文件中

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>int main(int argc,char *argv[])
{if(argc != 2){printf("you should input filename\n");exit(1);}freopen(argv[1],"w",stdout);execlp("ps","ps","aux",(char*)NULL);return 0;
}

更好的做法是使用dup2函數(其中的2的意思是to,如果后面是4的話意思常常是for),并將新進程放在子進程中

dep2(old fd,new fd); // 將舊文件描述符中的內容拷貝到新文件描述符中
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>int main(int argc,char *argv[])
{if(argc != 2){printf("you should input filename\n");exit(1);}
//	freopen(argv[1],"w",stdout);int fd = open(argv[1],O_CREAT | O_RDWR,06444);if(-1 == fd){perror("openfile error:");exit(1);}dup2(fd,STDOUT_FILENO);pid_t pid=fork();if(-1 == pid){perror("fork error:");exit(1);}else if(0 == pid){execlp("ps","ps","aux",(char*)NULL);perror("exec error:");	//不需要判斷返回值,如果成功不會運行下面的語句exit(1);}else{close(fd);}return 0;
}

需要注意的是使用dup2函數以后原本標準輸出文件指針丟失了,如果還要回到標準輸出文件,應該之前用dup保存一份,使用之后再dup2回去就可以了。

exec函數族沒有成功返回值,只有失敗返回值

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

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

相關文章

白話C++系列(27) -- RTTI:運行時類型識別

http://www.cnblogs.com/kkdd-2013/p/5601783.htmlRTTI—運行時類型識別 RTTI&#xff1a;Run-Time Type Identification。 那么RTTI如何來體現呢&#xff1f;這就要涉及到typeid和dynamic_cast這兩個知識點了。為了更好的去理解&#xff0c;那么我們就通過一個例子來說明。這個…

使用頭文件的原因和規范

原因 通過頭文件來調用庫功能。在很多場合&#xff0c;源代碼不便&#xff08;或不準&#xff09;向用戶公布&#xff0c;只 要向用戶提供頭文件和二進制的庫即可。用戶只需要按照頭文件中的接口聲明來調用庫 功能&#xff0c;而不必關心接口怎么實現的。編譯器會從庫中提取相應…

轉圈踢人問題

https://www.cnblogs.com/lanxuezaipiao/p/3339603.html 有N個人圍一圈依次報數&#xff0c;數到3的倍數的人出列&#xff0c;問當只剩一個人時他原來的位子在哪里&#xff1f; 解答&#xff1a;經典的轉圈踢人問題&#xff0c;好吧專業一點&#xff0c;約瑟夫環問題&#xff0…

Linux系統【三】回收子進程

孤兒進程 父進程先于子進程結束&#xff0c;則子進程成為孤兒進程&#xff0c;子進程的父進程成為init進程&#xff0c;則稱init進程領養孤兒進程。現在好像是用戶進程中的system進程。 僵尸進程 進程終止&#xff0c;父進程不進行回收&#xff0c;自己成殘留資源(PCB)存放在…

string類的基本實現

https://blog.csdn.net/qq_29503203/article/details/52265829在面試中面試官常常會讓你寫出string類的基本操作&#xff0c;比如&#xff1a;構造函數&#xff0c;析構函數&#xff0c;拷貝構造等等.下面是除此之外的一些操作&#xff0c;希望可以幫助你更好的理解string以便以…

Python3常用數據結構

Python3中有三種組合數據類型&#xff0c;分別為&#xff1a; 序列類型&#xff1a;字符串&#xff08;str&#xff09;、元組&#xff08;tuple&#xff09;、列表&#xff08;list&#xff09;集合類型&#xff1a;集合&#xff08;set&#xff09;映射類型&#xff1a;字典…

Linux C++ 回射服務器

http://blog.csdn.net/qq_25425023/article/details/53914820回射服務器就是服務端將客戶端的數據發送回去。我實現的回射服務器返回增加了時間。服務端代碼&#xff0c;可以很容易看懂&#xff1a;[cpp] view plaincopy#include <sys/socket.h> #include <stdio.h&g…

TCP第四次揮手為什么要等待2MSL

當客戶端進入TIME-WAIT狀態的時候(也就是第四次揮手的時候)&#xff0c;必須經過時間計數器設置的時間2MSL(最長報文段壽命)后&#xff0c;才能進入關閉狀態&#xff0c;這時為什么呢&#xff1f;&#xff1f;&#xff1f; 這最主要是因為兩個理由&#xff1a; 1、為了保證客戶…

計算機網絡【一】概述+OSI參考模型

網絡概述 局域網:覆蓋范圍小(100m以內)&#xff0c;自己花錢買設備&#xff0c;帶寬固定(10M,100M,1000M)&#xff0c;自己維護&#xff08;接入層交換機直接連接電腦、匯聚層交換機直接連接接入層交換機&#xff09; 廣域網:距離遠&#xff0c;花錢買服務&#xff0c;租帶寬&…

單鏈表逆序的多種方式

https://www.cnblogs.com/eniac12/p/4860642.htmltemplate<class T> void List<T>::Inverse() {if(first NULL) return;LinkNode<T> *p, *prev, *latter; p first->link;   // 當前結點prev NULL;   // 前一結點l…

Linux系統【四】進程間通信-管道

進程間通信&#xff08;IPC Interprocess Communication&#xff09; 進程和進程之間的通信只能通過內核&#xff0c;在內核中提供一塊緩沖區進行通信。內核提供的這種機制叫做IPC 在進程間完成數據傳輸需要借助操作系統提供的特殊方法&#xff0c;如&#xff1a;文件&#xf…

單鏈表各種操作詳解

#include "stdio.h" #include "stdlib.h"#define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0#define MAXSIZE 20 /* 存儲空間初始分配量 */typedef int Status;/* Status是函數的類型,其值是函數結果狀態代碼&#xff0c;如OK等 */ typedef int…

Linux系統【五】進程間通信-共享內存mmap

mmap函數 #include <sys/mman.h> void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);參數&#xff1a; void *addr建立映射區的首地址&#xff0c;由Linux內核指定&#xff0c;所以我們直接傳遞NULL。也就是說雖然這是一個參宿但是并不…

socket編程 -- epoll模型服務端/客戶端通信的實現

https://blog.csdn.net/y396397735/article/details/50680359 本例實現如下功能&#xff1a; 支持多客戶端與一個服務端進行通信&#xff0c;客戶端給服務端發送字符串數據&#xff0c;服務端將字符串中小寫轉為大寫后發送回客戶端&#xff0c;客戶端打印輸出經轉換后的字符串。…

Python3 面向對象程序設計

類的定義 Python使用class關鍵字來定義類 class Car:def infor(self):print("This is a car") car Car() car.infor()內置方法isinstance()來測試一個對象是否為某個類的實例 self參數 類的 所有實例方法都有一個默認的self參數&#xff0c;并且必須是方法的第一…

計算機網絡【二】物理層基礎知識

計算機網絡的性能 速率&#xff1a;連接在計算機網絡上的主機在數字信道上傳送數據位數的速率&#xff0c;也成為data rate 或bit rate&#xff0c;單位是b/s,kb/s,Mb/s,Gb/s。 我們平時所講的寬帶的速度是以字為單位的&#xff0c;但是實際中應用一般顯示的是字節 &#xff0…

Linux網絡編程——tcp并發服務器(多進程)

https://blog.csdn.net/lianghe_work/article/details/46503895一、tcp并發服務器概述一個好的服務器,一般都是并發服務器&#xff08;同一時刻可以響應多個客戶端的請求&#xff09;。并發服務器設計技術一般有&#xff1a;多進程服務器、多線程服務器、I/O復用服務器等。二、…

求序列第K大算法總結

參考博客&#xff1a;傳送門 在上面的博客中介紹了求序列第K大的幾種算法&#xff0c;感覺收益良多&#xff0c;其中最精巧的還是利用快速排序的思想O(n)查詢的算法。仔細學習以后我將其中的幾個實現了一下。 解法 1&#xff1a; 將亂序數組從大到小進行排序然后取出前K大&a…

Linux網絡編程——tcp并發服務器(多線程)

https://blog.csdn.net/lianghe_work/article/details/46504243tcp多線程并發服務器多線程服務器是對多進程服務器的改進&#xff0c;由于多進程服務器在創建進程時要消耗較大的系統資源&#xff0c;所以用線程來取代進程&#xff0c;這樣服務處理程序可以較快的創建。據統計&a…

計算機網絡【三】物理層數據通信

物理層傳輸媒介 導向傳輸媒體&#xff0c;比如光纖和銅線 雙絞線&#xff08;屏蔽雙絞線STP 五屏蔽雙絞線UTP&#xff09;電線扭曲在一起可以降低互相之間的電磁干擾 同軸電纜 (50歐姆的基帶同軸電纜&#xff0c;75歐姆的寬帶同軸電纜) 10M和100M網絡只使用了四根線&#xf…