分享
 
 
 

Linux环境进程间通信

王朝system·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

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。

fs/pipe.c

static struct file_system_type pipe_fs_type = {

.name = "pipefs",

.get_sb = pipefs_get_sb,

.kill_sb = kill_anon_super,

};

变量pipe_fs_type其类型是 struct file_system_type 用于向系统注册文件系统。

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

static struct super_block *pipefs_get_sb(struct file_system_type *fs_type,

int flags, const char *dev_name, void *data)

{

return get_sb_pseudo(fs_type, "pipe:", NULL, PIPEFS_MAGIC);

}

上为超级的生成函数。

Include/linux/pipe.h

#ifndef _LINUX_PIPE_FS_I_H

#define _LINUX_PIPE_FS_I_H

#define PIPEFS_MAGIC 0x50495045

struct pipe_inode_info {

wait_queue_head_t wait; 1

char *base; 2

unsigned int len; 3

unsigned int start; 4

unsigned int readers; 5

unsigned int writers; 6

unsigned int waiting_writers; 7

unsigned int r_counter; 8

unsigned int w_counter; 9

struct fasync_struct *fasync_readers; 10

struct fasync_struct *fasync_writers; 11

};

2 管道等待队列指针wait

3 内核缓冲区基地址base

4 缓冲区当前数据量

6 管道的读者数据量

7 管道的写者数据量

8 等待队列的读者个数

9 等待队列的写者个数

11、12 主要对 FIFO

五、管道的创建:

通过pipe系统调用来创建管道。

int do_pipe(int *fd)

{

struct qstr this;

char name[32];

struct dentry *dentry;

struct inode * inode;

struct file *f1, *f2;

int error;

int i,j;

error = -ENFILE;

f1 = get_empty_filp();//分配文件对象,得到文件对象指针用于读管道

if (!f1)

goto no_files;

f2 = get_empty_filp();//分配文件对象,得到文件对象指针用于读管道

if (!f2)

goto close_f1;

inode = get_pipe_inode(); 调用get_pipe_inode获得管道类型的索引节点

if (!inode) 的指针inode。

goto close_f12;

error = get_unused_fd(); 获得当前进程的两个文件描述符。在当前的

if (error < 0) 进程的进程描述符file域中,有一个fd 域,

goto close_f12_inode; 指向该进程当前打开文件指针数组,数组

i=error; 元素是指向文件对象的指针。

error = get_unused_fd();

if (error < 0)

goto close_f12_inode_i;

j = error;

error = -ENOMEM;

sprintf(name, "[%lu]", inode->i_ino); 生成对象目录dentry,

this.name = name; 并通过它将上述两个文

this.len = strlen(name); 件对象将的指针与管道

this.hash = inode->i_ino; /* will go */ 索引节点连接起来。

dentry = d_alloc(pipe_mnt->mnt_sb->s_root, &this);

if (!dentry)

goto close_f12_inode_i_j;

dentry->d_op = &pipefs_dentry_operations;

d_add(dentry, inode);

f1->f_vfsmnt = f2->f_vfsmnt = mntget(mntget(pipe_mnt));

f1->f_dentry = f2->f_dentry = dget(dentry);

f1->f_mapping = f2->f_mapping = inode->i_mapping;

/* read file */

f1->f_pos = f2->f_pos = 0; 为用于读的两个文件对象设置属性值

f1->f_flags = O_RDONLY; f_flage设置为只读,f_op设置为

f1->f_op = &read_pipe_fops; read_pipe_fops 结构的地址。

f1->f_mode = 1;

f1->f_version = 0;

/* write file */ 为用于写的两个文件对象设置属性值

f2->f_flags = O_WRONLY; f_flage设置为只写,f_op设置为

write_pipe_fops 结构的地址。

f2->f_op = &write_pipe_fops;

f2->f_mode = 2;

f2->f_version = 0;

fd_install(i, f1);

fd_install(j, f2);

fd[0] = i; 将两个文件描述符放入参数fd数组返回

fd[1] = j;

return 0;

close_f12_inode_i_j:

put_unused_fd(j);

close_f12_inode_i:

put_unused_fd(i);

close_f12_inode:

free_page((unsigned long) PIPE_BASE(*inode));

kfree(inode->i_pipe);

inode->i_pipe = NULL;

iput(inode);

close_f12:

put_filp(f2);

close_f1:

put_filp(f1);

no_files:

return error;

}

六、管道的释放

管道释放时f-op的release域在读管道和写管道中分别指向pipe_read_release()和pipe_write_release()。而这两个函数都调用release(),并决定是否释放pipe的内存页面或唤醒该管道等待队列的进程。

以下为管道释放的代码:

static int pipe_release(struct inode *inode, int decr, int decw)

{ down(PIPE_SEM(*inode));

PIPE_READERS(*inode) -= decr;

PIPE_WRITERS(*inode) -= decw;

if (!PIPE_READERS(*inode) && !PIPE_WRITERS(*inode)) {

struct pipe_inode_info *info = inode->i_pipe;

inode->i_pipe = NULL;

free_page((unsigned long) info->base);

kfree(info);

} else { wake_up_interruptible(PIPE_WAIT(*inode));

kill_fasync(PIPE_FASYNC_READERS(*inode), SIGIO, POLL_IN);

kill_fasync(PIPE_FASYNC_WRITERS(*inode), SIGIO, POLL_OUT); }

up(PIPE_SEM(*inode));

return 0;}

七、管道的读写

1.从管道中读取数据:

如果管道的写端不存在,则认为已经读到了数据的末尾,读函数返回的读出字节数为0;

当管道的写端存在时,如果请求的字节数目大于PIPE_BUF,则返回管道中现有的数据字节数,如果请求的字节数目不大于PIPE_BUF,则返回管道中现有数据字节数(此时,管道中数据量小于请求的数据量);或者返回请求的字节数(此时,管道中数据量不小于请求的数据量)。

2.向管道中写入数据:

向管道中写入数据时,linux将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。如果读进程不读走管道缓冲区中的数据,那么写操作将一直阻塞。

八、管道的局限性

管道的主要局限性正体现在它的特点上:

只支持单向数据流;

只能用于具有亲缘关系的进程之间;

没有名字;

管道的缓冲区是有限的(管道制存在于内存中,在管道创建时,为缓冲区分配一个页面大小);

管道所传送的是无格式字节流,这就要求管道的读出方和写入方必须事先约定好数据的格式,比如多少字节算作一个消息(或命令、或记录)等等。

九、后记

写完本文之后,发现有部分不足之处。在由于管道读写的代码过于冗长,限于篇幅不一一列出。有不足和错误之处还请各位老师指正。通过一段时间对Linux的内核代码的学习,开源的程序往往并非由“权威人士”、“享誉海内外的专家”所编写,它们的由一个个普通的程序员写就。但专业造就专家,长时间集中在某个领域中能够创建出据程序员应该珍视的财富。

完成之时特别感谢我的搭档周欣和张博的大力支持和帮助。

十、参考资料

《代码阅读方法与实践》 (希腊)Diomidis Spinellis 著

赵学良 译

清华大学出版社

《Linux 内核指导》 李善平 陈文智 著

浙江大学出版社

《Linux程序设计权威指南》 于明俭 陈向阳 方汉 编著

机械工业出版社

IBM developerWorks 中国网站

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有