Linux 进程及进程之间的通信机制——管道
參考: LInux C編程從初學到精通 電子工業出版社
Linux 進程
Linux 進程簡介
Linux是一個多用戶多任務的操作系統,多用戶是指多個用戶可以在同一時間使用同一臺計算機系統;多用戶是指Linux可以同時執行幾個任務,它可以在還未執行完一個任務時又執行另一個任務,操作系統管理著多個用戶的請求和多個任務。
Linux系統中所有運行的東西都可以稱之為一個進程。每個用戶任務、每個系統管理,都可以稱之為進程,Linux用分時管理方法使所有的任務共同分享系統資源。
歸結起來,進程具有以下4個要素:
- 要有一段程序供該進程運行。
- 進程專用的系統堆棧空間。
- 進程控制塊(PCB),在Linux中的具體實現是task_struct結構。
- 有獨立的存儲空間。
Linux操作系統包括三種不同類型的進程,每種進程都有自己的特點和屬性:
- 交互進程:由一個shell啟動的進程。交互進程既可以在前臺運行,也可以在后臺運行。
- 批處理進程:這種進程和終端沒有聯系,是一個進程序列
- 監控進程(守護進程):Linux系統啟動時啟動的進程,并在后臺運行。
進程相關的主要函數
fork() 創建一個子進程,若成功,父進程中返回子進程id,子進程中返回0;若出錯則返回-1;
#include<sys/types.h>
#include<unistd.h>
pid_t fork(void);
需要注意:
- fork()系統調用的作用是復制一個進程。當一個進程調用它,完成后就會出現兩個幾乎一摸一樣的進程。
- fork()函數被調用一次,但是返回兩次。在子進程中返回的是0,在父進程中返回的是子進程id。
getpid() 返回進程的進程id
#include<sys/types.h>
#include<unistd.h>
pid_t getpid(void);
exec函數族
#include<unistd.h>
int execl(const char*pathname, const char *arg, ...);
int execlp(const char*filename, const char *arg, ...);
int execle(const char*pathname, const char*arg, ..., char *const envp[]);
int execv(const char*pathname, char*const argv[]);
int execvp(const char*filename, char*const argv[]);
int execve(const char*pathname, char*const argv[], char*cons envp[]);
6 個函數若成功則無返回值,若出錯返回-1;
需要注意:
- 函數名中有字母 ‘l’ 的,其參數個數不定,參數由所調用程序的命令行參數列表組成,最后一個NULL表示結束。
- 函數名中有字母 ‘v’ 的,則是使用一個字符串數組指針argv指向參數列表,這一字符串數組和含有字母 ‘l’ 的函數中的參數列表完全相同,也同樣以NULL結束。
- 函數名中有字母 ‘p’ 的,可以自動在環境變量PATH指定的路徑中搜索要執行的程序,因此它的第一個參數為filename,表示可執行程序的文件名。而其他函數則需要用戶在參數中指定該程序,所以其第一個參數為pathname。
- 函數名中含有 ’e’ 的,比其他函數多含有一個參數envp,這是一個字符串數組指針,用于指定環境變量。調用這樣的函數(execle和execve)時,可以由用戶自行設定子進程的環境變量,存放在參數的envp所指向的字符串數組中。這個字符串數組也必須由NULL結束。其他函數則是接受當前的環境變量。
簡單示例:
#include<stdio.h>
#include<unistd.h>int main(){//char *envp[]={"THIS=test",NULL};//char *argv[]={"echo","this is a test",NULL};//char *argve[]={"env",NULL};//execl("/usr/bin/ls","ls","-a",NULL);//execlp("echo","echo","this is a test",NULL); //execle("/usr/bin/env","env",NULL,envp);//execv("/bin/echo",argv);//execvp("echo",argv);//execve("/usr/bin/env",argve,envp);return 0;
}
普遍情況下,如果一個進程想要執行另一個程序,它可以fork一個新進程,然后調用任何一個exec(使用system函數更簡單)。
**exit(int) 終止一個進程 **
#include<stdlib.h>
void exit(int status);
在一個進程調用了exit之后,該進程并非馬上就消失掉,而是留下一個僵尸進程的數據結構。
wait和waitpid函數:進程等待的系統調用
#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
兩函數:若成功則返回進程id,若出錯則返回-1;
對于waitpid如果設置了WNOHANG選項,返回0表示沒有已退出的子進程可收集。
需要注意:
- 系統一旦調用了wait,就立即阻塞自己,由wait自動分析是否當前進程中的某個子進程已經退出,如果讓它找到了這樣一個已經變成僵尸的子進程,wait就會手機這個子進程的信息,并把它徹底銷毀后返回,如果沒有找到這樣一個子進程,wait就會一直阻塞在這里。
- 從本質上講waitpid和wait的作用是完全相同的,但waitpid多了兩個可由用戶控制的參數pid和options,從而為用戶變成提供了一種更為靈活的方式。
pid取值及其含義:
options選項:
WNOHANG:即使沒有子進程退出,它也會立即返回
WUNTRRACED:極少用到
進程之間的通信
管道
Linux環境下使用pipe函數創建一個匿名管道,其函數原型如下:
#include<unistd.h>
int pipe(int fd[2]);
參數fd[2]是一個長度為2的文件描述符數組,fd[0]是讀出端的文件描述符,fd[1]是寫入端的文件描述符。
管道的讀寫
可以使用read和write函數對管道進行讀寫操作,需要注意的是,管道的兩端是固定了任務的,即管道的讀寫端智能用于讀取數據,管道的寫入端則只能用于寫入數據。如果試圖從管道寫端讀取數據,或者和向管道讀端寫入數據都將導致錯誤發生。
父子進程之間通信:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>int main(){ int fd[2];char inbuf[256]="this is a test!\n";char outbuf[256]={0};pid_t pid;int len;if(pipe(fd)<0){printf("create the pipe failed!\n");exit(1);}if((pid=fork())<0){printf("fork failed!\n");exit(1);}else if(pid>0){close(fd[0]);write(fd[1],inbuf,16);exit(0);}else{close(fd[1]);len=read(fd[0],outbuf,256);if(len<0){printf("read pipe failed\n");exit(1);}printf("%s",outbuf);}return 0;
}
兄弟進程之間通信
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>int main(){int fd[2];char inbuf[256]="this is a test!\n";char outbuf[256]={0};int len;pid_t pid;if(pipe(fd)<0){printf("create pipe failed!\n");exit(1);}if((pid=fork())<0){printf("fork failed!\n");exit(1);}else if(pid==0){close(fd[0]);write(fd[1],inbuf,16);exit(0);}if((pid=fork())<0){printf("fork failed!\n");exit(1);}else if(pid==0){close(fd[1]);len=read(fd[0],outbuf,256);printf("%s",outbuf);}close(fd[0]);close(fd[1]);return 0;
}
總結
以上是生活随笔為你收集整理的Linux 进程及进程之间的通信机制——管道的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Matlab 频域滤波处理周期噪声图像(
- 下一篇: C语言实现RSA