posix_fadvise源码分析

posix_fadvise是linux上对文件进行预取的系统调用,其中第四个参数int advice为预取的方式,主要有以下几种:

POSIX_FADV_NORMAL                         无特别建议                                      重置预读大小为默认值
POSIX_FADV_SEQUENTIAL                将要进行顺序操作                           设预读大小为默认值的2 倍
POSIX_FADV_RANDOM                        将要进行随机操作                           将预读大小清零(禁止预读)
POSIX_FADV_NOREUSE                     指定的数据将只访问一次              (暂无动作)
POSIX_FADV_WILLNEED                    指定的数据即将被访问                    立即预读数据到page cache
POSIX_FADV_DONTNEED                  指定的数据近期不会被访问            立即从page cache 中丢弃数据

其中,POSIX_FADV_NORMAL、POSIX_FADV_SEQUENTIAL、POSIX_FADV_RANDOM  是用来调整预取窗口的大小,POSIX_FADV_WILLNEED则可以将指定范围的磁盘文件读入到page cache中,POSIX_FADV_DONTNEED则将指定的磁盘文件中数据从page cache中换出。

在kernel源码的mm/fadvise.c中,如下所示:

SYSCALL_DEFINE(fadvise64_64)(int fd, loff_t offset, loff_t len, int advice)
{
    struct file *file = fget(fd);
    struct address_space *mapping;
    struct backing_dev_info *bdi;
    loff_t endbyte;            /* inclusive */
    pgoff_t start_index;
    pgoff_t end_index;
    unsigned long nrpages;
    int ret = 0;

/* 判断由文件描述符获得的file指针是否有效 */
    if (!file)
        return -EBADF;

/* 判断是否是管道 */
    if (S_ISFIFO(file->f_path.dentry->d_inode->i_mode)) {
        ret = -ESPIPE;
        goto out;
    }


    mapping = file->f_mapping;
    if (!mapping || len < 0) {
        ret = -EINVAL;
        goto out;
    }

    if (mapping->a_ops->get_xip_mem) {
        switch (advice) {
        case POSIX_FADV_NORMAL:
        case POSIX_FADV_RANDOM:
        case POSIX_FADV_SEQUENTIAL:
        case POSIX_FADV_WILLNEED:
        case POSIX_FADV_NOREUSE:
        case POSIX_FADV_DONTNEED:
            /* no bad return value, but ignore advice */
            break;
        default:
            ret = -EINVAL;
        }
        goto out;
    }

    /* Careful about overflows. Len == 0 means "as much as possible" */
    endbyte = offset + len;
    if (!len || endbyte < len)
        endbyte = -1;
    else
        endbyte--;        /* inclusive */

    bdi = mapping->backing_dev_info;

    switch (advice) {
    case POSIX_FADV_NORMAL:

       /* 将预取窗口调整为设备默认的大小 */
        file->f_ra.ra_pages = bdi->ra_pages;
        spin_lock(&file->f_lock);
        file->f_mode &= ~FMODE_RANDOM;
        spin_unlock(&file->f_lock);
        break;
    case POSIX_FADV_RANDOM:

        /* 将文件的访问模式设为random即不做预取 */
        spin_lock(&file->f_lock);
        file->f_mode |= FMODE_RANDOM;
        spin_unlock(&file->f_lock);
        break;
    case POSIX_FADV_SEQUENTIAL:

        /* 将预取窗口设为默认值的2倍 */
        file->f_ra.ra_pages = bdi->ra_pages * 2;
        spin_lock(&file->f_lock);
        file->f_mode &= ~FMODE_RANDOM;
        spin_unlock(&file->f_lock);
        break;
    case POSIX_FADV_WILLNEED:

        /* 强制读取文件指定范围内的数据 */

        if (!mapping->a_ops->readpage) {
            ret = -EINVAL;
            break;
        }

        /* First and last PARTIAL page! */

        /* 获得起始页、结束页的编号 */
        start_index = offset >> PAGE_CACHE_SHIFT;
        end_index = endbyte >> PAGE_CACHE_SHIFT;

        /* Careful about overflow on the "+1" */

        /* 计算需要读取的页数,并+1向上取整 */

        nrpages = end_index - start_index + 1;
        if (!nrpages)
            nrpages = ~0UL;

       /* 强制读取指定范围的数据 */       

        ret = force_page_cache_readahead(mapping, file,
                start_index,
                nrpages);
        if (ret > 0)
            ret = 0;
        break;
    case POSIX_FADV_NOREUSE:

        /* 没有实现,no use 没用 */

        break;
    case POSIX_FADV_DONTNEED:

        /* 将指定范围内的数据从page cache中换出 */

        if (!bdi_write_congested(mapping->backing_dev_info))
            filemap_flush(mapping);

        /* First and last FULL page! */

        /* 计算数据的起始页、结束页的编号 */

        start_index = (offset+(PAGE_CACHE_SIZE-1)) >> PAGE_CACHE_SHIFT;
        end_index = (endbyte >> PAGE_CACHE_SHIFT);

        if (end_index >= start_index)

           /* 将指定页从page cache中换出*/

            invalidate_mapping_pages(mapping, start_index,
                        end_index);
        break;
    default:
        ret = -EINVAL;
    }
out:
    fput(file);
    return ret;
}


阅读更多

没有更多推荐了,返回首页