說到Linux嘛
我個人是都沒有用過,從以前到現在都是在windows的懷抱
不過最近發現有個東西叫cygwin,可以讓我在windows上做一些事情,比如說跑linux的一些指令
常常聽到人家說學linux對未來很有幫助嘛,我就來學些基本的觀念吧
process間的溝通方式,俗稱IPC(Inter-process communication),顧名思義就是讓process間能夠互相溝通的方式
除了這篇會講到的方式之外,其實還有一些其他的,比如說共用memory之類的,但就先不提了
這個想當然對於程式設計是非常重要的一環(應該吧)
再次強調,其實網路上很多資料啦,我這邊只是記錄一下而已
而且很多圖和範例都是從網路上找的,文末會附上來源和參考資料
那就開始吧
1. pipe (匿名管道)
一般稱匿名管道,這個應該是最簡易最古老的方式
1. 是屬於半雙工,也就是說讀和寫的端口是固定的,表示資料流的方向也是固定的
2. 僅限於具有親屬關係的process(parent process)間使用,至於什麼是parent process呢,比如說B process是從A process裡切(fork)出來的,那麼B就是A的子process
3. 基本上可以視為file的讀寫,可以用write、read等等操作,只是它不屬於任何文件系統,但存在在memory裡面
長成這個樣子:
#include <unistd.h>
int pipe(int fd[2]); // 返回值:若成功返回0,失败返回-1
|
fd[0]是讀的端口,fd[1]是寫的端口
嗯... 我們直接看例子吧
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#include<stdio.h> #include<unistd.h> int main() { int fd[2]; // 两个文件描述符 pid_t pid; char buff[20]; if(pipe(fd) < 0) // 创建管道 printf("Create Pipe Error!\n"); if((pid = fork()) < 0) // 创建子进程 printf("Fork Error!\n"); else if(pid > 0) // 父进程 { close(fd[0]); // 关闭读端 write(fd[1], "hello world\n", 12); } else { close(fd[1]); // 关闭写端 read(fd[0], buff, 20); printf("%s", buff); } return 0; } |
看著很神奇吧,fork()就是切出子process的意思
fork()的時候會複製出一個和現在process(父)一樣的process(子),所以子process也會有一個fd[2]。
父子process就是利用他們各自的fd[2]來從在記憶體中的pipe端口讀寫資料,藉此達到process間溝通的效果
畫成圖的話大概是這樣:
搭配圖就很清楚了,應該... 不難吧XD
2. FIFO (命名管道)
同樣是管道,只是和pipe不同的地方是他是一種文件類型
1. 可以在無關的process間進行溝通
2. 是屬於文件系統的一部分(特殊文件),其路徑名稱就是用來識別用的
簡單說可以把它當成一個讓process間互相共用的文件,只是這個文件是內核提供來給process溝通用的,你從外面看就只看的到名稱而已
大概長這樣:
1 2 3 |
#include <sys/stat.h> // 返回值:成功返回0,出错返回-1 int mkfifo(const char *pathname, mode_t mode); |
前面那個參數是路徑名稱,後面是模式,其實這邊的模式是權限的意思
可以參考這邊的解說,也就是文件的權限:
http://qa.geeksforgeeks.org/4449/qa.geeksforgeeks.org/4449/meaning-of-0666-in-permission.html
FIFO還有幾個特點,pipe在開啟讀寫端口時是用pipe()的函式(fd),但是FIFO是用open()去打開的
此外在open時還可以設定是否為nonblock模式,注意預設是block
如果是在讀的情況下設定為block,就會卡在這直到有另一個process寫資料進去為止,否則就會直接回傳成功
在寫的情況下設定為block,就會卡在這邊直到有另一個process要從這個FIFO讀資料為止,否則就會直接回傳失敗(ENXIO)
接下來舉個例子:
write_fifo.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
#include<stdio.h> #include<stdlib.h> // exit #include<fcntl.h> // O_WRONLY #include<sys/stat.h> #include<time.h> // time int main() { int fd; int n, i; char buf[1024]; time_t tp; printf("I am %d process.\n", getpid()); // 说明进程ID if((fd = open("fifo1", O_WRONLY)) < 0) // 以写打开一个FIFO { perror("Open FIFO Failed"); exit(1); } for(i=0; i<10; ++i) { time(&tp); // 取系统当前时间 n=sprintf(buf,"Process %d's time is %s",getpid(),ctime(&tp)); printf("Send message: %s", buf); // 打印 if(write(fd, buf, n+1) < 0) // 写入到FIFO中 { perror("Write FIFO Failed"); close(fd); exit(1); } sleep(1); // 休眠1秒 } close(fd); // 关闭FIFO文件 return 0; } |
read_fifo.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
#include<stdio.h> #include<stdlib.h> #include<errno.h> #include<fcntl.h> #include<sys/stat.h> int main() { int fd; int len; char buf[1024]; if(mkfifo("fifo1", 0666) < 0 && errno!=EEXIST) // 创建FIFO管道 perror("Create FIFO Failed"); if((fd = open("fifo1", O_RDONLY)) < 0) // 以读打开FIFO { perror("Open FIFO Failed"); exit(1); } while((len = read(fd, buf, 1024)) > 0) // 如果fd(也就是FIFO這個管道)裡面有東西,就读取FIFO管道 printf("Read message: %s", buf); close(fd); // 关闭FIFO文件 return 0; } |
這個例子就是一個寫一個讀,如果你開兩個終端機然後各自用gcc編完之後執行,就會看到精美的結果
然後話說回來,read()和open()那些function都在 unistd.h 裡面,要記得include
其實這和server跟client的觀念有點像,server(read_fifo.c)開出一個FIFO管道並決定它的名稱,client(write_fifo.c)必需要知道FIFO的名稱才能傳資料進去
而server也一直監看這個管道,一有資料來就立刻讀取這樣。
好啦大概是這樣,我可能回去做個小實驗再來補充一點東西
之後還有很多IPC的方式再開幾篇記錄吧~
學習了~
參考資料:
留言列表