linux 0.11 内核学习 -- pipe.c

/*

 * 该文件中的两个函数read_pipe和write_pipe是上层函数

 * read和write的底层实现

 */

/*

 *  linux/fs/pipe.c

 *

 *  (C) 1991  Linus Torvalds

 */

#include <signal.h>

#include <linux/sched.h>

// 内存管理头文件。含有页面大小定义和一些页面释放函数原型

#include <linux/mm.h> /* for get_free_page */

#include <asm/segment.h>

/* pipe类似于队列,向pipe的尾部写入数据,从pipe的头部读取数据    */

/* 管道读操作函数    */

/* 参数inode 是管道对应的i 节点,buf 是数据缓冲区指针,count 是读取的字节数 */

/*

 * read_pipe()函数用于读取管道中的数据,如果管道中没有数据的话,

 * 唤醒写管道进程,自己睡眠,如果读到数据,并把数据传到用户缓冲区,

 * 当把管道中所有的数据都取走后,也要唤醒等待写管道的进程,并返回

 * 已读数据字节数

 */

int read_pipe(struct m_inode * inode, char * buf, int count)

{

int chars, size, read = 0;

while (count>0) 

{

// 若当前管道中没有数据(size=0),则唤醒等待该节点的进程

while (!(size=PIPE_SIZE(*inode)))

{

wake_up(&inode->i_wait);

// 如果已没有写管道者

if (inode->i_count != 2) /* are there any writers? */

return read; // 返回已读字节数

// 否则在该i 节点上睡眠,等待信息

sleep_on(&inode->i_wait);

}

// 取管道尾到缓冲区末端的字节数chars

chars = PAGE_SIZE-PIPE_TAIL(*inode);

if (chars > count)

chars = count;

if (chars > size)

chars = size;

// 读字节计数减去此次可读的字节数chars,并累加已读字节数

count -= chars;

read += chars;

// 令size 指向管道尾部,调整当前管道尾指针(前移chars 字节)

size = PIPE_TAIL(*inode);

PIPE_TAIL(*inode) += chars;

PIPE_TAIL(*inode) &= (PAGE_SIZE-1);

// 将管道中的数据复制到用户缓冲区中

while (chars-->0)

put_fs_byte(((char *)inode->i_size)[size++],buf++);

}

// 唤醒等待该管道i 节点的进程

wake_up(&inode->i_wait);

// 返回读取的字节数

return read;

/*

* 函数read_pipe首先检查管道中是否含有数据,没有的话

* 唤醒等待进程。然后得到管道的尾指针,将尾指针位置向

* 前移动size个字节,最后将数据复制到用户程序区

*

* ------|----------------------------|--------

*  tail |                            |   head

* ------|----------------------------|--------

*  /|\                                   /|\

*   |                                     |

*  write the data                     get the data

*/

}

/* 想管道中写入数据,参数inode是管道对应的i节点,buf是数据缓冲区 */

/* count是写入管道的字节数     */

int write_pipe(struct m_inode * inode, char * buf, int count)

{

int chars, size, written = 0;

while (count>0) 

{

// 若当前管道中没有已经满了(size=0),则唤醒等待该节点的进程

while (!(size=(PAGE_SIZE-1)-PIPE_SIZE(*inode))) 

{

wake_up(&inode->i_wait);

// 如果已没有读管道者

if (inode->i_count != 2) { /* no readers */

// 向进程发送SIGPIPE信号

current->signal |= (1<<(SIGPIPE-1));

// 并返回已写入的字节数并退出

return written?written:-1;

}

// 睡眠,等待管道腾出空间

sleep_on(&inode->i_wait);

}

// 取管道头部到缓冲区末端空间字节数chars。如果其大于还需要写入的字节数count,则令其等于

// count。如果chars 大于当前管道中空闲空间长度size,则令其等于size

chars = PAGE_SIZE-PIPE_HEAD(*inode);

if (chars > count)

chars = count;

if (chars > size)

chars = size;

// 写入字节计数减去此次可写入的字节数chars,并累加已写字节数到written

count -= chars;

written += chars;

// 令size 指向管道数据头部,调整当前管道数据头部指针

size = PIPE_HEAD(*inode);

PIPE_HEAD(*inode) += chars;

PIPE_HEAD(*inode) &= (PAGE_SIZE-1);

// 从用户缓冲区复制chars 个字节到管道中

while (chars-->0)

((char *)inode->i_size)[size++]=get_fs_byte(buf++);

}

// 唤醒等待该i 节点的进程,返回已写入的字节数,退出

wake_up(&inode->i_wait);

return written;

/*

* 该函数首先是得到管道的长度,如果管道已满的话,唤醒等待的进程

* 否则得到管道头,移动管道头指针,将数据从用户区写到管道中

*/

}

/* 创建管道系统调用函数,fileds[2],fileds[0]用于读管道中数据,fileds[1] */

/* 用于向管道中写入数据,成功时返回0,出错时返回-1 */

int sys_pipe(unsigned long * fildes)

{

struct m_inode * inode;

struct file * f[2];

int fd[2];

int i,j;

// 从系统文件表中取两个空闲项(引用计数字段为0 的项),并分别设置引用计数为1

j=0;

for(i=0;j<2 && i<NR_FILE;i++)

if (!file_table[i].f_count)

(f[j++]=i+file_table)->f_count++;

// 如果只有一个空闲项,则释放该项(引用计数复位)

if (j==1)

f[0]->f_count=0;

// 如果没有找到两个空闲项,则返回-1

if (j<2)

return -1;

// 针对上面取得的两个文件结构项,分别分配一文件句柄,并使进程的文件结构指针分别指向这两个

// 文件结构

j=0;

for(i=0;j<2 && i<NR_OPEN;i++)

if (!current->filp[i]) 

{

current->filp[ fd[j]=i ] = f[j];

j++;

}

// 如果只有一个空闲文件句柄,则释放该句柄

if (j==1)

current->filp[fd[0]]=NULL;

// 如果没有找到两个空闲句柄,则释放上面获取的两个文件结构项(复位引用计数值)

if (j<2) {

f[0]->f_count=f[1]->f_count=0;

return -1;

}

// 申请管道i 节点,并为管道分配缓冲区(1 页内存)

if (!(inode=get_pipe_inode())) // 不成功 ?

{

// 相应释放两个文件句柄和文件结构项

current->filp[fd[0]] =

current->filp[fd[1]] = NULL;

f[0]->f_count = f[1]->f_count = 0;

return -1; // 返回值

}

// 初始化两个文件指针,将f[2]复制到fileds[2]中,将内核数据复制

// 到用户数据中

f[0]->f_inode = f[1]->f_inode = inode;

f[0]->f_pos = f[1]->f_pos = 0;

f[0]->f_mode = 1; /* read */

f[1]->f_mode = 2; /* write */

put_fs_long(fd[0],0+fildes);

put_fs_long(fd[1],1+fildes);

// 返回成功0

return 0;

/*

* 该函数实现的是系统调用。首先是查找全局的file_table来找到

* 两个空闲项,如果成功的话,设置当前进程的文件结构表项。申请

* i节点,将数据复制到结果中去

*/

}

参考《linux内核完全注释》和网上资料

posted @ 2010-02-08 11:13  qiang.xu  阅读(1468)  评论(0编辑  收藏  举报