免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 908 | 回复: 0
打印 上一主题 下一主题

Linux环境进程间通信 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2005-03-21 20:59 |只看该作者 |倒序浏览
Linux环境进程间通信
                ——无名管道工作机制研究

引言
Linux作为一个开源的操作系统,是我们进行操作系统和提高编程水平的最佳途径之一。
好的程序如同好的音乐一样,完成的完美、巧妙。开放源码的程序都是经过无数人检验地,本文将以linux-kernel-2.6.5为例对pipe的工作机制进行阐述。
一、        进程间通信的分类
    大型程序大多会涉及到某种形式的进程间通信,一个较大型的应用程序设计成可以相互通信的“碎片”,从而就把一个任务分到多个进程中去。进程间通信的方法有三种方式:
      管道(pipe)
      套接字(socket)
      System v IPC 机制
管道机制在UNIX开发的早期就已经提供了,它在本机上的两个进程间的数据传递表现的相当出色;套接字是在BSD(Berkeley Software Development)中出现的,现在的应用也相当的广泛;而System V IPC机制Unix System V 版本中出现的。
二、        工作机制
管道分为pipe(无名管道)和FIFO(        命名管道),它们都是通过内核缓冲区按先进先出的方式数据传输,管道一端顺序地写入数据,另一端顺序地读入数据读写的位置都是自动增加,数据只读一次,之后就被释放。在缓冲区写满时,则由相应的规则控制读写进程进入等待队列,当空的缓冲区有写入数据或满的缓冲区有数据读出时,就唤醒等待队列中的写进程继续读写。

管道的读写规则:

管道两端可分别用描述字fd[0]以及fd[1]来描述,需要注意的是,管道的两端是固定了任务的。即一端只能用于读,由描述字fd[0]表示,称其为管道读端;另一端则只能用于写,由描述字fd[1]来表示,称其为管道写端。如果试图从管道写端读取数据,或者向管道读端写入数据都将导致错误发生。一般文件的I/O函数都可以用于管道,如close、read、write等等。
四、pipe的数据结构
首先要定义一个文件系统类型:pipe_fs_type。
  1. fs/pipe.c
  2. static struct file_system_type pipe_fs_type = {
  3.         .name                = "pipefs",
  4.         .get_sb                = pipefs_get_sb,
  5.         .kill_sb        = kill_anon_super,
  6. };
复制代码

变量pipe_fs_type其类型是 struct file_system_type 用于向系统注册文件系统。
Pipe以类似文件的方式与进程交互,但在磁盘上无对应节点,因此效率较高。Pipe主要包括一个inode和两个file结构——分别用于读和写。Pipe的缓冲区首地址就存放在inode的i_pipe域指向pipe_inode_info结构中。但是要注意pipe的inode并没有磁盘上的映象,只在内存中交换数据。

  1. static struct super_block *pipefs_get_sb(struct file_system_type *fs_type,
  2.         int flags, const char *dev_name, void *data)
  3. {
  4.         return get_sb_pseudo(fs_type, "pipe:", NULL, PIPEFS_MAGIC);
  5. }
复制代码

上为超级的生成函数。
  1. Include/linux/pipe.h
  2. #ifndef _LINUX_PIPE_FS_I_H
  3. #define _LINUX_PIPE_FS_I_H

  4. #define PIPEFS_MAGIC 0x50495045
  5. struct pipe_inode_info {
  6.         wait_queue_head_t wait;                              1
  7.         char *base;                                         2                                                        
  8.         unsigned int len;                                     3
  9.         unsigned int start;                                    4
  10.         unsigned int readers;                                  5
  11.         unsigned int writers;                                  6
  12.         unsigned int waiting_writers;                           7
  13.         unsigned int r_counter;                                8
  14.         unsigned int w_counter;                               9
  15.         struct fasync_struct *fasync_readers;                     10
  16.         struct fasync_struct *fasync_writers;                     11
  17. };
复制代码

    2 管道等待队列指针wait
    3 内核缓冲区基地址base
    4 缓冲区当前数据量
    6 管道的读者数据量
    7 管道的写者数据量
    8 等待队列的读者个数
    9 等待队列的写者个数
    11、12 主要对 FIFO

五、管道的创建:
通过pipe系统调用来创建管道。
  1. int do_pipe(int *fd)
  2. {
  3.         struct qstr this;
  4.         char name[32];
  5.         struct dentry *dentry;
  6.         struct inode * inode;
  7.         struct file *f1, *f2;
  8.         int error;
  9.         int i,j;

  10.         error = -ENFILE;
  11.         f1 = get_empty_filp();//分配文件对象,得到文件对象指针用于读管道
  12.         if (!f1)
  13.                 goto no_files;

  14.         f2 = get_empty_filp();//分配文件对象,得到文件对象指针用于读管道
  15.         if (!f2)
  16.                 goto close_f1;

  17.         inode = get_pipe_inode();     调用get_pipe_inode获得管道类型的索引节点
  18.         if (!inode)                  的指针inode。
  19.                 goto close_f12;         

  20.         error = get_unused_fd();       获得当前进程的两个文件描述符。在当前的
  21.         if (error < 0)                 进程的进程描述符file域中,有一个fd 域,
  22.                 goto close_f12_inode;      指向该进程当前打开文件指针数组,数组
  23.         i=error;                     元素是指向文件对象的指针。

  24.         error = get_unused_fd();
  25.         if (error < 0)
  26.                 goto close_f12_inode_i;
  27.         j = error;



  28.         error = -ENOMEM;                              
  29.         sprintf(name, "[%lu]", inode->;i_ino);                生成对象目录dentry,
  30.         this.name = name;                               并通过它将上述两个文
  31.         this.len = strlen(name);                           件对象将的指针与管道
  32.         this.hash = inode->;i_ino; /* will go */               索引节点连接起来。
  33.         dentry = d_alloc(pipe_mnt->;mnt_sb->;s_root, &this);
  34.         if (!dentry)
  35.                 goto close_f12_inode_i_j;
  36.         dentry->;d_op = &pipefs_dentry_operations;
  37.         d_add(dentry, inode);
  38.         f1->;f_vfsmnt = f2->;f_vfsmnt = mntget(mntget(pipe_mnt));
  39.         f1->;f_dentry = f2->;f_dentry = dget(dentry);
  40.         f1->;f_mapping = f2->;f_mapping = inode->;i_mapping;

  41.         /* read file */
  42.         f1->;f_pos = f2->;f_pos = 0;      为用于读的两个文件对象设置属性值
  43.         f1->;f_flags = O_RDONLY;      f_flage设置为只读,f_op设置为
  44.         f1->;f_op = &read_pipe_fops;     read_pipe_fops 结构的地址。
  45.         f1->;f_mode = 1;
  46.         f1->;f_version = 0;

  47.         /* write file */                  为用于写的两个文件对象设置属性值
  48.         f2->;f_flags = O_WRONLY;       f_flage设置为只写,f_op设置为
  49.                                 write_pipe_fops 结构的地址。
  50.         f2->;f_op = &write_pipe_fops;
  51.         f2->;f_mode = 2;
  52.         f2->;f_version = 0;

  53.         fd_install(i, f1);
  54.         fd_install(j, f2);
  55.         fd[0] = i;         将两个文件描述符放入参数fd数组返回
  56.         fd[1] = j;
  57.         return 0;

  58. close_f12_inode_i_j:
  59.         put_unused_fd(j);
  60. close_f12_inode_i:
  61.         put_unused_fd(i);
  62. close_f12_inode:
  63.         free_page((unsigned long) PIPE_BASE(*inode));
  64.         kfree(inode->;i_pipe);
  65.         inode->;i_pipe = NULL;
  66.         iput(inode);
  67. close_f12:
  68.         put_filp(f2);
  69. close_f1:
  70.         put_filp(f1);
  71. no_files:
  72.         return error;       
  73. }
复制代码

六、管道的释放
管道释放时f-op的release域在读管道和写管道中分别指向pipe_read_release()和pipe_write_release()。而这两个函数都调用release(),并决定是否释放pipe的内存页面或唤醒该管道等待队列的进程。
以下为管道释放的代码:
  1. static int pipe_release(struct inode *inode, int decr, int decw)
  2. {        down(PIPE_SEM(*inode));
  3.         PIPE_READERS(*inode) -= decr;
  4.         PIPE_WRITERS(*inode) -= decw;
  5.         if (!PIPE_READERS(*inode) && !PIPE_WRITERS(*inode)) {
  6.                 struct pipe_inode_info *info = inode->;i_pipe;
  7.                 inode->;i_pipe = NULL;
  8.                 free_page((unsigned long) info->;base);
  9.                 kfree(info);
  10.         } else {                wake_up_interruptible(PIPE_WAIT(*inode));
  11.                 kill_fasync(PIPE_FASYNC_READERS(*inode), SIGIO, POLL_IN);
  12.                 kill_fasync(PIPE_FASYNC_WRITERS(*inode), SIGIO, POLL_OUT);        }
  13.         up(PIPE_SEM(*inode));
  14.         return 0;}
复制代码

七、管道的读写
1.从管道中读取数据:
如果管道的写端不存在,则认为已经读到了数据的末尾,读函数返回的读出字节数为0;
当管道的写端存在时,如果请求的字节数目大于PIPE_BUF,则返回管道中现有的数据字节数,如果请求的字节数目不大于PIPE_BUF,则返回管道中现有数据字节数(此时,管道中数据量小于请求的数据量);或者返回请求的字节数(此时,管道中数据量不小于请求的数据量)。
2.向管道中写入数据:
向管道中写入数据时,linux将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。如果读进程不读走管道缓冲区中的数据,那么写操作将一直阻塞。
八、管道的局限性
管道的主要局限性正体现在它的特点上:
"        只支持单向数据流;
"        只能用于具有亲缘关系的进程之间;
"        没有名字;
"        管道的缓冲区是有限的(管道制存在于内存中,在管道创建时,为缓冲区分配一个页面大小);
"        管道所传送的是无格式字节流,这就要求管道的读出方和写入方必须事先约定好数据的格式,比如多少字节算作一个消息(或命令、或记录)等等。
九、后记
    写完本文之后,发现有部分不足之处。在由于管道读写的代码过于冗长,限于篇幅不一一列出。有不足和错误之处还请各位老师指正。通过一段时间对Linux的内核代码的学习,开源的程序往往并非由“权威人士”、“享誉海内外的专家”所编写,它们的由一个个普通的程序员写就。但专业造就专家,长时间集中在某个领域中能够创建出据程序员应该珍视的财富。


十、参考资料
《代码阅读方法与实践》 (希腊)Diomidis Spinellis  著
                                                                        赵学良                                  译
                                                        清华大学出版社
《Linux 内核指导》      李善平                陈文智  著
                                                        浙江大学出版社
《Linux程序设计权威指南》  于明俭 陈向阳 方汉 编著
                         机械工业出版社
IBM developerWorks 中国网站
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP