管道:
匿名管道(必须是亲缘进程)
单工管道:
FIFE *popen (const char * command ,const char *type );
popen() 函数通过创建一个管道,调用 fork 产生一个子进程,执行一个 shell 来开启一个进程。这个进程必须由 pclose() 函数关闭,popen()和pclose()配套使用。
type参数只能是读或者写中的一种,得到的返回值(标准 I/O 流)也具有和 type相应的只读或只写类型。
返回值:如果调用 fork() 或 pipe() 失败,或者不能分配内存将返回NULL,否则返回标准 I/O 流
特点:能够方便使用系统自带的功能,可以执行比较复杂的shell,但是默认启动两个进程,效率比较低。
例子:
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main( void )
{
FILE *stream;
FILE *wstream;
char buf[1024];
memset( buf, '/0', sizeof(buf) );//初始化buf
stream = popen( "ls -l", "r" ); //将“ls -l”命令的输出 通过管道读取(“r”参数)到FILE* stream
wstream = fopen( "test_popen.txt", "w+"); //新建一个可写的文件
fread( buf, sizeof(char), sizeof(buf), stream); //将刚刚FILE* stream的数据流读取到buf中
fwrite( buf, 1, sizeof(buf), wstream );//将buf中的数据写到FILE *wstream对应的流中,也是写到文件中
pclose( stream );
fclose( wstream );
return 0;
}
半双工管道:
int pipe(int fd[2]);
返回值: 若成功则返回零,否则返回-1。
fd[0]为管道里的读取端
fd[1]则为管道的写入端
简记:零下一度反过来
在打开读入端和写入端后在程序结束之前关闭两个端口。
注:当管道为空时,读进程阻塞,如果管道为满时,写进程阻塞。
例子:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
int fd[2];
pipe(fd);
if(!fork()){// child
char in[] = "Hello pipe";
sleep(3);
write(fd[1],in,sizeof(in));
printf("child %d write:%s\n",getpid(),in);
}else{// parent
char out[BUFSIZ]={0};
ssize_t n = read(fd[0],out,sizeof(out));
if(-1 == n){
perror("read error");
return -1;
}
printf("parent %d read:%s\n",getpid(),out);
}
close(fd[0]);
close(fd[1]);
}
文件描述符和文件流指针的区别:
dup函数:
int dup(int oldfd);
dup函数用来复制一个描述符。传给该函数一个描述符,它就会返回一个新的描述符,这个新的描述符是传给它的描述符的拷贝如:fd2=dup(fd1)。
dup2函数:
dup2(int oldfd,int newfd);
dup2函数也是复制描述符,但dup2函数允许调用者规定一个有效描述符和目标描述符的id。dup2函数成功返回时,目标描述符(dup2函数的第二个参数)将变成源描述符(dup2函数的第一个参数)的复制品。若newfd已经被程序使用,系统将其关闭释放该文件描述符。
注意:dup2函数在复制了oldfd之后,会立即将其关闭。
例子:
原文件test内容为:hhhhhhhhhhhh
int main()
{
int oldfd;
oldfd = open("test",O_RDWR|O_CREAT,0644);
dup2(oldfd,1); //复制oldfd到文件描述符1(stdout标准输出)
close(oldfd); //关闭文件描述符oldfd
printf("ggg"); //在标准输出上打印出ggg,这时由于标准输出已经被oldfd文件描述符代替
return 0; //打印到标准输出上的内容就全部打印到了文件test中
}
执行完成后结果test内容变为:ggghhhhhhhhh
FIFO管道(命名管道):
int mkfifo(const char * pathname,mode_t mode):
mkfifo作用是在文件系统中创建一个文件,此文件必须不存在,该文件用于提供FIFO功能,即命名管道,命名管道是一个可见的文件,它可以用于任何两个进程之间的通信,不管这两个进程是不是父子进程,也不管这两个进程之间有没有关系。
参数:第一个参数(pathname)是将要在文件系统中创建的一个专用文件。第二个参数(mode)用来规定FIFO的读写权限。Mkfifo函数如果调用成功的话,返回值为0;如果调用失败返回值为-1
管道创建例子mkfifo("/tmp/test",0644)):在/tmp下创建文件名为test的FIFO文件,权限为0644。
写入命名管道:
int main(){
int fd = open("/tmp/test",O_WRONLY|O_NONBLOCK);
if(-1 == fd){
perror("open error");
return 1;
}
char str[] = "Hello fifo";
write(fd,str,sizeof(str));
printf("write:%s\n",str);
}
读取与写入类似。
注:1.fifowrite与fiforead要同时执行,才会数据传递。否则会造成阻塞。
1. fcntl(fd,F_SETFL,O_NONBLOCK)不能控制命名管道读写阻塞问题。必须在open命名管道时,指定非阻塞打开。