無名管道簡介:
管道是半雙工的,數據只能向一個方向流動,需要雙方通信時,需要建立起兩個管道。管道只能用於父子進程或者兄弟進程之間(具有親緣關係的進程):管道對於管道兩端的進程而言,就是一個文件,但它不是普通的文件,並且只存在於內存中。管道的讀寫規則:fifo先進先出規則,寫入的內容每次都添加在管道緩衝區的末尾,並且每次都是從緩衝區的頭部讀出數據。
無名管道相關函數說明:
#include <unistd.h>
int pipe(int fd[2])
返回值:若成功,返回0;若出錯,返回-1。
函數傳入值 fd[2]:管道的兩個文件描述符,之後就可以直接操作這兩個文件描述符。
調用此函數之後,返回兩個文件描述符,fd[0]用於讀管道,fd[1]用於寫管道。
管道的運用:
通常先是創建一個管道,再通過 fork()函數創建一子進程,該子進程會繼承父進程所創建的管道。這個時候,子進程和父進程都有兩個文件描述符,fd[0]用於讀管道,fd[1]用於寫管道。我們想要讓其通信,只需要一個進程保留寫端,一個進程保留讀端。
示例程序:
/* pipe.c*/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main ()
{
pid_t pid;
int count=0;
int pipe_fd[2];
char read_pipe_buf[100];
char read_pipe_buf_last[100];
char write_pipe_buf[] = "hello";
int read_num;
//創建管道,pipe_fd[0]用於讀管道,pipe_fd[1]用於寫管道
if(pipe(pipe_fd)<0)
{
perror("pipe create error\n");
exit(-1);
}
if ((pid = fork()) < 0)
{
perror("fork");
exit(-1);
}
else if (pid == 0) //子進程
{
//關閉pipe_fd[1]寫管道,表明要讀取管道的數據
close(pipe_fd[1]);
sleep(1);
//子進程讀取管道內容
if((read_num=read(pipe_fd[0],read_pipe_buf,5))>0)
{
printf("this is child, %d numbers first read from pipe is %s\n",read_num,read_pipe_buf);
}
if((read_num=read(pipe_fd[0],read_pipe_buf_last,5))>0)
{
printf("this is child, %d numbers second read from pipe is %s\n",read_num,read_pipe_buf_last);
}
//關閉子進程讀描述符
close(pipe_fd[0]);
exit(0);
}
else //父進程
{
//關閉pipe_fd[0]讀管道,表明要向管道寫數據
close(pipe_fd[0]);
/*if(write(pipe_fd[1],write_pipe_buf,sizeof(write_pipe_buf)) == -1)
{
perror("write pipe");
exit(-1);
}*/
if(write(pipe_fd[1],"hello",5) == -1)
{
perror("write pipe");
exit(-1);
}
if(write(pipe_fd[1],"world",5) == -1)
{
perror("write pipe");
exit(-1);
}
//關閉父進程寫描述符
close(pipe_fd[1]);
//等待子進程退出
waitpid(pid,NULL,0);
exit(0);
}
return 0;
}
實驗結果:
ubuntu:~/test/process_test$ gcc pipe.c -o pipe
ubuntu:~/test/process_test$ ./pipe
this is child, 5 numbers first read from pipe is hello
this is child, 5 numbers second read from pipe is world
以上程序,先是調用pipe(pipe_fd),得到pipe_fd[0]用於讀管道,pipe_fd[1]用於寫管道。然後調用fork,創建出子進程,在子進程保留讀端,用於讀取管道的數據,在父進程保留寫端,用於寫管道,把字符串”hello”、”world”先後寫入管道,接着子進程分兩次讀取管道的內容,並把它打印出來。子進程先讀取的字符串是父進程先寫入的字符串”hello”,這也體現了管道數據fifo先進先出的原則。
有名管道:
無名管道只能用在親緣關係的進程之間,並且管道是存儲在內存中,進程一終止,數據就沒有了。而有名管道沒有這種限制,可以用在互不相關的兩個進程之間,文件也能保存在文件系統中。有名管道和無名管道都遵循先進先出規則。
有名管道函數說明:
#include <sys/state.h>
int mkfifo(const char *filename,mode_t mode)
返回值:若成功,返回文件流指針;若出錯,返回-1
參數:
- filename:要創建的管道
- mode:
- O_RDONLY:讀管道
- O_WRONLY:寫管道
- O_RDWR:讀寫管道
- O_NONBLOCK:非阻塞
- O_CREAT:如果該文件不存在,那麼就創建一個新的文件,併爲其設置權限
- O_EXCL:如果使用 O_CREAT 時文件存在,那麼可返回錯誤消息。這一參數可測試文件是否存在
可把有名管道的操作類似於對普通文件的操作,創建使用函數 mkfifo(),該函數類似普通文件中的 open()操作,然後在創建管道成功之後,就可以使用 open、read、write 這些函數了。
接下來創建兩個程序,一個用於寫管道,一個用於讀管道,這兩個程序是互不相關的。
寫管道示例程序:
/* write_fifo.c*/
#include <stdio.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FIFO "myfifo"
int main(int argc,char** argv)
{
/*創建有名管道*/
if(mkfifo(FIFO,O_CREAT|O_EXCL|777)<0)
perror("cannot create fifoserver\n");
/*打開管道*/
int fd=open(FIFO,O_RDWR|O_NONBLOCK,0);
if(fd==-1)
{
perror("open");
}
char write_buf[128];
int i = 0,write_num = 0;
while(1)
{
printf("enter something to the myfifo:");
scanf("%s",write_buf);
if (write_num = write(fd,write_buf,sizeof(write_buf)) < 0)
{
perror("write");
}
printf("This is a write fifo....write %s to the FIFO\n",write_buf);
}
return 0;
}
讀管道示例程序:
/* read_fifo.c*/
#include <stdio.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#define FIFO "myfifo"
int main(int argc,char** argv)
{
/*創建有名管道*/
if(mkfifo(FIFO,O_CREAT|O_EXCL|777)<0)
perror("cannot create fifoserver\n");
/*打開管道,默認阻塞模式*/
int fd = open(FIFO,O_RDONLY,0);
//int fd = open(FIFO,O_RDONLY|O_NONBLOCK,0);//非阻塞模式
if(fd == -1)
{
perror("open");
}
char read_buf[128];
int i = 0,read_num;
while(1)
{
memset(read_buf,0,sizeof(read_buf));
if (read_num = read(fd,read_buf,sizeof(read_buf)) < 0)
{
if(errno==EAGAIN)
printf("no data yet\n");
}
printf("This is a read fifo....read %s from FIFO\n",read_buf);
//sleep(2);
}
return 0;
}
我們在A終端運行寫管道的程序,在B終端運行讀管道的程序,根據A終端的提示,輸入任意字符串,然後在B終端看看是否有讀到相應的字符串。如果讀不到,B終端會一直阻塞,因爲我們程序設置了打開管道的默認屬性阻塞,我們也可以設置非阻塞模式。從下面的實驗結果,可以發現有名管道可以運用在沒有親緣關係的進程中。
//A終端
ubuntu:~/test/process_test$ gcc write_fifo.c -o write_fifo
ubuntu:~/test/process_test$ ./write_fifo
enter something to the myfifo:helloworld
This is a write fifo....write helloworld to the FIFO
enter something to the myfifo:
//B終端
ubuntu:~/test/process_test$ gcc read_fifo.c -o read_fifo
ubuntu:~/test/process_test$ ./read_fifo
cannot create fifoserver
: File exists
This is a read fifo....read helloworld from FIFO