现在的位置: 首页 > 综合 > 正文

Ext2层读文件入口函数

2013年05月18日 ⁄ 综合 ⁄ 共 6947字 ⁄ 字号 评论关闭

1.2.5 Ext2层读文件入口函数

好了,我们知道了Ext2文件系统的磁盘布局,以及始终缓存的磁盘超级拷贝块结构ext2_super_block和动态缓存的已分配磁盘索引节点结构ext2_inode这些预备知识。接下来就假设一个文件的inode已经分配好,并且包含该文件所有块号的对应宿主ext2_inode_info结构也在内存中初始化好了。那么如何读这个文件?

 

前面讲了,ext2层,也就是第二扩展文件系统的入口函数 generic_file_read,下面我们就从它开始,进入读文件操作的Ext2层:

 

ssize_t

generic_file_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)

{

       struct iovec local_iov = { .iov_base = buf, .iov_len = count };

       struct kiocb kiocb;

       ssize_t ret;

 

       init_sync_kiocb(&kiocb, filp);

       ret = __generic_file_aio_read(&kiocb, &local_iov, 1, ppos);

       if (-EIOCBQUEUED == ret)

              ret = wait_on_sync_kiocb(&kiocb);

       return ret;

}

 

我们看到,generic_file_read调用函数__generic_file_aio_read,来自mm/filemap.c

 

1134ssize_t

1135__generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov,

1136                unsigned long nr_segs, loff_t *ppos)

1137{

1138        struct file *filp = iocb->ki_filp;

1139        ssize_t retval;

1140        unsigned long seg;

1141        size_t count;

1142

1143        count = 0;

1144        for (seg = 0; seg < nr_segs; seg++) {

1145                const struct iovec *iv = &iov[seg];

1146

1147                /*

1148                 * If any segment has a negative length, or the cumulative

1149                 * length ever wraps negative then return -EINVAL.

1150                 */

1151                count += iv->iov_len;

1152                if (unlikely((ssize_t)(count|iv->iov_len) < 0))

1153                        return -EINVAL;

1154                if (access_ok(VERIFY_WRITE, iv->iov_base, iv->iov_len))

1155                        continue;

1156                if (seg == 0)

1157                        return -EFAULT;

1158                nr_segs = seg;

1159                count -= iv->iov_len;   /* This segment is no good */

1160                break;

1161        }

1162

1163        /* coalesce the iovecs and go direct-to-BIO for O_DIRECT */

1164        if (filp->f_flags & O_DIRECT) {

1165                loff_t pos = *ppos, size;

1166                struct address_space *mapping;

1167                struct inode *inode;

1168

1169                mapping = filp->f_mapping;

1170                inode = mapping->host;

1171                retval = 0;

1172                if (!count)

1173                        goto out; /* skip atime */

1174                size = i_size_read(inode);

1175                if (pos < size) {

1176                        retval = generic_file_direct_IO(READ, iocb,

1177                                                iov, pos, nr_segs);

1178                        if (retval > 0 && !is_sync_kiocb(iocb))

1179                                retval = -EIOCBQUEUED;

1180                        if (retval > 0)

1181                                *ppos = pos + retval;

1182                }

1183                file_accessed(filp);

1184                goto out;

1185        }

1186

1187        retval = 0;

1188        if (count) {

1189                for (seg = 0; seg < nr_segs; seg++) {

1190                        read_descriptor_t desc;

1191

1192                        desc.written = 0;

1193                        desc.arg.buf = iov[seg].iov_base;

1194                        desc.count = iov[seg].iov_len;

1195                        if (desc.count == 0)

1196                                continue;

1197                        desc.error = 0;

1198                        do_generic_file_read(filp,ppos,&desc,file_read_actor);

1199                        retval += desc.written;

1200                        if (desc.error) {

1201                                retval = retval ?: desc.error;

1202                                break;

1203                        }

1204                }

1205        }

1206out:

1207        return retval;

1208}

 

函数__generic_file_aio_read()是所有文件系统实现同步和异步读操作所使用的通用例程。该函数接受四个参数:kiocb描述符的地址iocbiovec描述符数组的地址iov、数组的长度和存放文件当前指针的一个变量的地址pposiovec描述符数组被函数generic_file_read()调用时只有一个元素,该元素描述待接收数据的用户态缓冲区。

 

为什么只有一个元素呢?read()系统调用的一个叫做readv()的变体允许应用程序定义多个用户态缓冲区,从文件读出的数据分散存放在其中;__generic_file_aio_read()函数也实现这种功能,只不过从文件读出的数据将只烤贝到一个用户态缓冲区,所以只有一个元素。不过,可以想象,使用多个缓冲区虽然简单,但需要执行更多的步骤。

 

我们现在来说明函数__generic_file_aio_read()的操作。为简单起见,我们只针对最常见的情形,即对页高速缓存文件的系统调用read()所引发的同步操作。我们不讨论如何对错误和异常的处理。

 

我们看到,1154行调用access_ok()来检查iovec描述符所描述的用户态缓冲区是否有效。因为起始地址和长度已经从sys_read()系统调用得到,因此在使用前需要对它们进行检查。如何检查呢?access_ok宏实际上是__range_not_ok宏:

 

#define access_ok(type, addr, size) (likely(__range_not_ok(addr, size) == 0))

#define __range_not_ok(addr, size)                                  /

({                                                            /

       unsigned long flag, roksum;                              /

       __chk_user_ptr(addr);                                       /

       asm("add %3,%1 ; sbb %0,%0 ; cmp %1,%4 ; sbb $0,%0"              /

           : "=&r" (flag), "=r" (roksum)                            /

           : "1" (addr), "g" ((long)(size)),                          /

             "rm" (current_thread_info()->addr_limit.seg));              /

       flag;                                                  /

})

 

如果参数无效,也就是检查addraddr+ size的地址区间大于current进程的thread_info结构的addr_limit.seg的值,则返回错误代码-EFAULT

 

随后1189,其实传进来的参数nr_segs1,所以1190行只建立一个读操作描述符,也就是一个read_descriptor_t类型的数据结构。该结构存放与单个用户态缓冲相关的文件读操作的当前状态。

 

typedef struct {

       size_t written;   //已经拷贝到用户态缓冲区的字节数

       size_t count;    //待传送的字节数

       union {

              char __user *buf;

              void *data;

       } arg;           //在用户态缓冲区中的当前位置

       int error;         //读操作的错误码(0表示无错误)

} read_descriptor_t;

 

__generic_file_aio_read函数判断本次读请求的访问方式,如果是直接I/O模式(filp->f_flags 被设置了 O_DIRECT 标志,即不经过 cache)的方式,则调用generic_file_direct_IO 函数;不过我们最常用的是 page cache 的方式,则调用1198行的do_generic_file_read 函数,传送给它文件对象指针filp、文件偏移量指针ppos,刚分配的读操作描述符的地址和函数file_read_actor()的地址:

 

static inline void do_generic_file_read(struct file * filp, loff_t *ppos,

                                   read_descriptor_t * desc,

                                   read_actor_t actor)

{

       do_generic_mapping_read(filp->f_mapping,

                            &filp->f_ra,

                            filp,

                            ppos,

                            desc,

                            actor);

}

 

函数 do_generic_file_read 仅仅是一个包装函数,把该文件的file结构的address_space字段传给 do_generic_mapping_read 函数:

 

872void do_generic_mapping_read(struct address_space *mapping,

 873                             struct file_ra_state *_ra,

 874                             struct file *filp,

 875                             loff_t *ppos,

 876                             read_descriptor_t *desc,

 877                             read_actor_t actor)

 878{

 879        struct inode *inode = mapping->host;

 880        unsigned long index;

 881        unsigned long end_index;

 882        unsigned long offset;

 883        unsigned long last_index;

 884        unsigned long next_index;

 885        unsigned long prev_index;

 886        loff_t isize;

 887        struct page *cached_page;

 888        int error;

 889        struct file_ra_state ra = *_ra;

 890

 891        cached_page = NULL;

 892        index = *ppos >> PAGE_CACHE_SHIFT;

 893        next_index = index;

 894        prev_index = ra.prev_page;

 895        last_index = (*ppos + desc->count + PAGE_CACHE_SIZE-1) >> PAGE_CACHE_SHIFT;

 896        offset = *ppos & ~PAGE_CACHE_MASK;

 897

 898        isize = i_size_read(inode);

 899        if (!isize)

 900                goto out;

 901

 902        end_index = (isize - 1) >> PAGE_CACHE_SHIFT;

 903        for (;;) {

 904                struct page *page;

 905                unsigned long nr, ret;

 906

<

抱歉!评论已关闭.