1.4 使用BLKFLSBUF清Buffer通过走读块设备驱动IOCTL命令实现 , 发现该命令能有效的清除整个块设备所占用的buffer 。
int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd, unsigned long arg){ struct gendisk *disk = bdev->bd_disk; struct backing_dev_info *bdi; loff_t size; int ret, n;switch(cmd) { case BLKFLSBUF: if (!capable(CAP_SYS_ADMIN)) return -EACCES;ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg); if (!is_unrecognized_ioctl(ret)) return ret;fsync_bdev(bdev); invalidate_bdev(bdev); return 0;case ……:…………} /* Invalidate clean unused buffers and pagecache. */void invalidate_bdev(struct block_device *bdev){ struct address_space *mapping = bdev->bd_inode->i_mapping;if (mapping->nrpages == 0) return;invalidate_bh_lrus(); lru_add_drain_all(); /* make sure all lru add caches are flushed */ invalidate_mapping_pages(mapping, 0, -1); /* 99% of the time, we don't need to flush the cleancache on the bdev. * But, for the strange corners, lets be cautious */ cleancache_invalidate_inode(mapping);}EXPORT_SYMBOL(invalidate_bdev);光代码不够 , 现在让我们看下对/dev/h_sda这个块设备执行BLKFLSBUF的IOCTL命令前后的实际内存变化:
[0225_19:10:25:10s][root@test nfs_dir] # cat /proc/meminfo[0225_19:10:25:10s]MemTotal: 90532 kB[0225_19:10:25:10s]MemFree: 12296 kB[0225_19:10:25:10s]Buffers: 46076 kB[0225_19:10:25:10s]Cached: 4136 kB…………[0225_19:10:42:10s][root@test nfs_dir] # /mnt/nfs_dir/a.out[0225_19:10:42:10s]ioctl cmd BLKFLSBUF ok![0225_19:10:44:10s][root@test nfs_dir] # cat /proc/meminfo[0225_19:10:44:10s]MemTotal: 90532 kB[0225_19:10:44:10s]MemFree: 58988 kB[0225_19:10:44:10s]Buffers: 0 kB…………[0225_19:10:44:10s]Cached: 4144 kB执行的效果如代码中看到的 , Buffers已被全部清除了 , MemFree一下增长了约46MB , 可以知道原先的Buffer已被回收并转化为可用的内存 。整个过程Cache几乎没有变化 , 仅增加的8K cache内存可以推断用于a.out本身及其他库文件的加载 。
上述a.out的示例如下:
#include <stdio.h>#include <fcntl.h>#include <errno.h>#include <sys/ioctl.h>#define BLKFLSBUF _IO(0x12, 97)int main(int argc, char* argv[]){ int fd = -1; fd = open("/dev/h_sda", O_RDWR); if (fd < 0) { return -1; } if (ioctl(fd, BLKFLSBUF, 0)) { printf("ioctl cmd BLKFLSBUF failed, errno:%dn", errno); } close(fd);printf("ioctl cmd BLKFLSBUF ok!n"); return 0;}综上 , 使用块设备命令BLKFLSBUF能有效的清除块设备上的所有buffer , 且清除后的buffer能立即被释放变为可用内存 。
利用这一点 , 联系后端业务场景 , 给出应用程序编程建议:
- 1、 每次关闭一个块设备文件描述符前 , 必须要调用BLKFLSBUF命令 , 确保buffer中的脏数据及时刷入块设备 , 避免意外断电导致数据丢失 , 同时也起到及时释放回收buffer的目的 。
- 2、 当操作一个较大的块设备时 , 必要时可以调用BLKFLSBUF命令 。怎样算较大的块设备?一般理解为当前Linux系统可用的物理内存小于操作的块设备大小 。
首先来看下内核源码实现:
int drop_caches_sysctl_handler(ctl_table *table, int write, void __user *buffer, size_t *length, loff_t *ppos){ int ret;ret = proc_dointvec_minmax(table, write, buffer, length, ppos); if (ret) return ret; if (write) { /* => echo 1 > /proc/sys/vm/drop_caches 清理页缓存 */ if (sysctl_drop_caches & 1) /* => 遍历所有的超级块 , 清理所有的缓存 */ iterate_supers(drop_pagecache_sb, NULL); if (sysctl_drop_caches & 2) drop_slab(); } return 0;} /** * iterate_supers - call function for all active superblocks * @f: function to call * @arg: argument to pass to it * * Scans the superblock list and calls given function, passing it * locked superblock and given argument. */void iterate_supers(void (*f)(struct super_block *, void *), void *arg){ struct super_block *sb, *p = NULL;spin_lock(&sb_lock); list_for_each_entry(sb, &super_blocks, s_list) { if (hlist_unhashed(&sb->s_instances)) continue; sb->s_count++; spin_unlock(&sb_lock);down_read(&sb->s_umount); if (sb->s_root && (sb->s_flags & MS_BORN)) f(sb, arg); up_read(&sb->s_umount);spin_lock(&sb_lock); if (p) __put_super(p); p = sb; } if (p) __put_super(p); spin_unlock(&sb_lock);} /* => 清理文件系统(包括bdev伪文件系统)的页缓存 */static void drop_pagecache_sb(struct super_block *sb, void *unused){ struct inode *inode, *toput_inode = NULL;spin_lock(&inode_sb_list_lock); /* => 遍历所有的inode */ list_for_each_entry(inode, &sb->s_inodes, i_sb_list) { spin_lock(&inode->i_lock); /* * => 若当前状态为(I_FREEING|I_WILL_FREE|I_NEW) 或 * => 若没有缓存页 * => 则跳过 */ if ((inode->i_state & (I_FREEING|I_WILL_FREE|I_NEW)) || (inode->i_mapping->nrpages == 0)) { spin_unlock(&inode->i_lock); continue; } __iget(inode); spin_unlock(&inode->i_lock); spin_unlock(&inode_sb_list_lock); /* => 清除缓存页(除了脏页、上锁的、正在回写的或映射在页表中的)*/ invalidate_mapping_pages(inode->i_mapping, 0, -1); iput(toput_inode); toput_inode = inode; spin_lock(&inode_sb_list_lock); } spin_unlock(&inode_sb_list_lock); iput(toput_inode);}
推荐阅读
- 活的化石,昌宁的千年古茶树
- 教你如何把电脑磁盘标修改成你女神的图片
- win10网页出现stack overflow at line 0的解决方法
- 太平猴魁外形品质的审评方法介绍
- 不知道这十项Linux常识,别说自己是运维工程师
- 普洱茶消费主流不是快消品
- HTTPS原理看了很多,这个是最清晰的
- MySQL中的并发控制概览
- 获取有关 Linux shell 内置命令的帮助
- SEO竞争对手分析