linux的管道是在pipefs上实现的,pipefs实现见linux pipe文件系统(pipefs)
注:
pipe会在挂载到内核上的pipefs上创建虚拟管道文件(文件对象superblock,dentry,inode,file都是内核内存中);FIFO是在物理文件系统(ext4/ext3/nfs等)中创建管道文件,物理存储中有对应的文件(物理存储中存在文件inode)
I.原型
#include <unistd.h>
int pipe(int fildes[2]);
pipe用于创建管道,并将代表管道读端和写端的打开文件描述符分别放入fildes[0],fildes[1]输出参数中;fildes是进程调用pipe时最小的两个可用打开文件描述符;两个描述符的O_NONBLOCK和O_CLOEXEC标识会被清空,可在fcntl中置位。
写入到fildes[1]中的数据,可以从fildes[0]中读出;从fildes[0]中读出的数据顺序与写入fildes[1]数据顺序相同。
返回值为0表示,管道创建成功;返回-1表示,管道创建失败,errno中有失败原因。
下面只讨论pipe创建部分(pipe系统调用),pipe的read,write,poll,fcntl,ioctl见linux pipe文件系统(pipefs)
pipe主要就是创建管道及管道读写端文件,并将读写端文件的操作设置为相应的管道操作,就可以通过文件系统相关的系统调用(read,write,poll)来操作管道了。
II.pipe实现
i.pipe
创建管道并返回管道读写端文件描述符
/* fs/pipe.c */
1094 /*
1095 * sys_pipe() is the normal C calling standard for creating
1096 * a pipe. It's not the way Unix traditionally does this, though.
1097 */
1098 SYSCALL_DEFINE2(pipe2, int __user *, fildes, int, flags)
1099 {
1100 int fd[2];
1101 int error;
1102
1103 error = do_pipe_flags(fd, flags);
1104 if (!error) {
1105 if (copy_to_user(fildes, fd, sizeof(fd))) {
1106 sys_close(fd[0]);
1107 sys_close(fd[1]);
1108 error = -EFAULT;
1109 }
1110 }
1111 return error;
1112 }
1113
1114 SYSCALL_DEFINE1(pipe, int __user *, fildes)
1115 {
1116 return sys_pipe2(fildes, 0);
1117 }
1.pipe2可以带flags(O_NONBLOCK/O_CLOEXEC)标识创建管道;pipe是无标识的创建管道,可以调用pipe2实现
2.创建管道,如果成功将管道读写端对应的文件描述符复制到pipe的输出参数中。
3.如果失败返回错误,成功返回0
ii.do_pipe_flags
创建管道并返回管道读写端文件描述符
1049 int do_pipe_flags(int *fd, int flags)
1050 {
1051 struct file *fw, *fr;
1052 int error;
1053 int fdw, fdr;
1054
1055 if (flags & ~(O_CLOEXEC | O_NONBLOCK))
1056 return -EINVAL;
1057
1058 fw = create_write_pipe(flags);
1059 if (IS_ERR(fw))
1060 return PTR_ERR(fw);
1061 fr = create_read_pipe(fw, flags);
1062 error = PTR_ERR(fr);
1063 if (IS_ERR(fr))
1064 goto err_write_pipe;
1065
1066 error = get_unused_fd_flags(flags);
1067 if (error < 0)
1068 goto err_read_pipe;
1069 fdr = error;
1070
1071 error = get_unused_fd_flags(flags);
1072 if (error < 0)
1073 goto err_fdr;
1074 fdw = error;
1075
1076 audit_fd_pair(fdr, fdw);
1077 fd_install(fdr, fr);
1078 fd_install(fdw, fw);
1079 fd[0] = fdr;
1080 fd[1] = fdw;
1081
1082 return 0;
1083
1084 err_fdr:
1085 put_unused_fd(fdr);
1086 err_read_pipe:
1087 path_put(&fr->f_path);
1088 put_filp(fr);
1089 err_write_pipe:
1090 free_write_pipe(fw);
1091 return error;
1092 }
1.标识检查,标识中只能有O_CLOEXEC,O_NONBLOCK
2.创建管道及管道写端文件对象
3.创建管道读端文件对象
4.从进程的打开文件描述符表中取出两个未使用的文件描述符
5.将管道读写端文件对象与文件描述符关联起来
6.返回管道读写端文件描述符
iii.create_write_pipe
1.create_write_pipe
创建管道及写端文对象
972 struct file *create_write_pipe(int flags)
973 {
974 int err;
975 struct inode *inode;
976 struct file *f;
977 struct dentry *dentry;
978 struct qstr name = { .name = "" };
979
980 err = -ENFILE;
981 inode = get_pipe_inode();
982 if (!inode)
983 goto err;
984
985 err = -ENOMEM;
986 dentry = d_alloc(pipe_mnt->mnt_sb->s_root, &name);
987 if (!dentry)
988 goto err_inode;
989
990 dentry->d_op = &pipefs_dentry_operations;
991 /*
992 * We dont want to publish this dentry into global dentry hash table.
993 * We pretend dentry is already hashed, by unsetting DCACHE_UNHASHED
994 * This permits a working /proc/$pid/fd/XXX on pipes
995 */
996 dentry->d_flags &= ~DCACHE_UNHASHED;
997 d_instantiate(dentry, inode);
998
999 err = -ENFILE;
1000 f = alloc_file(pipe_mnt, dentry, FMODE_WRITE, &write_pipefifo_fops);
1001 if (!f)
1002 goto err_dentry;
1003 f->f_mapping = inode->i_mapping;
1004
1005 f->f_flags = O_WRONLY | (flags & O_NONBLOCK);
1006 f->f_version = 0;
1007
1008 return f;
1009
1010 err_dentry:
1011 free_pipe_info(inode);
1012 dput(dentry);
1013 return ERR_PTR(err);
1014
1015 err_inode:
1016 free_pipe_info(inode);
1017 iput(inode);
1018 err:
1019 return ERR_PTR(err);
1020 }
1.创建管道文件对应的inode
2.创建管道文件对应的dentry;并将dentry操作赋值为pipefs_dentry_operations
3.将管道文件对应的inode,dentry关联起来
4.创建管道写端文件,将文件操作赋值为write_pipefifo_fops;并将file与dentry关联起来
5.设置写端文件标识O_WRONLY,O_NONBLOCK
6.返回管道写端文件对象
注:
file,dentry,inode之间的关系见linux 虚拟文件系统VFS
管道文件操作见linux pipe文件系统(pipefs)
3.get_pipe_inode
创建管道及inode,并将管道与inode关联
935 static struct inode * get_pipe_inode(void)
936 {
937 struct inode *inode = new_inode(pipe_mnt->mnt_sb);
938 struct pipe_inode_info *pipe;
939
940 if (!inode)
941 goto fail_inode;
942
943 pipe = alloc_pipe_info(inode);
944 if (!pipe)
945 goto fail_iput;
946 inode->i_pipe = pipe;
947
948 pipe->readers = pipe->writers = 1;
949 inode->i_fop = &rdwr_pipefifo_fops;
950
951 /*
952 * Mark the inode dirty from the very beginning,
953 * that way it will never be moved to the dirty
954 * list because "mark_inode_dirty()" will think
955 * that it already _is_ on the dirty list.
956 */
957 inode->i_state = I_DIRTY;
958 inode->i_mode = S_IFIFO | S_IRUSR | S_IWUSR;
959 inode->i_uid = current_fsuid();
960 inode->i_gid = current_fsgid();
961 inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
962
963 return inode;
964
965 fail_iput:
966 iput(inode);
967
968 fail_inode:
969 return NULL;
970 }
1.pipefs中创建一个inode,即创建一个pipe文件
2.创建一个管道并与inode关联
3.将管道的当前读写者个数初始化为1
4.将管道文件操作置为rdwr_pipefifo_fops
5.初始化inode
4.alloc_pipe_info
创建管道并关联到inode
874 struct pipe_inode_info * alloc_pipe_info(struct inode *inode)
875 {
876 struct pipe_inode_info *pipe;
877
878 pipe = kzalloc(sizeof(struct pipe_inode_info), GFP_KERNEL);
879 if (pipe) {
880 init_waitqueue_head(&pipe->wait);
881 pipe->r_counter = pipe->w_counter = 1;
882 pipe->inode = inode;
883 }
884
885 return pipe;
886 }
1.从slab中分配管道描述符
2.实始化管道描述符:初始化管道的等待队列,读写者计数器为1,关联inode
3.返回管道描述符
5.free_pipe_info
释放管道
888 void __free_pipe_info(struct pipe_inode_info *pipe)
889 {
890 int i;
891
892 for (i = 0; i < PIPE_BUFFERS; i++) {
893 struct pipe_buffer *buf = pipe->bufs + i;
894 if (buf->ops)
895 buf->ops->release(pipe, buf);
896 }
897 if (pipe->tmp_page)
898 __free_page(pipe->tmp_page);
899 kfree(pipe);
900 }
901
902 void free_pipe_info(struct inode *inode)
903 {
904 __free_pipe_info(inode->i_pipe);
905 inode->i_pipe = NULL;
906 }
1.释放管道缓存
2.释放管道缓存页帧
3.释放管道描述符
4.取消inode的关联
6.free_write_pipe
释放管道写端文件及对应的管道
1022 void free_write_pipe(struct file *f)
1023 {
1024 free_pipe_info(f->f_dentry->d_inode);
1025 path_put(&f->f_path);
1026 put_filp(f);
1027 }
1.释放文件关联的管道
2.释放路径dentry
3.释放file对象
iv.create_read_pipe
创建管道读端文件对象
1029 struct file *create_read_pipe(struct file *wrf, int flags)
1030 {
1031 struct file *f = get_empty_filp();
1032 if (!f)
1033 return ERR_PTR(-ENFILE);
1034
1035 /* Grab pipe from the writer */
1036 f->f_path = wrf->f_path;
1037 path_get(&wrf->f_path);
1038 f->f_mapping = wrf->f_path.dentry->d_inode->i_mapping;
1039
1040 f->f_pos = 0;
1041 f->f_flags = O_RDONLY | (flags & O_NONBLOCK);
1042 f->f_op = &read_pipefifo_fops;
1043 f->f_mode = FMODE_READ;
1044 f->f_version = 0;
1045
1046 return f;
1047 }
1.分配file对象
2.将管道读端文件指向管道写端同一文件,即指向同一管道
3.初始化管道读端文件操作为read_pipefifo_fops
4.初始化管道读端文件