Skip to content

Commit 8947aba

Browse files
Bulk of updates.
Signed-off-by: Leon <[email protected]>
1 parent 285b8de commit 8947aba

File tree

16 files changed

+587
-369
lines changed

16 files changed

+587
-369
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
- [内存管理](https://github.com/freelancer-leon/notes/blob/master/kernel/mm/mm.md)
3737
- [分页管理](https://github.com/freelancer-leon/notes/blob/master/kernel/mm/mm_pagetable.md)
3838
- [进程地址空间](https://github.com/freelancer-leon/notes/blob/master/kernel/mm/mm-1-process_addr_spc.md)
39+
- [slab](https://github.com/freelancer-leon/notes/blob/master/kernel/mm/slab.md)
3940
- [slub](https://github.com/freelancer-leon/notes/blob/master/kernel/mm/slub.md)
4041

4142
## 网络

kernel/Bottom_Half.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,9 @@ struct softirq_action
9292
```c
9393
asmlinkage __visible void __softirq_entry __do_softirq(void)
9494
{
95-
unsigned long end = jiffies + MAX_SOFTIRQ_TIME;
95+
unsigned long end = jiffies + MAX_SOFTIRQ_TIME; /*默认为 2ms*/
9696
unsigned long old_flags = current->flags;
97-
int max_restart = MAX_SOFTIRQ_RESTART;
97+
int max_restart = MAX_SOFTIRQ_RESTART; /*默认为 10 次*/
9898
struct softirq_action *h;
9999
bool in_hardirq;
100100
__u32 pending;
@@ -147,13 +147,13 @@ restart:
147147

148148
rcu_bh_qs();
149149
local_irq_disable(); /*返回之前恢复中断的关闭状态。*/
150-
151-
pending = local_softirq_pending();
152-
if (pending) {
150+
/*以上为在软中断上下文中调用软中断处理函数的部分,下面是考虑是否还要继续在软中断上下文中处理软中断*/
151+
pending = local_softirq_pending(); /*如果此时还有未处理完的软中断*/
152+
if (pending) { /*软中断处理未超过 2ms,且没有进程需要调度,且以上过程未重复执行超过 10次*/
153153
if (time_before(jiffies, end) && !need_resched() &&
154154
--max_restart)
155-
goto restart;
156-
155+
goto restart; /*仍然在软中断上下文中执行软中断*/
156+
/*否则唤醒 ksoftirqd 去执行剩下的软中断*/
157157
wakeup_softirqd();
158158
}
159159

kernel/RCU/stallwarn.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
* RCU是基于其原理命名的,Read-Copy Update
2+
* **Read** 指的是对于被 RCU 保护的共享数据,reader 可以直接访问,不需要获得任何锁;
3+
* **Copy Update** 指的是 writer 修改数据前首先拷贝一个副本,然后在副本上进行修改,修改完毕后向reclaimer(垃圾回收器)注册一个回调函数(callback),在适当的时机完成真正的修改操作 – 把原数据的指针重新指向新的被修改的数据,
4+
* 这里所说的适当的时机就是当既有的reader全都退出临界区的时候,而等待恰当时机的过程被称为 **grace period**
5+
* 在RCU机制中,writer 不需要和 reader 竞争任何锁,只在有多个 writer 的情况下它们之间需要某种锁进行同步作,如果写操作频繁的话RCU的性能会严重下降,所以 RCU 只适用于读多写少的情况。
6+
7+
* /sys/module/rcupdate/parameters/rcu_cpu_stall_suppress
8+
* /sys/module/rcupdate/parameters/rcu_cpu_stall_timeout
19

210
# 导致 RCU Warning 的原因
311
* CPU 在 RCU read-side 临界区内忙循环

kernel/debug/debug.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ static int __init loglevel(char *str)
139139
}
140140

141141
early_param("loglevel", loglevel);
142+
...*```
142143
```
143144
### 调整
144145
@@ -169,7 +170,7 @@ log_buf_len=n[KMG] Sets the size of the printk ring buffer,
169170
170171
## sysrq
171172
172-
* 开启sysrq特性
173+
* 开启 sysrq 特性
173174
```
174175
echo 1 > /proc/sys/kernel/sysrq
175176
```
@@ -190,6 +191,7 @@ log_buf_len=n[KMG] Sets the size of the printk ring buffer,
190191
...
191192
Jun 5 16:23:33 hostdomain kernel: [26953.774424] SysRq : HELP : loglevel(0-9) reboot(b) crash(c) terminate-all-tasks(e) memory-full-oom-kill(f) kill-all-tasks(i) thaw-filesystems(j) sak(k) show-backtrace-all-active-cpus(l) show-memory-usage(m) nice-all-RT-tasks(n) poweroff(o) show-registers(p) show-all-timers(q) unraw(r) sync(s) show-task-states(t) unmount(u) force-fb(V) show-blocked-tasks(w) dump-ftrace-buffer(z)
192193
```
194+
* sysrq 的键映射表见 drivers/tty/sysrq.c 的`struct sysrq_key_op *sysrq_key_table[]`数组
193195
194196
## git二分法查找
195197
```

kernel/mm/mm.md

Lines changed: 1 addition & 298 deletions
Original file line numberDiff line numberDiff line change
@@ -360,302 +360,6 @@ Need DMA-able memory, cannot sleep | Use `(GFP_DMA | GFP_ATOMIC)`, or perform yo
360360
361361
> **TLB (translation lookside buffer)** 是一种 **硬缓冲区**,很多体系结构用它来缓冲 **虚拟地址到物理地址的映射关系**。它极大地提高了系统性能,因为大多数内存都要进行虚拟寻址。
362362
363-
# slab层(Slab Layer)
364-
365-
* slab分配器扮演了 **通用数据结构缓存** 的角色。
366-
* slab分配器试图在几个基本原则之间寻求一种平衡:
367-
* 频繁使用的数据结构也会频繁分配和释放,因此应当缓存它们。
368-
* 频繁地分配和回收必然会导致内存碎片(难以找到大块连续可用的内存)。为了避免这种现象,空闲链表的缓存会连续地存放。因为已经释放的数据结构又会放回空闲链表,因此不会导致碎片。
369-
* 回收的对象可以立即投入下一次分配,因此,对于频繁的分配和释放,空闲链表能够提高其性能。
370-
* 如果分配器知道对象大小,页大小和总的高速缓存的大小这样的概念,它会作出更明智的决策。
371-
* 如果让部分缓存专属于单个处理器(对系统上的每个处理器独立而唯一),那么,分配和释放就可以在不加SMP锁的情况下运行。
372-
* 如果分配器是与NUMA相关的,它就可以从相同的内存结点为请求者进行分配。
373-
* 对存放的对象进行着色(color),以防止多个对象映射到相同的高速缓存行(cache line)。
374-
375-
## slab层的设计
376-
![mm_slab](pic/mm_slab_1.gif)
377-
* 不同对象划分为 **高速缓存组**,其中每个高速缓存组都存放 **不同类型** 的对象。**每种对象类型** 对应一个高速缓存。
378-
* `kmalloc()`接口建立在slab层之上,使用了一组通用高速缓存。
379-
* 高速缓存被划分为 **slab**,slab由一个或多个物理上连续的页组成。
380-
* 一般情况下,slab也就仅仅由一页组成。每个高速缓存可以由多个slab组成。
381-
* 每个slab都包含一些被缓存的数据结构。
382-
* 每个slab处于三种状态之一:满,部分满,空。
383-
* 当内核某一部分需要一个新对象时:
384-
* 先从 *部分满* 的slab中进行分配。
385-
* 没有 *部分满*,则从 *空* slab中进行分配。
386-
* 没有 *空* slab,则创建一个slab。
387-
* 注意 slabs_empty 列表中的 slab 是进行 **回收(reaping)** 的主要备选对象。正是通过此过程,slab 所使用的内存被返回给操作系统供其他用户使用。
388-
* slab 列表中的每个 slab 都是一个连续的内存块(一个或多个连续页),它们被划分成一个个对象。这些对象是从特定缓存中进行分配和释放的基本元素。
389-
* 注意 slab 是 slab 分配器进行操作的最小分配单位,因此如果需要对 slab 进行扩展,这也就是所扩展的最小值。
390-
* 由于对象是从 slab 中进行分配和释放的,因此单个 slab 可以在 slab 列表之间进行移动。
391-
* 例如,当一个 slab 中的所有对象都被使用完时,就从 slabs_partial 列表中移动到 slabs_full 列表中。
392-
* 当一个 slab 完全被分配并且有对象被释放后,就从 slabs_full 列表中移动到 slabs_partial 列表中。
393-
* 当所有对象都被释放之后,就从 slabs_partial 列表移动到 slabs_empty 列表中。
394-
395-
### slab 背后的动机
396-
与传统的内存管理模式相比, slab 缓存分配器提供了很多优点。
397-
* 首先,内核通常依赖于对小对象的分配,它们会在系统生命周期内进行无数次分配。slab 缓存分配器通过对类似大小的对象进行缓存而提供这种功能,从而避免了常见的碎片问题。
398-
* slab 分配器还支持通用对象的初始化,从而避免了为同一目而对一个对象重复进行初始化。
399-
* 最后,slab 分配器还可以支持硬件缓存对齐和着色,这允许不同缓存中的对象占用相同的缓存行,从而提高缓存的利用率并获得更好的性能。
400-
401-
#### 高速缓存结构 kmem_cache
402-
* 每个高速缓存都用`kmem_cache`结构表示。
403-
* include/linux/slab_def.h
404-
```c
405-
/*
406-
* Definitions unique to the original Linux SLAB allocator.
407-
*/
408-
409-
struct kmem_cache {
410-
/*per-cpu数据,每次分配/释放期间都会访问*/
411-
struct array_cache __percpu *cpu_cache;
412-
413-
/* 1) Cache tunables. Protected by slab_mutex */
414-
unsigned int batchcount;
415-
unsigned int limit;
416-
unsigned int shared;
417-
418-
unsigned int size;
419-
struct reciprocal_value reciprocal_buffer_size;
420-
/* 2) touched by every alloc & free from the backend */
421-
422-
unsigned int flags; /* constant flags */
423-
unsigned int num; /* # of objs per slab */
424-
425-
/* 3) cache_grow/shrink */
426-
/* order of pgs per slab (2^n) */
427-
unsigned int gfporder;
428-
429-
/* force GFP flags, e.g. GFP_DMA */
430-
gfp_t allocflags;
431-
432-
size_t colour; /* cache colouring range */
433-
unsigned int colour_off; /* colour offset */
434-
struct kmem_cache *freelist_cache;
435-
unsigned int freelist_size;
436-
437-
/* constructor func */
438-
void (*ctor)(void *obj);
439-
440-
/* 4) cache creation/removal */
441-
const char *name;
442-
struct list_head list;
443-
int refcount;
444-
int object_size;
445-
int align;
446-
447-
/* 5) statistics */
448-
...
449-
450-
struct kmem_cache_node *node[MAX_NUMNODES];
451-
};
452-
...
453-
```
454-
455-
#### 三个 slab 链表和 Per-CPU 的 array_cache
456-
457-
* mm/slab.h
458-
```c
459-
/*
460-
* struct array_cache
461-
*
462-
* Purpose:
463-
* - LIFO ordering, to hand out cache-warm objects from _alloc
464-
* - reduce the number of linked list operations
465-
* - reduce spinlock operations
466-
*
467-
* The limit is stored in the per-cpu structure to reduce the data cache
468-
* footprint.
469-
*
470-
*/
471-
struct array_cache {
472-
unsigned int avail;
473-
unsigned int limit;
474-
unsigned int batchcount;
475-
unsigned int touched;
476-
void *entry[]; /*
477-
* Must have this definition in here for the proper
478-
* alignment of array_cache. Also simplifies accessing
479-
* the entries.
480-
*/
481-
};
482-
...
483-
/*
484-
* The slab lists for all objects.
485-
*/
486-
struct kmem_cache_node {
487-
spinlock_t list_lock;
488-
489-
#ifdef CONFIG_SLAB
490-
struct list_head slabs_partial; /* partial list first, better asm code */
491-
struct list_head slabs_full;
492-
struct list_head slabs_free;
493-
unsigned long free_objects;
494-
unsigned int free_limit;
495-
unsigned int colour_next; /* Per-node cache coloring */
496-
struct array_cache *shared; /* shared per node */
497-
struct alien_cache **alien; /* on other nodes */
498-
unsigned long next_reap; /* updated without locking */
499-
int free_touched; /* updated without locking */
500-
#endif
501-
502-
#ifdef CONFIG_SLUB
503-
unsigned long nr_partial;
504-
struct list_head partial;
505-
#ifdef CONFIG_SLUB_DEBUG
506-
atomic_long_t nr_slabs;
507-
atomic_long_t total_objects;
508-
struct list_head full;
509-
#endif
510-
#endif
511-
512-
};
513-
```
514-
515-
* `kmem_getpages()`创建新的slab。
516-
* `kmem_getpages()``alloc_pages()`都会调用`__alloc_pages_nodemask()`来分配页。
517-
* `kmem_freepages()`释放内存。
518-
* slab层只有当给定高速缓存部分中即 **没有满****没有空** 的slab的时才会调用页分配函数。
519-
* 只有在下列情况下才会调用 *释放函数*
520-
* 当可用内存变得紧缺时,系统试图释放出更多的内存以供使用;
521-
* 当高速缓存显示撤销时。
522-
* slab层的管理是在每个高速缓存的基础上,通过提供给整个系统一个简单的接口来完成的。通过接口就可以:
523-
* 创建和撤销新的高速缓存。
524-
* 在高速缓存内分配和释放对象。
525-
* 创建一个高速缓存后,slab层所起的作用就像一个专用的分配器,可以为具体的对象类型进行分配。
526-
527-
#### __SetPageSlab()宏
528-
* include/linux/page-flags.h
529-
```c
530-
enum pageflags {
531-
PG_locked, /* Page is locked. Don't touch. */
532-
PG_error,
533-
PG_referenced,
534-
PG_uptodate,
535-
PG_dirty,
536-
PG_lru,
537-
PG_active,
538-
PG_slab,
539-
...
540-
};
541-
...
542-
static inline struct page *compound_head(struct page *page)
543-
{
544-
unsigned long head = READ_ONCE(page->compound_head);
545-
546-
if (unlikely(head & 1))
547-
return (struct page *) (head - 1);
548-
return page;
549-
}
550-
551-
static __always_inline int PageTail(struct page *page)
552-
{
553-
return READ_ONCE(page->compound_head) & 1;
554-
}
555-
...
556-
/*
557-
* Page flags policies wrt compound pages
558-
*
559-
* PF_ANY:
560-
* the page flag is relevant for small, head and tail pages.
561-
*
562-
* PF_HEAD:
563-
* for compound page all operations related to the page flag applied to
564-
* head page.
565-
*
566-
* PF_NO_TAIL:
567-
* modifications of the page flag must be done on small or head pages,
568-
* checks can be done on tail pages too.
569-
*
570-
* PF_NO_COMPOUND:
571-
* the page flag is not relevant for compound pages.
572-
*/
573-
#define PF_ANY(page, enforce) page
574-
#define PF_HEAD(page, enforce) compound_head(page)
575-
#define PF_NO_TAIL(page, enforce) ({ \
576-
VM_BUG_ON_PGFLAGS(enforce && PageTail(page), page); \
577-
compound_head(page);})
578-
#define PF_NO_COMPOUND(page, enforce) ({ \
579-
VM_BUG_ON_PGFLAGS(enforce && PageCompound(page), page); \
580-
page;})
581-
582-
/*
583-
* Macros to create function definitions for page flags
584-
*/
585-
#define TESTPAGEFLAG(uname, lname, policy) \
586-
static __always_inline int Page##uname(struct page *page) \
587-
{ return test_bit(PG_##lname, &policy(page, 0)->flags); }
588-
...
589-
#define __SETPAGEFLAG(uname, lname, policy) \
590-
static __always_inline void __SetPage##uname(struct page *page) \
591-
{ __set_bit(PG_##lname, &policy(page, 1)->flags); }
592-
593-
#define __CLEARPAGEFLAG(uname, lname, policy) \
594-
static __always_inline void __ClearPage##uname(struct page *page) \
595-
{ __clear_bit(PG_##lname, &policy(page, 1)->flags); }
596-
...
597-
#define __PAGEFLAG(uname, lname, policy) \
598-
TESTPAGEFLAG(uname, lname, policy) \
599-
__SETPAGEFLAG(uname, lname, policy) \
600-
__CLEARPAGEFLAG(uname, lname, policy)
601-
602-
...
603-
__PAGEFLAG(Slab, slab, PF_NO_TAIL)
604-
```
605-
* 宏展开后的函数定义
606-
```c
607-
/*__ arch/x86/include/asm/bitops.h*/
608-
/**
609-
* __set_bit - Set a bit in memory
610-
* @nr: the bit to set
611-
* @addr: the address to start counting from
612-
*
613-
* Unlike set_bit(), this function is non-atomic and may be reordered.
614-
* If it's called on the same region of memory simultaneously, the effect
615-
* may be that only one operation succeeds.
616-
*/
617-
static __always_inline void __set_bit(long nr, volatile unsigned long *addr)
618-
{
619-
asm volatile("bts %1,%0" : ADDR : "Ir" (nr) : "memory");
620-
}
621-
622-
static __always_inline void __SetPageSlab(struct page *page)
623-
{ __set_bit(PG_slab, &({
624-
do {
625-
if (unlikely(1 && PageTail(page))) {
626-
dump_page(page, "VM_BUG_ON_PAGE(" __stringify(1 && PageTail(page))")");
627-
BUG();
628-
}
629-
} while (0);
630-
compound_head(page);})->flags); }
631-
```
632-
633-
## slab分配器的接口
634-
635-
* `kmem_cache_create()`创建一个新的高速缓存。
636-
* `SLAB_HWCACHE_LINE` slab层把一个slab内的所有对象和硬件cache line对齐。可以提高性能,但增加了内存开销,空间换时间。
637-
* `SLAB_POISON` 内存毒化标志,用已知的值填充slab。
638-
* `SLAB_RED_ZONE` 在已分配的内存周围插入“红色警界区”以探测缓冲越界。
639-
* `SLAB_PANIC` 分配失败时提醒slab层。这在要求分配只能成功的时候非常有用。
640-
* `SLAB_CACHE_DMA` 命令slab层用 *可以执行DMA的内存* 给每个slab分配空间。
641-
* 只在 *分配的对象用于DMA*,且 *必须驻留在`ZONE_DMA`区`时* 才需要这个标志。
642-
* 否则不需要,也不应该设置。
643-
* **不能用于中断上下文**,会睡眠。
644-
* `kmem_cache_destory()`撤销给定的高速缓存。
645-
* **不能用于中断上下文**,会睡眠。
646-
* 除此之外还需确保:
647-
* 高速缓存中所有的slab都必须为空。
648-
* 在调用`kmem_cache_destory()`过程中(更别提之后了)不再访问这个高速缓存。调用者必须确保同步。
649-
* `kmem_cache_alloc()`从给定的高速缓存中分配对象。
650-
* 如果高速缓存中所有的slab中都没有空闲对象,slab层必须通过`kmem_getpages()`获取新的页。
651-
* `kmem_cache_free()`释放一个对象,并返还给原先的slab。
652-
* slab层负责内存紧缺情况下所有底层的对齐,着色,分配,释放,回收等。
653-
* 如果要频繁创建很多相同类型的对象,应该考虑使用slab高速缓存,而不是自己实现空闲链表。
654-
655-
## 查看slab信息
656-
* [`cat /proc/slabinfo`](http://man7.org/linux/man-pages/man5/slabinfo.5.html)
657-
* [`slbtop`](http://man7.org/linux/man-pages/man1/slabtop.1.html)
658-
659363
# 栈上的静态分配
660364
* 每个进程的 **内核栈** 大小即依赖 *体系结构*,也与 *编译选项* 有关。
661365
* 历史上,每个进程都有 **两页** 的内核栈。
@@ -822,9 +526,8 @@ static inline void __kunmap_atomic(void *addr)
822526
* 注意:**不能在访问 Per-CPU 数据过程中睡眠**,否则,醒来可能在其他CPU上。
823527
* Per-CPU 的新接口并不兼容之前的内核。
824528
825-
826529
# 参考资料
827-
* https://www.ibm.com/developerworks/cn/linux/l-linux-slab-allocator/
530+
* [Linux slab 分配器剖析](https://www.ibm.com/developerworks/cn/linux/l-linux-slab-allocator/)
828531
* [/PROC/MEMINFO之谜](http://linuxperf.com/?p=142)
829532
* [怎样诊断SLAB泄露问题](http://linuxperf.com/?p=148)
830533
* [Linux内核高端内存](http://ilinuxkernel.com/?p=1013)

kernel/mm/pic/SLAB-DS.png

69.7 KB
Loading

kernel/mm/pic/SLUB-DS.png

61.7 KB
Loading

kernel/mm/pic/slab_bufctl.jpg

19.7 KB
Loading

0 commit comments

Comments
 (0)