免费注册 查看新帖 |

Chinaunix

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

linux 2.6.11内核文件IO的系统调用实现分析(read,write) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2005-05-17 17:22 |只看该作者 |倒序浏览

    本帖是《linux 2.6.11内核文件IO的系统调用实现分析(open,creat)》的续帖,将主要说明read和write两个文件IO的系统调用实现。所用到的主要数据结构在前面一帖中已经详细说明了,如有需要,请参见前一帖。


    6.        read 函数
    6.1.        原型与参数

    ssize_t read(unsigned int fd, char * buf, size_t count)
            read函数是从打开的文件中读取数据。如read成功,则返回读到的字节数。如已到达文件的尾端,则返回0。如果失败,则返回-1。有多种情况可使实际读到的字节数少于要求读字节数:
    • 读普通文件时,在读到要求字节数之前已到达了文件尾端。例如,若在到达文件尾端之前还有30个字节,而要求读100个字节,则read返回30,下一次再调用read时,它将返回0 (文件尾端)。
    • 当从终端设备读时,通常一次最多读一行(第11章将介绍如何改变这一点)。
    • 当从网络读时,网络中的缓冲机构可能造成返回值小于所要求读的字节数。
    • 某些面向记录的设备,例如磁带,一次最多返回一个记录。
    读操作从文件的当前位移量处开始,在成功返回之前,该位移量增加实际读得的字节数。

    6.2.        实现分析

    6.2.1.        主要函数调用关系图
    sys_read (参见6.2.2 )
    | ------------- vfs_read (参见6.2.3)

    6.2.2.        主调用函数sys_read
    asmlinkage ssize_t sys_read(unsigned int fd, char __user * buf, size_t count)
    {
            struct file *file;
            ssize_t ret = -EBADF;
            int fput_needed;
           
    // 从进程文件链表中根据fd获取file文件表,如果没有这个文件表,则返回EBADF错误
            file = fget_light(fd, &fput_needed); // fput_needed标识是否需要更新file结构的count
            if (file) {
                    loff_t pos = file_pos_read(file); // 获取file结构的pos位移参数
                    ret = vfs_read(file, buf, count, &pos); // 读取文件内容,参见6.2.2
                    file_pos_write(file, pos); // 更新file结构的pos位移参数
                    fput_light(file, fput_needed); // 更新文件count标识
            }

            return ret;
    }

    6.2.3.        sys_read子函数vfs_read
    vfs_read将调用文件驱动模块的read函数从磁盘中读入数据并返回。
    ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
    {
            ssize_t ret;

            if (!(file->;f_mode & FMODE_READ))
                    return -EBADF;// 如果文件不允许读模式,则返回EBADF错误
            if (!file->;f_op || (!file->;f_op->;read && !file->;f_op->;aio_read))
                    return -EINVAL;// 如果文件结构没有read函数指针,则返回EINVAL错误
            if (unlikely(!access_ok(VERIFY_WRITE, buf, count)))
                    return -EFAULT;// 检查传入的参数是否超出了用户空间地址,如果是则返回EFAULT错误

            // 通过inode结构lock当前要操作的区域,成功返回0
            ret = rw_verify_area(READ, file, pos, count);
            if (!ret) {
                    // 检查进程对文件是否有READ操作权限,成功,则返回0
                    ret = security_file_permission (file, MAY_READ);
                    if (!ret) {
                            if (file->;f_op->;read) // 调用文件驱动模块read函数读入数据
                                    ret = file->;f_op->;read(file, buf, count, pos);
                            else // 否则调用直接磁盘读操作
                                    ret = do_sync_read(file, buf, count, pos);
                            if (ret >; 0) {
                                    // 通知目录,当前文件已经被访问
                                    dnotify_parent(file->;f_dentry, DN_ACCESS);
                                    current->;rchar += ret; // 刷新进程读数据计数器
                            }
                            current->;syscr++;
                    }
            }

            return ret;
    }


    7.        write函数

    7.1.        原型与参数
    sys_write(unsigned int fd, const char * buf, size_t count)
            sys_write函数将数据写入到文件中。其返回值通常与参数nbytes的值不同,-1表示出错。write出错的一个常见原因是:磁盘已写满,或者超过了对一个给定进程的文件长度限制。对于普通文件,写操作从文件的当前位移量处开始。如果在打开该文件时,指定了O_APPEND选择项,则在每次写操作之前,将文件位移量设置在文件的当前结尾处。在一次成功写之后,该文件位移量增加实际写的字节数。

    7.2.        实现分析

    7.2.1.        主要函数调用关系图
    sys_write (参见7.2.2 )
    | ------------- vfs_write (参见7.2.3)

    7.2.2.        主调用函数sys_write
    asmlinkage ssize_t sys_write(unsigned int fd, const char __user * buf, size_t count)
    {
            struct file *file;
            ssize_t ret = -EBADF;
            int fput_needed;

    // 从进程文件链表中根据fd获取file文件表,如果没有这个文件表,则返回EBADF错误
            file = fget_light(fd, &fput_needed); // fput_needed标识是否需要更新file结构的count
            if (file) {
                    loff_t pos = file_pos_read(file); // 获取file结构的pos位移参数
                    ret = vfs_write(file, buf, count, &pos); // 写入文件,参见7.2.3 描述
                    file_pos_write(file, pos); // 更新file结构的pos位移参数
                    fput_light(file, fput_needed); // 更新文件count标识
            }

            return ret;
    }

    7.2.3.        sys_write子函数vfs_write
    vfs_write将调用文件驱动模块的write函数向磁盘中写入数据并返回。如果在打开该文件时,指定了O_APPEND选择项,则在每次写操作之前,将文件位移量设置在文件的当前结尾处再继续写入。
    ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
    {
            ssize_t ret;

            if (!(file->;f_mode & FMODE_WRITE))
                    return -EBADF; // 如果文件不允许写模式,则返回EBADF错误
            if (!file->;f_op || (!file->;f_op->;write && !file->;f_op->;aio_write))
                    return -EINVAL; // 如果文件结构没有read函数指针,则返回EINVAL错误
            if (unlikely(!access_ok(VERIFY_READ, buf, count)))
                    return -EFAULT; // 检查传入的参数是否超出了用户空间地址,如果是则返回EFAULT错误

            // 通过inode结构lock当前要操作的区域,成功返回0
            ret = rw_verify_area(WRITE, file, pos, count);
            if (!ret) {
                    // 检查进程对文件是否有WRITE操作权限,成功,则返回0
                    ret = security_file_permission (file, MAY_WRITE);
                    if (!ret) {
                            if (file->;f_op->;write) // 调用文件驱动模块write函数写入数据
                                    ret = file->;f_op->;write(file, buf, count, pos);
                            else // 否则调用直接磁盘写操作
                                    ret = do_sync_write(file, buf, count, pos);
                            if (ret >; 0) {
                                    // 通知目录,当前文件已经被改写
                                    dnotify_parent(file->;f_dentry, DN_MODIFY);
                                    current->;wchar += ret; // 刷新进程写数据计数器
                            }
                            current->;syscw++;
                    }
            }

            return ret;
    }

论坛徽章:
0
2 [报告]
发表于 2005-05-17 18:27 |只看该作者

linux 2.6.11内核文件IO的系统调用实现分析(read,write)

好帖,谢谢!

论坛徽章:
0
3 [报告]
发表于 2005-05-18 09:50 |只看该作者

linux 2.6.11内核文件IO的系统调用实现分析(read,write)

小结一下:
   Read 和write函数部分其实处理逻辑比较简单,主要流程都是这样:
   A,        检测操作权限
   B,        获取偏移信息
   C,        调用文件驱动模块操作
   D,        刷新偏移信息
   E,        更新文件状态
   F,        进程读写计数器刷新

论坛徽章:
0
4 [报告]
发表于 2005-05-18 11:23 |只看该作者

linux 2.6.11内核文件IO的系统调用实现分析(read,write)

先给鼓励。

你继续深入分析。比如

if (file->;f_op->;write) // 调用文件驱动模块write函数写入数据
ret = file->;f_op->;write(file, buf, count, pos);
else // 否则调用直接磁盘写操作

不是调用驱动模块,而是正常的非驱动函数。中间还有复杂的软件MMU层(address space).

所以你分析的远没有完成,仅仅是open read write syscall的外层的一小部分。

论坛徽章:
0
5 [报告]
发表于 2005-05-18 12:49 |只看该作者

linux 2.6.11内核文件IO的系统调用实现分析(read,write)

原帖由 "思一克" 发表:
先给鼓励。

你继续深入分析。比如

if (file->;f_op->;write) // 调用文件驱动模块write函数写入数据
ret = file->;f_op->;write(file, buf, count, pos);
else // 否则调用直接磁盘写操作

..........


斑竹说得不错,我是怕帖出来没有人看,现在帖的这点都不知道大家意见如何啊。如果大家觉得有必要,俺也可以把更下层的函数帖出来

论坛徽章:
0
6 [报告]
发表于 2005-05-18 13:05 |只看该作者

linux 2.6.11内核文件IO的系统调用实现分析(read,write)

那就更加值得鼓励了

论坛徽章:
0
7 [报告]
发表于 2005-05-19 09:31 |只看该作者

linux 2.6.11内核文件IO的系统调用实现分析(read,write)

这个看起来容易多了,支持!!!

论坛徽章:
0
8 [报告]
发表于 2005-05-20 09:24 |只看该作者

linux 2.6.11内核文件IO的系统调用实现分析(read,write)

[quote]原帖由 "lifengliu"]这个看起来容易多了,支持!!![/quote 发表:


是否像斑竹所说,需要更底层的说明哪?? 下面函数太多,我写了一个比较完整的分析文档,参考了2.4的internel文档写的,如果有需要,可以发给你

论坛徽章:
0
9 [报告]
发表于 2005-05-20 13:53 |只看该作者

linux 2.6.11内核文件IO的系统调用实现分析(read,write)

牛人,学习,顶
可以吧你的文裆打个包,放个地方下载

论坛徽章:
0
10 [报告]
发表于 2005-05-23 09:42 |只看该作者

linux 2.6.11内核文件IO的系统调用实现分析(read,write)

原帖由 "benlan" 发表:
......
可以吧你的文裆打个包,放个地方下载


哥哥,俺现在有FS部分的文档,不过不知道该放在那里...................
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP