如何在Linux内核中禁用页面缓存?

5
如何避免内核页面缓存,在应用程序可以直接从磁盘读写数据?在内核中,如何设置?

不太清楚你的问题。你是想在用户空间中禁用页面缓存,还是针对所有用户空间进程?你必须在内核本身中执行此操作,还是无论如何都可以?请提供更多细节。 - Sam Protsenko
我只想为GlusterFS禁用页面缓存。通过Fuse,我将GlusterFS客户端(nodeA)以直接IO模式(支持FUSE)挂载到/mnt/glusterfs。当我在GlusterFS中打开文件时,由于FUSE支持的直接IO模式,文件不会被缓存在GlusterFS客户端(nodeA)中,但是文件会被缓存在GlusterFS服务器(nodeB)中。换句话说,在服务器(nodeB)中,我不想缓存该文件。那么问题是,如何为GlusterFS服务器(nodeB)禁用页面缓存? - user6481589
打开一个文件,请求被发送如下:open(User-Space,nodeA) ---> sys_open(Kernel,nodeA) ---> fuse_open(kernel,nodeA) ---> client_open(User-space,nodeA) ---> server_open(User-Space,nodeB) ---> sys_open(Kernel,nodeB) ---> ext4_open(Kernel,nodeB)。 - user6481589
2个回答

5
你需要应用程序来调用O_DIRECT。参考man手册http://man7.org/linux/man-pages/man2/open.2.html
使用O_DIRECT,你告诉内核在进行I/O时不要从页面缓存中读取/写入数据。
O_DIRECT (自Linux 2.4.10起) 尝试最小化文件的I/O缓存效果。一般情况下会降低性能,但在特殊情况下非常有用,比如当应用程序自己做缓存的时候。文件I/O直接与用户空间缓冲区交互。仅使用O_DIRECT标志会尝试以同步方式传输数据,但并不能像O_SYNC标志那样保证数据和必要的元数据都被传输。为了保证同步I/O,必须同时使用O_SYNC和O_DIRECT标志。有关进一步讨论,请参见下面的注释。
          A semantically similar (but deprecated) interface for block
          devices is described in raw(8).

打开一个文件,请求会像这样发送:open(User-Space,nodeA)--->sys_open(Kernel,nodeA)----->fuse_open(kernel,node‌​A)------->client_open(User-space,nodeA)-----network------->server_open(User-Space,nodeB)‌​------->sys_open(Kernel,nodeB)------->ext4_open(Kernel,nodeB)。在网络之前,它们属于GlusterFS客户端,在网络之后,它们是GlusterFS服务器。如果我在client_open()中添加O_DIRECT标志,那么服务器将以直接IO模式打开文件吗? - user6481589
嗯,如果我理解正确的话,您想要在跨越Gluster FUSE挂载的网络文件系统中打开文件,并且希望在不使用服务器页面缓存的情况下打开服务器,但是您希望客户端能够指定这一点给服务器。如果我的理解是正确的,我会建议您阅读Gluster FS协议规范,以查看它是否将此类属性传递给服务器。 - Chaitanya Lala

0

更新

规范中的写缓存与页面缓存无关。这里的缓存实际上是指集成在磁盘控制器中的RAM/NVRAM,这种内存不应与页面缓存混淆!




据我所知,这些仅保证SATA和NVMe设备的写入页面启用/禁用开关。

SATA

参考SATA 3.0规范:

SET FEATURES(写缓存启用/禁用):由子命令代码02h或82h的SET FEATURES命令建立的写缓存启用/禁用设置。

在Linux内核下,HDIO_SET_WCACHE ioctl可以控制它:

static DEFINE_MUTEX(ide_disk_ioctl_mutex);
static const struct ide_ioctl_devset ide_disk_ioctl_settings[] = {
{ HDIO_GET_ADDRESS, HDIO_SET_ADDRESS,   &ide_devset_address   },
{ HDIO_GET_MULTCOUNT,   HDIO_SET_MULTCOUNT, &ide_devset_multcount },
{ HDIO_GET_NOWERR,  HDIO_SET_NOWERR,    &ide_devset_nowerr    },
{ HDIO_GET_WCACHE,  HDIO_SET_WCACHE,    &ide_devset_wcache    },
{ HDIO_GET_ACOUSTIC,    HDIO_SET_ACOUSTIC,  &ide_devset_acoustic  },
{ 0 }
};

int ide_disk_ioctl(ide_drive_t *drive, struct block_device *bdev, fmode_t mode,
           unsigned int cmd, unsigned long arg)
{
    int err;

    mutex_lock(&ide_disk_ioctl_mutex);
    err = ide_setting_ioctl(drive, bdev, cmd, arg, ide_disk_ioctl_settings);
    if (err != -EOPNOTSUPP)
        goto out;

    err = generic_ide_ioctl(drive, bdev, cmd, arg);
out:
    mutex_unlock(&ide_disk_ioctl_mutex);
    return err;
}

你也可以使用hdparm -W0/1 /dev/sdx方便地禁用/启用写缓存,这也会在内部调用HDIO_SET_WCACHE

}
        if (!wcache)
            err = flush_wcache(fd);
        if (ioctl(fd, HDIO_SET_WCACHE, wcache)) {
            __u8 setcache[4] = {ATA_OP_SETFEATURES,0,0,0};
            setcache[2] = wcache ? 0x02 : 0x82;
            if (do_drive_cmd(fd, setcache, 0)) {
                err = errno;
                perror(" HDIO_DRIVE_CMD(setcache) failed");
            }
        }

NVME

内核源代码:

static ssize_t queue_wc_show(struct request_queue *q, char *page)
{
    if (test_bit(QUEUE_FLAG_WC, &q->queue_flags))
        return sprintf(page, "write back\n");

    return sprintf(page, "write through\n");
}

static ssize_t queue_wc_store(struct request_queue *q, const char *page,
                  size_t count)
{
    int set = -1;

    if (!strncmp(page, "write back", 10))
        set = 1;
    else if (!strncmp(page, "write through", 13) ||
         !strncmp(page, "none", 4))
        set = 0;

    if (set == -1)
        return -EINVAL;

    if (set)
        blk_queue_flag_set(QUEUE_FLAG_WC, q);
    else
        blk_queue_flag_clear(QUEUE_FLAG_WC, q);

    return count;
}

nvme规范:

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接