下面幾節,將分別溫習下linux進程進通信的幾種機制
1:管道
管道是比較古老的進程間的通信方式。主要有有名管道和無名管道兩種。
2:無名管道
它的特點就是:
1:只能使用在具有親緣關係的進程之間的通信(父子進程或者兄弟進程之間)。因爲只有具有親緣關係的進程才能繼承其創建的文件描述符。
2:是一個半雙工的通信模式,具有固定的讀端和寫段。
3:可以將管道看着特殊的文件,也可以調用read()和write()操作。
管道是基於文件描述符的通信方式,當一個管道建立時,它會創建兩個文件描述符fd[0]和fd[1],其中fd[0]是固定用於讀管道, 而fd[1]是固定用於寫管道,構成一個半雙工通道。
當一個管道共享多對文件描述符時,若將其中的一對讀寫文件描述符都刪除,則該管道將失效。
上面插圖來自《linux應用程序開發標準教程》中,從圖中可以看到當fork()後 , 子進程會繼承父進程創建的管道,這樣父子進程都擁有了自己的讀寫通道, 從而實現了父子之間的讀寫,只需要把無關的讀端或寫段的文件描述符關閉即可。
例如上圖中,父進程負責讀段,則把它的寫段fd[1]關閉, 子進程負責寫,則把它的讀段fd[0]關掉。這樣就建立起來一條“子進程寫父進程讀”的通道。
下面來看一下無名管道的實例,直接上代碼:
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#define MAX_DATA_LEN 128
#define DELAY_TIME 1
int main()
{
pid_t pid;
int pipe_fd[2];
char buf[MAX_DATA_LEN] = {0};
const char data[] ="Pipe Test Program";
int real_read, real_write;
if( pipe(pipe_fd) < 0)
{
printf("Pipe create error\n");
exit(1);
}
if((pid = fork()) == 0)
{
/* clode write fd */
close(pipe_fd[1]);
sleep(DELAY_TIME *3);
if ((real_read = read(pipe_fd[0], buf, MAX_DATA_LEN)) > 0)
{
printf("Read data from pipe is %s\n", buf);
}
close(pipe_fd[0]);
exit(0);
} else if (pid > 0)
{
/*close read fd*/
close(pipe_fd[0]);
sleep(DELAY_TIME);
if ((real_write = write(pipe_fd[1], data, strlen(data))) != -1)
{
printf("Write to pipe is: %s\n", data);
}
{
/* clode write fd */
close(pipe_fd[1]);
sleep(DELAY_TIME *3);
if ((real_read = read(pipe_fd[0], buf, MAX_DATA_LEN)) > 0)
{
printf("Read data from pipe is %s\n", buf);
}
close(pipe_fd[0]);
exit(0);
} else if (pid > 0)
{
/*close read fd*/
close(pipe_fd[0]);
sleep(DELAY_TIME);
if ((real_write = write(pipe_fd[1], data, strlen(data))) != -1)
{
printf("Write to pipe is: %s\n", data);
}
close(pipe_fd[1]);
waitpid(pid, NULL, 0);
exit(0);
}
return 0;
}
運行結果:
Write to pipe is: Pipe Test Program
Read data from pipe is Pipe Test Program
3:有名管道
有名管道突破了無名管道的限制,可以使兩個不相關的進程實現彼此之間的通信。相當於創建一個文件,建立管道之後,兩個進程可以把它當做普通文件一樣進行讀寫操作。嚴格遵循先進先出規則,對管道及FIFO的讀寫總是從開始出返回數據,對它們的寫則把數據添加到末尾,需要指出的是不支持lseek()等文件定位操作。
可以採用mkdfifo()函數來創建一個命名管道,創建成功後就可以使用open, read, write函數進行操作。
mkfifo函數原型:
int mkfifo(const char *filename, mode_t mode)
傳參:
filename爲要創建的管道,包括路徑名
mode只創建的管道權限,一般有O_RDONLY(讀管道), O_WRONLY(寫管道),O_RDWR(讀寫管道),O_NONBLOCK:非堵塞管道。 O_CREAT(如果文件不存在,則新建文件),O_EXCL:可以測試文件是否存在。
下面來看一個有名管道的例子,爲了方便看代碼,直接將命名管道應用在父子進程間通信:
這#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#define MYFIFO "myfifo"
#define MAX_DATA_LEN 128
#define DELAY_TIME 1
int main()
{
pid_t pid;
int pipe_fd[2];
char buf[MAX_DATA_LEN] = {0};
const char data[] ="Pipe Test Program";
int real_read, real_write;
int fd = 0;
// memset((void *)buf, 0, sizeof(buf));
if(access(MYFIFO, F_OK) == -1)
{
if ((mkfifo(MYFIFO, 0666) < 0) )
printf("Can not create fifo file\n");
exit(1);
}
}
if((pid = fork()) == 0)
{
/* open write fifo */
fd= open(MYFIFO,O_WRONLY);
if ( fd == -1)
{
printf("Open write file error\n");
exit(1);
}
if(write(fd, data, strlen(data)) > 0)
{
printf("Write %s to FIFO\n", data);
}
close(fd);
exit(0);
} else if (pid > 0)
{
/*open write fifo */
fd = open(MYFIFO, O_RDONLY);
if (fd == -1)
{
printf("Open fifo error\n");
exit(1);
}
if(read(fd, buf, MAX_DATA_LEN) > 0)
{
printf("Read %s from FIFO\n", buf);
}
close(fd);
exit(0);
}
return 0;
}
運行結果:
Write Pipe Test Program to FIFO
Read Pipe Test Program from FIFO
下一節將溫習信號的使用