日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux进程编程(PS: exec族函数、system、popen函数)

發(fā)布時間:2023/12/10 linux 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux进程编程(PS: exec族函数、system、popen函数) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

目錄

  • 1.進程相關(guān)概念
    • 程序和進程
    • 查看系統(tǒng)中的進程
      • ps指令
      • top指令
    • 進程標識符 使用getpid()獲取
    • 父進程,子進程
  • 2.創(chuàng)建進程fork
    • 進程創(chuàng)建發(fā)生了什么——C程序的存儲空間如何分配
  • 3.創(chuàng)建進程vfork(區(qū)別fork)
  • 4.進程退出
    • 正常退出
    • 異常退出
  • 5.父進程等待子進程退出
    • 父進程收集子進程退出狀態(tài)(僵尸進程)
    • 等待退出函數(shù)wait()
    • 等待退出函數(shù)waitpid()
    • 父進程先于子進程退出(孤兒進程)
  • 6.exec族函數(shù)(讓子進程調(diào)用其他程序)
    • execl,execlp
    • execv execvp
    • exec配合fork使用
    • system函數(shù)
    • popen函數(shù)

1.進程相關(guān)概念

程序和進程

程序是靜態(tài)的概念,gcc xx.x -o pro,磁盤中生成的pro就是程序。

進程是程序的一次運行活動,通俗的講就是程序跑起來了,系統(tǒng)中就多了一個進程。

查看系統(tǒng)中的進程

ps指令

查看系統(tǒng)中所有進程

ps -aux

結(jié)果:

查看系統(tǒng)中的init進程

ps -aux | grep init

結(jié)果:

即把ps -aux指令所有輸出的結(jié)果通過管道導(dǎo)向grep進行搜索,查找init關(guān)鍵字的文本

-aux 顯示所有包含其他使用者的進程
|管道符號
grep 用于查找文件里符合條件的字符串

top指令

類似windows的任務(wù)管理器,數(shù)據(jù)也是實時動態(tài)變化的。

進程標識符 使用getpid()獲取

每一個進程都有一個非負整數(shù)標識唯一的ID,即為pid。

調(diào)用getpid()獲取自身進程標識符,getppid()獲取獲取父進程標識符

其中系統(tǒng)所占用的進程標識符如下:

pid進程名稱作用說明
pid = 0交換進程用于進程調(diào)度所有“同時”在運行的程序所占用的資源受到進程調(diào)度的影響
pid = 1init進程用于系統(tǒng)初始化程序運行,內(nèi)核加載完畢,文件系統(tǒng)起來的第一個進程就是init進程,讀取配置文件然后再啟動其他進程。(如ktv點歌機開機后看到的是點歌界面,而不是字符界面)
#include <stdio.h> #include <sys/types.h> #include <unistd.h>int main() {//pid_t getpid(void);pid_t pid;pid=getpid();printf("我的pid是:%d\n",pid);return 0; }

父進程,子進程

進程A創(chuàng)建了進程B,A就是父進程,B就是子進程

2.創(chuàng)建進程fork

pid_t fork(void);

fork函數(shù)調(diào)用成功,返回兩次:返回值為0,代表當(dāng)前進程是子進程,非負數(shù)為父進程,如果調(diào)用失敗則返回-1
創(chuàng)建子進程的目的:復(fù)制父進程(此時兩個或兩個以上進程),父進程等待客戶端服務(wù)請求,當(dāng)這種請求到達時,父進程調(diào)用fork,讓子進程去處理(QQ服務(wù)器 客戶端 結(jié)合Socket網(wǎng)絡(luò)編程)

#include <stdio.h> #include <sys/types.h> #include <unistd.h>int main() {pid_t pid;pid = getpid();fork();//此處開始,后面所有代碼執(zhí)行了兩次 //fork()有兩個返回值printf("my pid is %d\n",pid);return 0; }

返回兩次:

#include <stdio.h> #include <sys/types.h> #include <unistd.h>int main() {pid_t pid;pid = getpid();fork();printf("my pid is %d,current pro id:%d\n",pid,getpid());return 0; }

#include <stdio.h> #include <sys/types.h> #include <unistd.h>int main() {pid_t pid;pid = getpid();fork();if(pid == getpid()){printf("this is father print\n");}else{printf("this is child print,child pid = %d\n",getpid());}return 0; }

#include <stdio.h> #include <sys/types.h> #include <unistd.h>int main() {pid_t pid;printf("father: pid = %d\n",getpid());pid = fork();if(pid > 0){printf("this is father print, pid = %d\n",getpid());}else if(pid == 0){printf("this is child print,child pid = %d\n",getpid());}return 0; }

應(yīng)用場景(模擬網(wǎng)絡(luò)等待):

#include <stdio.h> #include <sys/types.h> #include <unistd.h>int main() {pid_t pid;int data;while(1){printf("please input a data\n");scanf("%d",&data);if(data == 1){pid = fork();if(pid > 0){}else if(pid == 0){while(1){printf("do net request,pid=%d\n",getpid());sleep(3);}}}else{printf("wait, do nothing\n");}}return 0; }

進程創(chuàng)建發(fā)生了什么——C程序的存儲空間如何分配

詳細參照博文:內(nèi)存四區(qū)(代碼區(qū) 靜態(tài)區(qū) 棧區(qū) 堆區(qū))

fork創(chuàng)建進程,所有的東西都進行了拷貝,包括全局變量、局部變量、代碼等,各進程內(nèi)改變變量的值互不影響。


詳細說明:

3.創(chuàng)建進程vfork(區(qū)別fork)

vfork與fork的區(qū)別:
1.(共用)vfork直接使用父進程的存儲空間,不拷貝(隨著內(nèi)核的發(fā)展,fork目前執(zhí)行的是寫時拷貝—copy on write 若子進程未改變原始變量的值時不會進行拷貝)
2.(等待)vfork保證子進程先運行,當(dāng)子進程調(diào)用exit退出后,父進程才執(zhí)行

#include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h>int main(){int cnt=0;pid_t pid;pid_t repid;pid=getpid();repid=vfork();if(repid > 0){while(1){printf("cnt=%d\n",cnt);printf("this is father pid,repid=%d\n",repid);sleep(1);}}else if(repid == 0){while(1){printf("this is child pid,repid=%d\n",repid);sleep(1);cnt++;if(cnt==2){exit(0);}}}return 0; }

運行結(jié)果:

4.進程退出

正常退出

1.main函數(shù)調(diào)用return

2.進程調(diào)用exit(),標準C庫

3.進程調(diào)用_exit()或者_Exit(),系統(tǒng)調(diào)用

4.進程最后一個線程返回

5.最后一個線程調(diào)用pthread_exit

異常退出

1.調(diào)用abort

2.當(dāng)進程收到某些信號時,如ctrl+c

3.最后一個線程對取消(cancellation)請求作出響應(yīng)

注意:不管進程如何終止,最后都會執(zhí)行內(nèi)核中的同一段代碼,這段代碼為相應(yīng)進程關(guān)閉所有打開描述符,釋放它所使用的存儲器等。

5.父進程等待子進程退出

父進程收集子進程退出狀態(tài)(僵尸進程)

父進程等待子進程退出并收集子進程退出狀態(tài)
子進程退出狀態(tài)不被收集,會變成僵尸進程

僵尸進程的例子:

#include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h>int main() {pid_t pid;int i;int cnt=0;pid=fork();if(pid > 0){//父進程while(1){printf("這是父進程,pid=%d\n",getpid());printf("cnt=%d\n",cnt);sleep(2);//防止刷屏} }else if(pid == 0){//子進程for(i=0;i<5;i++){//看看是不是保證子進程先運行,五次過后推出進入父進程printf("這是子進程,pid=%d,這是第%d次\n",getpid(),i+1);cnt++;sleep(1);//防止刷屏}exit(-1);}return 0; }

此時運行的結(jié)果是子進程退出了,但是退出狀態(tài)沒有被收集,子進程成了僵尸進程(zomb)。

等待退出函數(shù)wait()

wait(int *status): status參數(shù),他是一個整型數(shù)指針。 非空:子進程退出狀態(tài)放在它所指向的地址 空(NULL):不關(guān)心退出狀態(tài)

#include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <sys/wait.h>int main() {pid_t pid;int i;int cnt=0;int status;pid=fork();if(pid > 0){//父進程wait(&status);printf("子進程退出,status:%d\n",WEXITSTATUS(status));//WEXITSTATUS()是解析子進程退出碼的宏,下面有詳細介紹while(1){printf("這是父進程,pid=%d\n",getpid());printf("cnt=%d\n",cnt);sleep(2);//防止刷屏} }else if(pid == 0){//子進程for(i=0;i<5;i++){//看看是不是保證子進程先運行,五次過后推出進入父進程printf("這是子進程,pid=%d,這是第%d次\n",getpid(),i+1);cnt++;sleep(1);//防止刷屏}exit(3);//退出碼返回給父進程中的wait進行收集}return 0; }

解析出wait()對子進程退出碼回收的宏

等待退出函數(shù)waitpid()


來個例子:

#include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <sys/wait.h>int main() {pid_t pid;int i;int cnt=0;int status;pid=fork();if(pid > 0){//父進程waitpid(pid,&status,WNOHANG);//WNOHANG不阻塞printf("子進程退出,status:%d\n",WEXITSTATUS(status));while(1){printf("這是父進程,pid=%d\n",getpid());printf("cnt=%d\n",cnt);sleep(2);//防止刷屏}}else if(pid == 0){//子進程for(i=0;i<5;i++){//看看是不是保證子進程先運行,五次過后推出進入父進程printf("這是子進程,pid=%d,這是第%d次\n",getpid(),i+1);cnt++;sleep(1);//防止刷屏}exit(3);}return 0; }

結(jié)果如下所示,發(fā)現(xiàn)子進程也變?yōu)榻┦M程。

父進程先于子進程退出(孤兒進程)

父進程如果不等待子進程退出 ,在子進程之前就結(jié)束了自己的生命,此時的子進程就叫做是孤兒進程
Linux避免系統(tǒng)存在太多的孤兒進程,init進程收留孤兒進程,變成孤兒進程的父進程

例子:

#include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <sys/wait.h>int main() {pid_t pid;int i;int status;int cunt=0;pid=fork();if(pid > 0){printf("這是父進程,pid=%d\n",getpid());printf("cunt=%d\n",cunt);sleep(2);}else if(pid == 0){for(i=0;i<5;i++){printf("這是子進程,pid=%d,我的父進程的pid=%d\n",getpid(),getppid());cunt++;sleep(2);}exit(3);}return 0; }

運行結(jié)果:

最后再來一個綜合的例子:

6.exec族函數(shù)(讓子進程調(diào)用其他程序)

參照博文:https://blog.csdn.net/u014530704/article/details/73848573

execl,execlp

#include <unistd.h> #include <stdio.h> #include <stdlib.h>int main(){//int execl(const char *path, const char *arg, .../* (char *) NULL */); /* path:可執(zhí)行文件的路徑名字arg:可執(zhí)行程序所帶的參數(shù),第一個參數(shù)為可執(zhí)行文件名字,沒有帶路徑且arg必須以NULL結(jié)束file:如果參數(shù)file中包含/,則就將其視為路徑名,否則就按 PATH環(huán)境變量,在它所指定的各目錄中搜尋可執(zhí)行文件exec族函數(shù)參數(shù)極難記憶和分辨,函數(shù)名中的字符會給我們一些幫助:l : 使用參數(shù)列表p:使用文件名,并從PATH環(huán)境進行尋找可執(zhí)行文件v:應(yīng)先構(gòu)造一個指向各參數(shù)的指針數(shù)組,然后將該數(shù)組的地址作為這些函數(shù)的參數(shù)。e:多了envp[]數(shù)組,使用新的環(huán)境變量代替調(diào)用進程的環(huán)境變量exec函數(shù)族的函數(shù)執(zhí)行成功后不會返回,調(diào)用失敗時,會設(shè)置errno并返回-1,然后從原程序的調(diào)用點接著往下執(zhí)行。 */printf("before execl\n");if(execl("/bin/ls","ls",NULL,NULL) == -1)//通過whereis指令找到ls命令的位置{printf("execl failed!\n");perror("why");}printf("after execl\n");return 0; } int main(){//int execlp(const char *file, const char *arg, .../* (char *) NULL */);printf("before execl\n");if(execlp("ls","ls","-l",NULL) == -1)//不用找到命令的路徑{printf("execl failed!\n");perror("why");}printf("after execl\n");return 0; }

運行結(jié)果:

通過whereis指令查找ls、date(獲取系統(tǒng)時間)系統(tǒng)指令的位置:


execv execvp

int main(){//int execv(const char *path, char *const argv[]); char* argv[]={"ls",NULL,NULL};if(execv("/bin/ls",argv) == -1){printf("execl failed!\n");perror("why");}printf("after execl\n");return 0; } int main(){// int execvp(const char *file, char *const argv[]);char* argv[]={"ls","-l",NULL};if(execvp("ls",argv) == -1){printf("execl failed!\n");perror("why");}printf("after execl\n");return 0; }

exec配合fork使用

應(yīng)用場景:在執(zhí)行A程序的過程中讓子進程去執(zhí)行B程序

代碼B(用來修改配置文件) 編譯生成程序名為change

#include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> #include <sys/wait.h>int main() {int fd;int n_write;int n_read;pid_t pid;char *readBuf;fd=open("test15.txt",O_RDWR);int size=lseek(fd,0,SEEK_END);//計算文件的大小lseek(fd,0,SEEK_SET);//光標重新到開頭readBuf=(char *)malloc(sizeof(char)*size+8 );//+8防止溢出n_read=read(fd,readBuf,size);char *p=strstr(readBuf,"length=");if(p == NULL ){printf("沒有找到\n");exit(-1);}p=p+strlen("length=");//指針往后移動大哦想要改的地方*p='9';//將里面的6改稱9lseek(fd,0,SEEK_SET);n_write=write(fd,readBuf,strlen(readBuf));close(fd);exit(0); }

代碼A

#include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> #include <sys/wait.h>int main() {int fd;int n_write;int n_read;pid_t pid;int data;char *readBuf;while(1){printf("請輸入一個數(shù)\n");scanf("%d",&data);if(data == 1){printf("成功進入\n");pid=fork();if(pid > 0){//父進程wait(NULL);//防止子進程變成僵尸進程}if(pid == 0){//子進程execl("./change","change",NULL,NULL);//獲取系統(tǒng)時間也能實現(xiàn)}}else{printf("等待輸入正確的指令\n");}}return 0; }

初始的test15.txt

運行程序A后

system函數(shù)

本質(zhì)上是對execl函數(shù)的二次封裝,可查看其源碼。實際上比execl更好用
和execl區(qū)別:system執(zhí)行完該函數(shù)后,還會繼續(xù)執(zhí)行后面的函數(shù) ,而exec則不會。

#include <stdlib.h> int system(const char *command); system()函數(shù)返回值如下: 成功,則返回進程的狀態(tài)值 當(dāng)sh不執(zhí)行時,返回127//shell腳本 失敗返回 -1 #include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> #include <stdlib.h>int main(){pid_t pid;pid_t repid;int data;while(1){printf("please input a number:");scanf("%d",&data);if(data==1){repid=fork();//創(chuàng)建進程if(repid > 0){wait(NULL);}if(repid == 0){//execl("./changeNumToFile.out","changeNumToFile.c","changeNum.c",NULL);system("./changeNumToFile.out");//執(zhí)行完該函數(shù)后,還會執(zhí)行后面的函數(shù) exec就不會回來繼續(xù)執(zhí)行后面代碼了,除非execl出錯返回-1exit(0);}}else{printf("waiting!\n");}}return 0;

popen函數(shù)

比system在應(yīng)用中的好處:可以獲取運行的輸出結(jié)果

#include <stdio.h> FILE *popen(const char *command, const char *type); int pclose(FILE *stream); #include <stdio.h> #include <stdlib.h>// FILE *popen(const char *command, const char *type); // FILE *fopen(const char *path, const char *mode); // size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); int main(){// system("ls");FILE* fp;char ret[1024];fp=popen("ls -l","r");int n_read=fread(ret,1,1024,fp);if(n_read != -1){printf("read sucess!\n");printf("return n_read=%d,read data=%s\n",nread,ret);}else{printf("no read datas\n");}return 0; }

總結(jié)

以上是生活随笔為你收集整理的Linux进程编程(PS: exec族函数、system、popen函数)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。