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.初始化管道读端文件