基本介绍
Linux内核补丁“[v3,00/57] Scope-based Resource Management”向内核增加了资源自动释放
的机制,“小范围”内成对使用的资源可以直接使用这种机制,比如成对使用的内存申请、成对
使用的加锁和释放锁操作。
这个特性的原理其实比较直白,就是使用了GCC里的cleanup属性,在定义变量的时候cleanup
属性容许为这个变量添加一个函数,GCC编译的时候在这个变量生命周期结束的地方插入对应
的函数调用。
一个简单的C代码的示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include <stdio.h>
void print(int *t) { printf("cleanup: %d\n", *t); }
void do_something(void) { printf("do something\n"); }
int main() { printf("main function start\n"); int a __attribute__((__cleanup__(print))) = 10;
for (int i = 0; i < 3; i++) { int b __attribute__((__cleanup__(print))) = i; do_something(); }
return 0; }
|
编译运行的输出是:
1 2 3 4 5 6 7 8 9
| sherlock@m1:~/tests/cleanup$ ./a.out main function start do something cleanup: 0 do something cleanup: 1 do something cleanup: 2 cleanup: 10
|
可以看出,a的生命周期是整个main函数,所以在最后调用print,b的生命周期是一次for循
环,所以for循环里,每次在do_something之后都会调用print。
Linux内核用宏封装了如上特性,include cleanup.h后就可以直接使用。比如,我们可以把
kernel/sched/core.c的sched_getaffinity中原来保护cpumask_and()的锁的写法改成:
1 2
| guard(raw_spinlock_irqsave)(&p->pi_lock); cpumask_and(mask, &p->cpus_mask, cpu_active_mask);
|
原理分析
内核在include/linux/cleanup.h定义各种宏来封装cleanup属性,在各个头文件中,比如锁
的头文件中,使用cleanup.h中的宏定义class类型。所以,具体使用的时候,只要在文件里
include对应资源的头文件和cleanup.h就好。在需要使用cleanup的地方就可以直接用宏静态
定义对应class类型的对象变量。
直接看cleanup.h中的宏会有点不直观。我们可以在编译内核或者驱动的时候加上V=1,这样
会打印出详细的编译命令,然后我们把对应的编译命令改成只做预编译,再次单独运行对应
的编译命令,得到的预编译后的文件看上去会比较直观。
我们以一个简单的驱动编译为例看下,这个驱动的具体内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #include <linux/kernel.h> #include <linux/module.h> #include <linux/cleanup.h>
MODULE_LICENSE("Dual BSD/GPL");
static int __init busyloop_init(void) { spinlock_t lock; int tmp;
spin_lock_init(&lock);
guard(spinlock_irq)(&lock); while (1) { tmp++; }
return 0; }
static void __exit busyloop_exit(void) {}
module_init(busyloop_init); module_exit(busyloop_exit);
MODULE_AUTHOR("Sherlock"); MODULE_DESCRIPTION("The driver is for testing soft lockup");
|
加V=1编译的输出如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| sherlock@m1:~/tests/softlockup$ make V=1 -C ~/repos/linux M=`pwd` modules make: Entering directory '/home/sherlock/repos/linux' make --no-print-directory -C /home/sherlock/repos/linux \ -f /home/sherlock/repos/linux/Makefile modules make -f ./scripts/Makefile.build obj=/home/sherlock/tests/softlockup need-builtin=1 need-modorder=1 # CC [M] /home/sherlock/tests/softlockup/busyloop.o gcc -Wp,-MMD,/home/sherlock/tests/softlockup/.busyloop.o.d -nostdinc -I./arch/arm64/include -I./arch/arm64/include/generated -I./include -I./arch/arm64/include/uapi -I./arch/arm64/include/generated/uapi -I./include/uapi -I./include/generated/uapi -include ./include/linux/compiler-version.h -include ./include/linux/kconfig.h -include ./include/linux/compiler_types.h -D__KERNEL__ -mlittle-endian -DKASAN_SHADOW_SCALE_SHIFT= -fmacro-prefix-map=./= -std=gnu11 -fshort-wchar -funsigned-char -fno-common -fno-PIE -fno-strict-aliasing-mgeneral-regs-only -DCONFIG_CC_HAS_K_CONSTRAINT=1 -Wno-psabi -mabi=lp64 -fno-asynchronous-unwind-tables -fno-unwind-tables -mbranch-protection=pac-ret -Wa,-march=armv8.5-a -DARM64_ASM_ARCH='"armv8.5-a"' -DKASAN_SHADOW_SCALE_SHIFT= -fno-delete-null-pointer-checks -O2 --param=allow-store-data-races=0 -fstack-protector-strong -fno-omit-frame-pointer -fno-optimize-sibling-calls -fno-stack-clash-protection -falign-functions=4 -fno-strict-overflow -fno-stack-check -fconserve-stack -Wall -Wundef -Werror=implicit-function-declaration -Werror=implicit-int -Werror=return-type -Werror=strict-prototypes -Wno-format-security -Wno-trigraphs -Wno-frame-address -Wno-address-of-packed-member -Wmissing-declarations -Wmissing-prototypes -Wframe-larger-than=2048 -Wno-main -Wvla -Wno-pointer-sign -Wcast-function-type -Wno-stringop-overflow -Wno-array-bounds -Wno-alloc-size-larger-than -Wimplicit-fallthrough=5 -Werror=date-time -Werror=incompatible-pointer-types -Werror=designated-init -Wextra -Wunused -Wno-unused-but-set-variable -Wno-unused-const-variable -Wno-packed-not-aligned -Wno-format-overflow -Wno-format-truncation -Wno-stringop-truncation -Wno-override-init -Wno-missing-field-initializers -Wno-type-limits -Wno-shift-negative-value -Wno-maybe-uninitialized -Wno-sign-compare -Wno-unused-parameter -g -fno-var-tracking -femit-struct-debug-baseonly -mstack-protector-guard=sysreg -mstack-protector-guard-reg=sp_el0 -mstack-protector-guard-offset=1224 -DMODULE -DKBUILD_BASENAME='"busyloop"' -DKBUILD_MODNAME='"busyloop"' -D__KBUILD_MODNAME=kmod_busyloop -c -o /home/sherlock/tests/softlockup/busyloop.o /home/sherlock/tests/softlockup/busyloop.c # cmd_gen_order /home/sherlock/tests/softlockup/modules.order { echo /home/sherlock/tests/softlockup/busyloop.o; :; } > /home/sherlock/tests/softlockup/modules.order sh ./scripts/modules-check.sh /home/sherlock/tests/softlockup/modules.order make -f ./scripts/Makefile.modpost # MODPOST /home/sherlock/tests/softlockup/Module.symvers scripts/mod/modpost -M -o /home/sherlock/tests/softlockup/Module.symvers -T /home/sherlock/tests/softlockup/modules.order -i Module.symvers -e make -f ./scripts/Makefile.modfinal # CC [M] /home/sherlock/tests/softlockup/busyloop.mod.o gcc -Wp,-MMD,/home/sherlock/tests/softlockup/.busyloop.mod.o.d -nostdinc -I./arch/arm64/include -I./arch/arm64/include/generated -I./include -I./arch/arm64/include/uapi -I./arch/arm64/include/generated/uapi -I./include/uapi -I./include/generated/uapi -include ./include/linux/compiler-version.h -include ./include/linux/kconfig.h -include ./include/linux/compiler_types.h -D__KERNEL__ -mlittle-endian -DKASAN_SHADOW_SCALE_SHIFT= -fmacro-prefix-map=./= -std=gnu11 -fshort-wchar -funsigned-char -fno-common -fno-PIE -fno-strict-aliasing -mgeneral-regs-only -DCONFIG_CC_HAS_K_CONSTRAINT=1 -Wno-psabi -mabi=lp64 -fno-asynchronous-unwind-tables -fno-unwind-tables -mbranch-protection=pac-ret -Wa,-march=armv8.5-a -DARM64_ASM_ARCH='"armv8.5-a"' -DKASAN_SHADOW_SCALE_SHIFT= -fno-delete-null-pointer-checks -O2 --param=allow-store-data-races=0 -fstack-protector-strong -fno-omit-frame-pointer -fno-optimize-sibling-calls -fno-stack-clash-protection -falign-functions=4 -fno-strict-overflow -fno-stack-check -fconserve-stack -Wall -Wundef -Werror=implicit-function-declaration -Werror=implicit-int -Werror=return-type -Werror=strict-prototypes -Wno-format-security -Wno-trigraphs -Wno-frame-address -Wno-address-of-packed-member -Wmissing-declarations -Wmissing-prototypes -Wframe-larger-than=2048 -Wno-main -Wvla -Wno-pointer-sign -Wcast-function-type -Wno-stringop-overflow -Wno-array-bounds -Wno-alloc-size-larger-than -Wimplicit-fallthrough=5 -Werror=date-time -Werror=incompatible-pointer-types -Werror=designated-init -Wextra -Wunused -Wno-unused-but-set-variable -Wno-unused-const-variable -Wno-packed-not-aligned -Wno-format-overflow -Wno-format-truncation -Wno-stringop-truncation -Wno-override-init -Wno-missing-field-initializers -Wno-type-limits -Wno-shift-negative-value -Wno-maybe-uninitialized -Wno-sign-compare -Wno-unused-parameter -g -fno-var-tracking -femit-struct-debug-baseonly -mstack-protector-guard=sysreg -mstack-protector-guard-reg=sp_el0 -mstack-protector-guard-offset=1224 -DMODULE -DKBUILD_BASENAME='"busyloop.mod"' -DKBUILD_MODNAME='"busyloop"' -D__KBUILD_MODNAME=kmod_busyloop -c -o /home/sherlock/tests/softlockup/busyloop.mod.o /home/sherlock/tests/softlockup/busyloop.mod.c # LD [M] /home/sherlock/tests/softlockup/busyloop.ko ld -r -EL -maarch64elf -z noexecstack --build-id=sha1 -T scripts/module.lds -o /home/sherlock/tests/softlockup/busyloop.ko /home/sherlock/tests/softlockup/busyloop.o /home /sherlock/tests/softlockup/busyloop.mod.o make: Leaving directory '/home/sherlock/repos/linux'
|
我们进入内核源码路径,GCC的参数加上-E, 重新指定输出文件:
1
| gcc -E -Wp,-MMD,/home/sherlock/tests/softlockup/.busyloop.o.d -nostdinc -I./arch/arm64/include -I./arch/arm64/include/generated -I./include -I./arch/arm64/include/uapi -I./arch/arm64/include/generated/uapi -I./include/uapi -I./include/generated/uapi -include ./include/linux/compiler-version.h -include ./include/linux/kconfig.h -include ./include/linux/compiler_types.h -D__KERNEL__ -mlittle-endian -DKASAN_SHADOW_SCALE_SHIFT= -fmacro-prefix-map=./= -std=gnu11 -fshort-wchar -funsigned-char -fno-common -fno-PIE -fno-strict-aliasing-mgeneral-regs-only -DCONFIG_CC_HAS_K_CONSTRAINT=1 -Wno-psabi -mabi=lp64 -fno-asynchronous-unwind-tables -fno-unwind-tables -mbranch-protection=pac-ret -Wa,-march=armv8.5-a -DARM64_ASM_ARCH='"armv8.5-a"' -DKASAN_SHADOW_SCALE_SHIFT= -fno-delete-null-pointer-checks -O2 --param=allow-store-data-races=0 -fstack-protector-strong -fno-omit-frame-pointer -fno-optimize-sibling-calls -fno-stack-clash-protection -falign-functions=4 -fno-strict-overflow -fno-stack-check -fconserve-stack -Wall -Wundef -Werror=implicit-function-declaration -Werror=implicit-int -Werror=return-type -Werror=strict-prototypes -Wno-format-security -Wno-trigraphs -Wno-frame-address -Wno-address-of-packed-member -Wmissing-declarations -Wmissing-prototypes -Wframe-larger-than=2048 -Wno-main -Wvla -Wno-pointer-sign -Wcast-function-type -Wno-stringop-overflow -Wno-array-bounds -Wno-alloc-size-larger-than -Wimplicit-fallthrough=5 -Werror=date-time -Werror=incompatible-pointer-types -Werror=designated-init -Wextra -Wunused -Wno-unused-but-set-variable -Wno-unused-const-variable -Wno-packed-not-aligned -Wno-format-overflow -Wno-format-truncation -Wno-stringop-truncation -Wno-override-init -Wno-missing-field-initializers -Wno-type-limits -Wno-shift-negative-value -Wno-maybe-uninitialized -Wno-sign-compare -Wno-unused-parameter -g -fno-var-tracking -femit-struct-debug-baseonly -mstack-protector-guard=sysreg -mstack-protector-guard-reg=sp_el0 -mstack-protector-guard-offset=1224 -DMODULE -DKBUILD_BASENAME='"busyloop"' -DKBUILD_MODNAME='"busyloop"' -D__KBUILD_MODNAME=kmod_busyloop -c -o /home/sherlock/tests/softlockup/busyloop.i /home/sherlock/tests/softlockup/busyloop.c
|
可以看到在busyloop.i中展开guard()的结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| [...]
typedef struct { spinlock_t *lock; ; } class_spinlock_irq_t; static inline __attribute__((__gnu_inline__)) __attribute__((__unused__)) __attribute__((__no_instrument_function__)) void class_spinlock_irq_destructor(class_spinlock_irq_t *_T) { if (_T->lock) { spin_unlock_irq(_T->lock); } } static inline __attribute__((__gnu_inline__)) __attribute__((__unused__)) __attribute__((__no_instrument_function__)) void *class_spinlock_irq_lock_ptr(class_spinlock_irq_t *_T) { return _T->lock; } static inline __attribute__((__gnu_inline__)) __attribute__((__unused__)) __attribute__((__no_instrument_function__)) class_spinlock_irq_t class_spinlock_irq_constructor(spinlock_t *l) { class_spinlock_irq_t _t = { .lock = l }, *_T = &_t; spin_lock_irq(_T->lock); return _t; }
[...]
static int __attribute__((__section__(".init.text"))) busyloop_init(void) { spinlock_t lock; int tmp;
do { spinlock_check(&lock); *(&lock) = (spinlock_t) { { .rlock = { .raw_lock = { { .val = { (0) } } }, } } }; } while (0);
class_spinlock_irq_t __UNIQUE_ID_guard339 __attribute__((__cleanup__(class_spinlock_irq_destructor))) = class_spinlock_irq_constructor(&lock); while (1) { tmp++; }
return 0; }
[...]
|
从上面的宏展开可以看到,guard静态定义了一个类型为class_spinlock_irq_t的匿名对象,
在初始化函数里把锁指针传入对象中,并调用下加锁函数。在cleanup函数中获得锁指针,并
在cleanup函数中调用释放锁的函数。因为匿名对象的生命周期是busyloop_init这个函数,
所以在这个函数结束的时候会调用cleanup函数。
如果busyloop_init变成了如下,只想对tmp和tmp1上锁,那么就需要使用scoped_guard宏,
所示如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| static int __init busyloop_init(void) { spinlock_t lock; spinlock_t s_lock; int tmp, tmp1, tmp2;
spin_lock_init(&lock);
guard(spinlock_irq)(&lock); while (1) { scoped_guard(spinlock_irq, &s_lock) { tmp++; tmp1++; } tmp2++; }
return 0; }
|
宏展开是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| static int __attribute__((__section__(".init.text"))) busyloop_init(void) { spinlock_t lock; spinlock_t s_lock; int tmp, tmp1, tmp2;
do { spinlock_check(&lock); *(&lock) = (spinlock_t) { { .rlock = { .raw_lock = { { .val = { (0) } } }, } } }; } while (0);
class_spinlock_irq_t __UNIQUE_ID_guard339 __attribute__((__cleanup__(class_spinlock_irq_destructor))) = class_spinlock_irq_constructor(&lock); while (1) { for (class_spinlock_irq_t scope __attribute__((__cleanup__(class_spinlock_irq_destructor))) = class_spinlock_irq_constructor(&s_lock), *done = ((void *)0); class_spinlock_irq_lock_ptr(&scope) && !done; done = (void *)1) { tmp++; tmp1++; } tmp2++; }
return 0; }
|
如果scoped_guard这里换成guard,会变成在每次while循环中对tmp/tmp1/tmp2加锁和释放锁。