linux 系统调用pipe

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

你可能感兴趣的:(linux 系统调用pipe)