Ext2层读文件入口函数

    技术2024-07-26  65

    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

     907                /* nr is the maximum number of bytes to copy from this page */

     908                nr = PAGE_CACHE_SIZE;

     909                if (index >= end_index) {

     910                        if (index > end_index)

     911                                goto out;

     912                        nr = ((isize - 1) & ~PAGE_CACHE_MASK) + 1;

     913                        if (nr <= offset) {

     914                                goto out;

     915                        }

     916                }

     917                nr = nr - offset;

     918

     919                cond_resched();

     920                if (index == next_index)

     921                        next_index = page_cache_readahead(mapping, &ra, filp,

     922                                        index, last_index - index);

     923

     924find_page:

     925                page = find_get_page(mapping, index);

     926                if (unlikely(page == NULL)) {

     927                        handle_ra_miss(mapping, &ra, index);

     928                        goto no_cached_page;

     929                }

     930                if (!PageUptodate(page))

     931                        goto page_not_up_to_date;

     932page_ok:

     933

     934                /* If users can be writing to this page using arbitrary

     935                 * virtual addresses, take care about potential aliasing

     936                 * before reading the page on the kernel side.

     937                 */

     938                if (mapping_writably_mapped(mapping))

     939                        flush_dcache_page(page);

     940

     941                /*

     942                 * When (part of) the same page is read multiple times

     943                 * in succession, only mark it as accessed the first time.

     944                 */

     945                if (prev_index != index)

     946                        mark_page_accessed(page);

     947                prev_index = index;

     948

     949                /*

     950                 * Ok, we have the page, and it's up-to-date, so

     951                 * now we can copy it to user space...

     952                 *

     953                 * The actor routine returns how many bytes were actually used..

     954                 * NOTE! This may not be the same as how much of a user buffer

     955                 * we filled up (we may be padding etc), so we can only update

     956                 * "pos" here (the actor routine has to update the user buffer

     957                 * pointers and the remaining count).

     958                 */

     959                ret = actor(desc, page, offset, nr);

     960                offset += ret;

     961                index += offset >> PAGE_CACHE_SHIFT;

     962                offset &= ~PAGE_CACHE_MASK;

     963

     964                page_cache_release(page);

     965                if (ret == nr && desc->count)

     966                        continue;

     967                goto out;

     968

     969page_not_up_to_date:

     970                /* Get exclusive access to the page ... */

     971                lock_page(page);

     972

     973                /* Did it get unhashed before we got the lock? */

     974                if (!page->mapping) {

     975                        unlock_page(page);

     976                        page_cache_release(page);

     977                        continue;

     978                }

     979

     980                /* Did somebody else fill it already? */

     981                if (PageUptodate(page)) {

     982                        unlock_page(page);

     983                        goto page_ok;

     984                }

     985

     986readpage:

     987                /* Start the actual read. The read will unlock the page. */

     988                error = mapping->a_ops->readpage(filp, page);

     989

     990                if (unlikely(error)) {

     991                        if (error == AOP_TRUNCATED_PAGE) {

     992                                page_cache_release(page);

     993                                goto find_page;

     994                        }

     995                        goto readpage_error;

     996                }

     997

     998                if (!PageUptodate(page)) {

     999                        lock_page(page);

    1000                        if (!PageUptodate(page)) {

    1001                                if (page->mapping == NULL) {

    1002                                        /*

    1003                                         * invalidate_inode_pages got it

    1004                                         */

    1005                                        unlock_page(page);

    1006                                        page_cache_release(page);

    1007                                        goto find_page;

    1008                                }

    1009                                unlock_page(page);

    1010                                error = -EIO;

    1011                                shrink_readahead_size_eio(filp, &ra);

    1012                                goto readpage_error;

    1013                        }

    1014                        unlock_page(page);

    1015                }

    1016

    1017                /*

    1018                 * i_size must be checked after we have done ->readpage.

    1019                 *

    1020                 * Checking i_size after the readpage allows us to calculate

    1021                 * the correct value for "nr", which means the zero-filled

    1022                 * part of the page is not copied back to userspace (unless

    1023                 * another truncate extends the file - this is desired though).

    1024                 */

    1025                isize = i_size_read(inode);

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

    1027                if (unlikely(!isize || index > end_index)) {

    1028                        page_cache_release(page);

    1029                        goto out;

    1030                }

    1031

    1032                /* nr is the maximum number of bytes to copy from this page */

    1033                nr = PAGE_CACHE_SIZE;

    1034                if (index == end_index) {

    1035                        nr = ((isize - 1) & ~PAGE_CACHE_MASK) + 1;

    1036                        if (nr <= offset) {

    1037                                page_cache_release(page);

    1038                                goto out;

    1039                        }

    1040                }

    1041                nr = nr - offset;

    1042                goto page_ok;

    1043

    1044readpage_error:

    1045                /* UHHUH! A synchronous read error occurred. Report it */

    1046                desc->error = error;

    1047                page_cache_release(page);

    1048                goto out;

    1049

    1050no_cached_page:

    1051                /*

    1052                 * Ok, it wasn't cached, so we need to create a new

    1053                 * page..

    1054                 */

    1055                if (!cached_page) {

    1056                        cached_page = page_cache_alloc_cold(mapping);

    1057                        if (!cached_page) {

    1058                                desc->error = -ENOMEM;

    1059                                goto out;

    1060                        }

    1061                }

    1062                error = add_to_page_cache_lru(cached_page, mapping,

    1063                                                index, GFP_KERNEL);

    1064                if (error) {

    1065                        if (error == -EEXIST)

    1066                                goto find_page;

    1067                        desc->error = error;

    1068                        goto out;

    1069                }

    1070                page = cached_page;

    1071                cached_page = NULL;

    1072                goto readpage;

    1073        }

    1074

    1075out:

    1076        *_ra = ra;

    1077

    1078        *ppos = ((loff_t) index << PAGE_CACHE_SHIFT) + offset;

    1079        if (cached_page)

    1080                page_cache_release(cached_page);

    1081        if (filp)

    1082                file_accessed(filp);

    1083}

     

    要看懂这个函数的代码,首先要简单地介绍一下文件高速缓存的背景知识。前面我们在讲解Ext2超级块对象和索引节点对象的时候,涉及到了一点高速缓存的知识,其实页高速缓存的类型分为两种,磁盘页高速缓存和文件页高速缓存。

     

    磁盘页高速缓存是,把缓冲区页的描述符插人基树,树根是与块设备相关的特殊bdev文件系统中索引节点的address_space对象。前面我们已经讲过了,这里再强调一下,这种缓冲区页必须满足很强的约束条件,就是所有的块缓冲区涉及的块必须是在块设备上相邻存放的。

     

    而文件页高速缓存却不同,是把缓冲区页的描述符插入普通文件的inode对应address_space的基树。文件数据被分割为一个个以 4k字节,也就是一个页面大小为单元的数据块,这些数据块(页)被组织成一个基树( radix 树)。树中所有叶子节点为一个个页框结构(struct page),表示了用于缓存该文件的每一个页。在叶子层最左端的第一个页保存着该文件的前4096 个字节,接下来的页保存着文件第二个4096 个字节,严格地依次类推。

     

    树中的所有中间节点为组织节点,指示某一地址上的数据所在的页。此树的层次可以从0 层到6 层,所支持的文件大小从 0 字节到16 T 个字节。树的根节点指针可以从和文件相关的 address_space 对象(该对象保存在和文件关联的inode 对象中)中取得(更多关于 page cache 的结构内容请参见博客“磁盘高速缓存”

    http://blog.csdn.net/yunsongice/archive/2010/08/23/5833154.aspx)。

     

    言归正传,函数 do_generic_mapping_read首先879行获得地址空间对象的所有者,即索引节点对象,它将拥有填充了文件数据的页面。它的地址存放在address_space对象的host字段中。千万要注意,如果所读文件是块设备文件,这也是一般情况下,我们读取SCSI磁盘上的文件,那么所有者就不是由filp->f_dentry->d_inode所指向的索引节点对象,而是bdev特殊文件系统中的索引节点对象。

     

    892行,把文件看作细分的数据页(每页4096字节),并从文件指针*ppos导出第一个请求字节所在页的逻辑号,即地址空间中的页索引,并把它存放在index局部变量中。也把第一个请求字节在页内的偏移量存放在offset局部变量中。

     

    904行开始一个循环来读入包含请求字节的所有页,要读数据的字节数存放在read_descriptor_t描述符的count字段中。在一次单独的循环期间,函数通过执行下列的子步骤来传送一个数据页:

     

    909~916行,如果index*4096+offset超过存放在索引节点对象的i_size字段中的文件大小,则从循环退出。919行调用cond_resched()来检查当前进程的标志TIF_NEED_RESCHED。如果该标志置位,则调用函数schedule()。如果有预读的页,921行读入这些页面。

     

    注意到函数中还有一个内部变量next_index,在函数内等于index,所以921page_cache_readahead函数肯定会执行,用来预读一些页面,取决于传递进来的file参数的f_ra字段。后面章节我们会详细讲解文件的预读。这里就略过。

     

    函数中还有一个内部变量last_index,这个last_index指向需要读的所有页面集合的最后一个页框号(我们需要读的页面范围是last_index - index)。那么接下来的工作就是从indexlast_index一页一页的读入到用户空间。

     

    925行调用find_get_page(),并传入指向address_space对象的指针及索引值作为参数;它将查找页高速缓存以找到包含所请求数据的页描述符(如果有的话)。我们看到,根据文件当前的读写位置,在页高速缓存中中找到缓存请求数据的 page。这个pageindex的值是文件当前读写位置ppos右移PAGE_CACHE_SHIFT。这个宏等于PAGE_SHIFT,也就是12正好反映了,ppos在磁盘中,距离文件开头所使用了页面个数。

     

    如果find_get_page()返回NULL指针,则所请求的页不在页高速缓存中。如果这样,927行调用handle_ra_miss()来调整预读系统的参数。然后走到1050行的no_cached_page标号下,调用page_cache_alloc_cold使用伙伴系统alloc_pages分配一个新页;调用add_to_page_cache()插入该新页的页描述符到address_space的基树page_tree中,对应位置为局部变量index,即对应磁盘地址空间中的页索引。记住该函数将新页的PG_locked标志置位。调用add_to_page_cache_lru()将该新页描述符插入到LRU链表。然后跳到readpage开始读文件数据。

     

    如果find_get_page()返回该页,说明页已经位于页高速缓存中。通过930PageUptodate宏检查标志PG_uptodate:如果置位,则页所存数据是最新的,因此无需从磁盘读数据,就到page_ok标号。如果没有置位,说明页中的数据是无效的,因此必须从磁盘读取,则跳到page_not_up_to_date标号处,通过971行调用lock_page()函数获取对页的互斥访问。

     

    现在页已由当前进程锁定。然而,另一个进程也许会在上一步之前已从页高速缓存中删除该页,那么,它就要检查页描述符的mapping字段是否为NULL。在这中情形下,它将调用975行的unlock_page()来解锁页,976行的page_cache_release宏减少它的引用计数(find_get_page()所增加的计数),并跳回924行的find_page标号处来重读同一页。

     

    如果函数已运行至980行,说明页已被锁定且在页高速缓存中。981行再次检查标志PG_uptodate,因为另一个内核控制路径可能已经完成上面的必要读操作。如果标志置位,则调用unlock_page()并跳至page_ok标号处来跳过读操做。

     

    现在真正的I/O操作可以开始了,988行调用文件的address_space对象之readpage方法。传递给它的是对应的待填充页描述符page和文件对象file,函数会负责激活磁盘到页之间的I/O数据传输。我们稍后再讨论该函数对普通文件与块设备文件都会做些什么。

     

    来到998行,如果标志PG_uptodate还没有置位,则它会等待直到调用lock_page()函数后页被有效读入。该页在前面被锁定,一旦读操作完成就被解锁。因此当前进程在I/O数据传输完成时才停止睡眠。

     

    1027行,如果index超出文件包含的页数(该数是通过将inode对象的i_size字段的值除以4096得到的),那么它将减少页的引用计数器,并跳出循环。这种情况发生在这个正被本进程读的文件同时有其他进程正在删减它的时候。

     

    1033-1041行将应被拷入用户态缓冲区的页中的字节数存放在局部变量nr中。这个值应该等于页的大小(4096字节),除非offset0(这只发生在读请求书的首尾页时)或请求数据不全在该文件中。

     

    又回到page_ok标号,这回已经prev_index不等于index了,所以调用mark_page_accessed()将标志PG_referencedPG_active置位,从而表示该页正被访问并且不应该被换出。如果同一文件(或它的一部分)在do_generic_file_read()的后续执行中要读几次,那么这个步骤只在第一次读时执行。

     

    现在到了把页中的数据拷贝到用户态缓冲区的时候了。为了这么做,do_generic_file_read()959行调用file_read_actor()函数,该函数的地址作为参数传递。file_read_actor()执行下列步骤:

     

    i.     调用kmap(),该函数为处于高端内存中的页建立永久的内核映射。

    ii.    调用__copy_to_user(),该函数把页中的数据拷贝到用户态地址空间。注意,这个操作在访问用户态地址空间时如果有缺页异常将会阻塞进程。

    iii.   调用kunmap()来释放页的任一永久内核映射。

    iv.    更新read_descriptor_t描述符的count writtenbuf字段。

     

    960行,根据传入用户态缓冲区的有效字节数来更新局部变量indexcount一般情况下,如果页的最后一个字节已拷贝到用户态缓冲区,那么index的值加1offset的值清0;否则,index的值不变而offset的值被设为已拷贝到用户态缓冲区的字节数。

     

    964行减少页描述符的引用计数器。965行判断:如果read_descriptor_t描述符的count字段不为0,那么文件中还有其他数据要读,继续循环来读文件中的下一页数据。

     

    至此,所有请求的或者说可以读到的数据已读完。到out;标号,1076行更新预读数据结构filp->f_ra来标记数据已被顺序从文件读入。1078行把index*4096+offset值赋给*ppos,从而保存以后调用read()write()进行顺序访问的位置。

     

    最后调用file_accessed(filp)把当前时间存放在文件的索引节点对象的i_atime字段中,并把它标记为脏后返回。

     

    到此,我们知道:当页上的数据不是最新的时候,该函数调用mapping->a_ops->readpage 所指向的函数(变量mappinginode 对象中的address_space对象),那么这个函数到底是什么呢?

     

    前面我们学习了,ext2_fill_super函数初始化超级快后,ext2_read_inode就是ext2超级块的s_op->read_inode具体实现函数。当需要建立一个文件的inode时,该函数会调用ext2_get_inode函数,从一个页高速缓存中读入一个磁盘索引节点结构ext2_inode,然后初始化VFSinode。其中,索引节点inode->i_mapping->a_ops 被初始化成了ext2_aops全局变量。

     

    其实,address_space 对象是嵌入在 inode 对象之中的,那么不难想象,address_space 对象成员 a_ops 的初始化工作将会在初始化 inode 对象时进行。ext2_aops全局变量的readpage字段都是ext2_readpage,最终调用ext2_readpage 函数处理读数据请求。到此为止, ext2 文件系统层的工作结束。

    最新回复(0)