linux学习之十八---管道pipe&dup&dup2
1.管道的用法:进程在使用fork函数创建子进程前先创建一个管道,该管道用于在父子进程间通信,然后创建子进程,之后父进程关闭管道的读端,子进程关闭管道的写端。父进程负责向管道写数据而子进程负责读数据。也可以父进程关闭管道的写端,子进程关闭管道的读端。这样管道就可以用于父子进程间的通信,也可以用于兄弟进程间的通信。
进程在使用fork函数创建子进程前先创建一个管道,该管道用于在父子进程间通信,然后创建子进程,之后父进程关闭管道的读端,子进程关闭管道的写端。父进程负责向管道写数据而子进程负责读数据。也可以父进程关闭管道的写端,子进程关闭管道的读端。这样管道就可以用于父子进程间的通信,也可以用于兄弟进程间的通信。
linux下创建管道可以通过函数pipe来完成。该函数如果调用成功返回0,并且数组中将包含两个新的文件描述符;如果有错误发生,返回-1。
#include<unistd.h>
int pipe(int fd[2])
管道两端可分别用描述符fd[0]以及fd[1]来描述。一个端只能用于读,由描述符fd[0]表示,称其为管道读端;另一端只能用于写,由描述符fd[1]来表示,称其为管道写端。如果试图从管道写端读数据或者从管道读端写数据,都将导致出错。
示例代码1:
一个管道实现半双工通信
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<unistd.h>
/*读管道*/
void read_from_pipe(int fd)
{
char message[100];
read(fd,message,100);
printf("read from pipe:%s",message);
}
/*写管道*/
void write_to_pipe(int fd)
{
char *message="Hello,pipe!\n";
write(fd,message,strlen(message)+1);
}
int main()
{
int fd[2];
pid_t pid;
int stat_val;
if(pipe(fd))
{
printf("create pipe failed!\n");
exit(1);
}
pid=fork();
switch(pid)
{
case -1:
printf("fork error!\n");
exit(1);
case 0:
//子进程
close(fd[1]);//关闭子进程写端
read_from_pipe(fd[0]);//子进程读
exit(0);
default:
//父进程
close(fd[0]);//关闭父进程读端
write_to_pipe(fd[1]);//父进程写
wait(&stat_val);//等待子进程
exit(0);
}
return 0;
}
运行结果:
pc@ubuntu:~/linux_lan/pipe$ ./pipe
read from pipe:Hello,pipe!
示例代码2:
两个管道实现全双工:
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<unistd.h>
#include<string.h>
//子进程读写管道的函数
void child_rw_pipe(int readfd,int writefd)
{
char *message1="from child process!\n";
write(writefd,message1,strlen(message1)+1);
char message2[100];
read(readfd,message2,100);
printf("child process read from pipe:%s",message2);
}
//父进程读写管道的函数
void parent_rw_pipe(int readfd,int writefd)
{
char *message1="from parent process!\n";
write(writefd,message1,strlen(message1)+1);
char message2[100];
read(readfd,message2,100);
printf("parent process read pipe:%s",message2);
}
int main()
{
int pipe1[2],pipe2[2];
pid_t pid;
int stat_val;
printf("realize full-duplex communication:\n\n");
if(pipe(pipe1))//创建管道1
{
printf("pipe1 failed\n");
exit(1);
}
if(pipe(pipe2))//创建管道2
{
printf("pipe2 failed\n");
exit(1);
}
pid=fork();//创建进程
switch(pid)
{
case -1:
printf("fork error!\n");
exit(1);
//子进程
case 0:
close(pipe1[1]);//子进程管道1关闭写端
close(pipe2[0]);//子进程管道2关闭读端
child_rw_pipe(pipe1[0],pipe2[1]);//子进程管道1控制读端,管道2控制写端
exit(0);
default:
close(pipe1[0]);//父进程管道1关闭读端
close(pipe2[1]);//父进程管道2关闭写端
parent_rw_pipe(pipe2[0],pipe1[1]);//父进程管道1控制写端,管道2控制读端
wait(&stat_val);
exit(0);
}
return 0;
}
运行结果:
pc@ubuntu:~/linux_lan/pipe$ ./dual_pipe
realize full-duplex communication:
parent process read pipe:from child process!
child process read from pipe:from parent process!
二、dup&dup2
1.文件描述符:
内核(kernel)利用文件描述符(file descriptor)来访问文件。文件描述符是非负整数。打开现存文件或新建文件时,内核会返回一个文件描述符。读写文件也需要使用文件描述符来指定待读写的文件。
标准输入(standard input)的文件描述符是 0,标准输出(standard output)是 1,标准错误(standard error)是 2。尽管这种习惯并非Unix内核的特性,但是因为一些 shell 和很多应用程序都使用这种习惯,因此,如果内核不遵循这种习惯的话,很多应用程序将不能使用。
POSIX 定义了 STDIN_FILENO、STDOUT_FILENO 和 STDERR_FILENO 来代替 0、1、2。这三个符号常量的定义位于头文件 unistd.h。
进程获取文件描述符最常见的方法是通过本机子例程open或create获取或者通过从父进程继承。后一种方法允许子进程同样能够访问由父进程使用的文件。文件描述符对于每个进程一般是唯一的。当用fork子例程创建某个子进程时,该子进程会获得其父进程所有文件描述符的副本,这些文件描述符在执行fork时打开。在由fcntl、dup和dup2子例程复制或拷贝某个进程时,会发生同样的复制过程。
dup dup2
#include<unistd.h>
int dup(int oldfd);
int dup2(int oldfd,int newfd);
dup和dup2函数调用成功时返回一个oldfd文件描述符的副本,失败则返回-1。所不同的是,由dup函数返回的文件描述符是当前可用文件描述符中最小数值,而dup2函数则可以利用参数newfd指定欲返回的文件描述符。如果参数newfd指定的文件描述符已经打开,系统先将其关闭,然后将oldfd指定的文件描述符赋值到该参数。如果newfd等于oldfd,则dup2返回newfd,而不是关闭它。
/dup程序片段*/
pid=fork();
if(pid==0)
{
/*关闭子进程的标准输出*/
close(1);
//复制管道输入端到标准输出
dup(fd[1])
execve("exam",argv,environ);
}
/*dup2程序片段*/
pid=fork();
if(pid==0)
{
/*关闭标准输出并复制管道输入端到标准输出*/
dup2(1,fd[1]);
execve("exam",argv,environ);
}
可见,dup2系统调用将close操作和文件描述符拷贝操作集成在同一个函数里,而且保证操作具有原子性。
2.示例代码:
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
int fd, save_fd;
char msg[] = "This is a test of dup() & dup2()\n";
int test;
fd = open("zhonghe.txt", O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
if(fd<0) {
perror("open");
exit(1);
}
save_fd = dup(STDOUT_FILENO); //运行后save_fd指向STDOUT_FILENO,即save_fd指向标准输出
printf("save_fd=%d\n",save_fd); //测试用
test=dup2(fd, STDOUT_FILENO); //运行后STDOUT_FILENO指向fd所指向的文件,即STDOUT_FILENO指向zhonghe.txt
printf("dup2_1=%d\n",test); //测试用 此时的标准输出不再指向显示器,因此该段测试将写入zhonghe.txt文件中
close(fd);
write(STDOUT_FILENO, msg, strlen(msg)); //此时STDOUT_FILENO所指向的是zhonghe.txt文件不再是标准输出流,因此该段将
//写入zhonghe.txt文件中
test=dup2(save_fd, STDOUT_FILENO); //运行后STDOUT_FILENO指向save_fd所指向的文件,即标准输出流
printf("dup2_2=%d\n",test); //测试用 此时标准输出流重新指回显示器,因此该段测试将写入显示器
write(STDOUT_FILENO, msg, strlen(msg)); //此时STDOUT_FILENO所指向的便回标准输出流该段将写入显示器
close(save_fd);
return 0;
}
运行结果:
pc@ubuntu:~/linux_lan/pipe$ ./test_zh
save_fd=4
dup2_2=1
This is a test of dup() & dup2()
并且在目录下创建名为zhonghe.txt文件,内容为:
dup2_1=1
This is a test of dup() & dup2()
PS:部分代码来自互联网。
更多推荐
所有评论(0)