文章归档

文件系统:overlayfs

overlay文件系统的主要目的是要实现文件系统重叠,docker中的差分机制所依赖的文件系统分层就是依赖这种技术来实现的

1. upper and lower

overlay机制允许将两个文件系统重叠成一个文件系统,其中一个是upper,另一个是lower,对用户的可视顺序是: upper -> lower

简单来说,如果upper和lower同时存在一个相同的文件,那么用户看到的是upper中的文件,lower中的同路径文件会被自动隐藏

overlay只关心文件,目录是会被穿透的,所以严格来说,overlay重叠的是目录树,而不是“文件系统”

所有的修改都会写入upper,lower是只读的。upper的文件系统必须支持trusted.*扩展属性,所以upper是不支持NFS的

»» 继续阅读全文

Dynamic writeback throttling

https://lwn.net/Articles/405076/

Dynamic writeback throttling最主要的核心思想就是IO带宽估算。

the bandwidth estimation allows the kernel to scale dirty limits and I/O sizes to make the best use of all of the devices in the system, regardless of any specific device's performance characteristics. 传统writeback机制的做法是,当进程脏页超过一定比例时,调用balance_dirty_pages()函数进入同步写dirty pages过程,直到dirty pages的比例下降到一定比例,之后才允许该进程返回。

该机制存在三个问题:

  1. 进程脏页比率多少才合适?
  2. 内存压力太大时,多个后台进程同时writeback,会产生大量的随机IO,设备吞吐量下降
  3. 如何更准确的估算设备的真实带宽?

Dynamic writeback throttling的基本做法是:

  1. 启发式的计算设备的真实带宽
  2. 用户进程不再主动同步写,而是通过wait方式等待后台进程刷脏页

»» 继续阅读全文

dentry缓存

最近追查某线上服务发现一个问题:频繁的创建&删除目录或者文件,会消耗大量的物理内存。

原因是因为,目录或者文件删除后,dentry并没有立即释放,而是保存在了superblock上的lru链表上。通常只有内核认为内存紧张的时候才会回收此部分内存。

不过由于dentry结构非常小,这种case下内存增长是很慢。

Active / Total Objects (% used) : 740781 / 764463 (96.9%) Active / Total Slabs (% used) : 31522 / 31522 (100.0%) Active / Total Caches (% used) : 74 / 101 (73.3%) Active / Total Size (% used) : 144439.65K / 148432.28K (97.3%) Minimum / Average / Maximum Object : 0.01K / 0.19K / 8.00K OBJS ACTIVE USE OBJ SIZE SLABS OBJ/SLAB CACHE SIZE NAME 460278 460278 100% 0.19K 21918 21 87672K dentry 91416 81993 89% 0.10K 2344 39 9376K buffer_head 30660 29625 96% 0.19K 1460 21 5840K

»» 继续阅读全文

memory.usage_in_bytes内存只增不减

今天发现了一个问题,似乎memory.usage_in_bytes的内存总是在不停的增加,但很少减少,并不能说不减,只是很少。即使这个cgroup里的进程全kill了,usage_in_bytes依然纹丝不动。

经过分析之后,原因如下,与memory.usage_in_bytes的统计方式有关:

  1. 内核在统计的时候,usage_in_bytes包含进程的命名页 + 匿名页,匿名页通常就是file cache
  2. file cache是谁第一次使用算到谁的头上。
  3. file cache只有在完完全全被释放掉之后才会从usage_in_bytes中减掉,即使第一次使用它的进程早已死亡。

简单来说,就是我(某进程)读文件产生的file cache你(内核)算到我的头上,我退出了你也不清空我的计数,万一这个文件正好又被别的cgroup的进程依赖了,这笔帐还是赖在我的头上。。(内核虽然这么做,但其实是不合理的)

1、charge(计入)

file cache的charge函数是mem_cgroup_cache_charge(),这个函数通常是在文件页第一次被读上来时,内核将其加到file cache中时被调用,贴一个常见的调用栈:

[<ffffffff8116019d>] ? mem_cgroup_cache_charge+0xed/0x130 [<ffffffff8110bdba>] ? add_to_page_cache_locked+0xea/0x160 [<ffffffff8119c232>] mpage_readpages+0x102/0x150 [<ffffffff8120157d>] ext4_readpages+0x1d/0x20 [<ffffffff8110c521>] do_generic_file_read.clone.23+0x271/0x450 [<ffffffff8110d1ba>] generic_file_aio_read+0x1ca/0x240 [<ffffffff81164c82>] do_sync_read+0xd2/0x110 [<ffffffff81165463>] vfs_read+0xc3/0x180 [<ffffffff81165571>] sys_read+0x51/0x90

page cache的维护是独立于进程的,所以即使cgroup里的进程退出后,其所使用的page cache不会马上被释放。

2、uncharge(计出)

file cache的计出函数是 mem_cgroup_uncharge_cache_page(),uncharge主要有三种情况:

  1. 手动触发内核回收file cache时被调用
  2. 文件被删除
  3. 内核主动回收file cache时被调用。

手动触发回收file cache,通过sysctl修改vm.drop_caches的值来实现,调用栈如下:

[<ffffffff81114bae>] __mem_cgroup_uncharge_common+0x1ab/0x1f6 [<ffffffff81114c09>] mem_cgroup_uncharge_cache_page+0x10/0x12 [<ffffffff810e0ad0>] __remove_mapping+0xd0/0xf4 [<ffffffff810e0b0a>]

»» 继续阅读全文

多进程共享socket之close调用

当父子进程共享一个socket描述符时,如果其中一个进程执行close调用,那会不会发送FIN包,进而影响另一个进程的其他相关操作?只会减少引用计数,没什么其他的操作。

看下源码,可以顺着以下的调用栈看:

close ------------ open.c __close_fd ---- fs/file.c filp_close ---------- fs/open.c fput ------ fs/file_table.c __fput ---------- fs/file_table.c (the last op)

首先socket描述符本身也属于Unix文件的一种,其文件相关的file_operations实现在net/socket.c源文件,如下:

/* * Socket files have a set of 'special' operations as well as the generic file ones. * These don't appear in the operation structures but are done directly via the socketcall() * multiplexor. */ static const struct file_operations socket_file_ops = { .owner = THIS_MODULE, .llseek = no_llseek, .aio_read = sock_aio_read, .aio_write = sock_aio_write, .poll = sock_poll, .unlocked_ioctl = sock_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = compat_sock_ioctl, #endif .mmap = sock_mmap, .open = sock_no_open, /* special open code to disallow open

»» 继续阅读全文