close

說到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的方式再開幾篇記錄吧~

學習了~

 

參考資料:

https://songlee24.github.io/2015/04/21/linux-IPC/

https://blog.csdn.net/jnu_simba/article/details/8953960

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 頁頁頁六滴 的頭像
    頁頁頁六滴

    人森很精彩,所以要把所有事情都記起來=ˇ=

    頁頁頁六滴 發表在 痞客邦 留言(1) 人氣()