Sholck

不积跬步,无以至千里.不积小流,无以成江海

0%

linux-likely学习

linux-likely学习

背景

在日常的工作和学习中,经常发现likely和unlikely的使用,在通知链节点注册时,有如下函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static int notifier_chain_register(struct notifier_block **nl,                                                                                                                                                      
struct notifier_block *n)
{
while ((*nl) != NULL) {
if (unlikely((*nl) == n)) {
WARN(1, "notifier callback %ps already registered",
n->notifier_call);
return -EEXIST;
}
if (n->priority > (*nl)->priority)
break;
nl = &((*nl)->next);
}
n->next = *nl;
rcu_assign_pointer(*nl, n);
return 0;
}

这里通过遍历节点和插入节点地址对比,防止程序运行时重复插入通知链节点。当然,重复插入这种情况下是非常少见的,因此编译器针对此进行了性能优化。

实现

1
2
3
//include/linux/compiler.h
# define likely(x) __builtin_expect(!!(x), 1)
# define unlikely(x) __builtin_expect(!!(x), 0)

内核未见__builtin_expect的实现,为编译器实现,称为内建函数,最后深入研究

原理

处理器的流水线处理,处理器在取指,执行等都是并行执行的,提前取指可以提高性能,但是提前取指是一个预测的过程,如果预测错误,会导致branch miss,需要重新取指(属于跳转取指,但是跳转取指是比较耗时的),这种情况反而会导致性能降低。

编译器可以针对代码情况进行取指优化,如果大部分情况都是A,即(likely(A)),那么将A情况下的指令取指提前到条件指令后面,如果大部分都不是A,即(unlikely(A)), 那么将其他情况的指令提前到条件指令后面。

实践

通过代码汇编指令对比检查优化逻辑,汇编有两种方式:

  1. 通过objdump进行汇编
  2. gdb通过disassemble指令进行汇编

编译环境

需要确定kernel的编译环境打开了编译器优化

可以发现编译器优化等级为-O2

1
2
3
4
5
//Makefile
$(info KBUILD_CFLAGS is $(KBUILD_CFLAGS))

➜ 2-likely git:(master) ✗ make
KBUILD_CFLAGS is -Wall ... -std=gnu89 ... -O2 ... -g -gdwarf-4

修改编译优化等级有两种方式

  1. 修改linux Makefile编译方式
1
2
3
4
5
6
7
//Makefile
ifdef CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE >>默认
KBUILD_CFLAGS += -O2
else ifdef CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE_O3
KBUILD_CFLAGS += -O3
else ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
KBUILD_CFLAGS += -Os
  1. 增加模块编译参数
1
2
//驱动模块下Makefile
EXTRA_CFLAGS = -Wall -g -O2

代码

在代码设计时,要注意三点

  1. 确保变量不能是定值,比如a = 1; if(likely(a == 1)),因此选择使用进程号
  2. 要确保不同条件下的指令有明显差异,不能都是printk,不然只能看到汇编流程上je和jne的差异,因此选择a++和a–。
  3. 因为使用gdb通过disassemble指令进行汇编,因此需要汇编的函数需要导出symbol,可以通过nm确认
test-A.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
25
26
27
28
29
30
31
//Makefile
MODULE_SRCS += likely.c

MODULE_OBJS += $(wildcard *.o) \
$(wildcard *.ko) \
$(wildcard *.mod) \
$(wildcard *.mod.c)

OUT_FILES += modules.order \
Module.symvers

#$(info "OBJS is $(MODULE_OBJS)")

obj-m += likely.o
module-objs := likely.o
#EXTRA_CFLAGS = -Wall -g -O2 >>这里也可以设置编译器优化等级

#The path of kernel code
KDIR := /github/linux
PWD ?= $(shell pwd)

build: kernel_modules
kernel_modules:
make -C $(KDIR) M=$(PWD) modules

#$(info EXTRA_CFLAGS is $(EXTRA_CFLAGS))
$(info KBUILD_CFLAGS is $(KBUILD_CFLAGS))

clean:
rm -rf $(MODULE_OBJS)
rm -rf $(OUT_FILES)
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
//likely.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>

MODULE_LICENSE("Dual BSD/GPL");

static struct task_struct *p;
static int a = 0;

void test(void) {
if((p->pid % 2 ) == 0) {
a++;
// printk(KERN_ALERT "pid is even\n");
}
else {
a--;
// printk(KERN_ALERT "pid is odd\n");
}
}
EXPORT_SYMBOL(test);

void test_likely(void) {
if(likely((p->pid % 2) == 0)) {
a++;
// printk(KERN_ALERT "pid is even\n");
}
else {
a--;
//printk(KERN_ALERT "pid is odd\n");
}
}
EXPORT_SYMBOL(test_likely);

void test_unlikely(void) {
if(unlikely((p->pid % 2) == 0)) {
a++;
//printk(KERN_ALERT "pid is even\n");
}
else {
a--;
//printk(KERN_ALERT "pid is odd\n");
}
}
EXPORT_SYMBOL(test_unlikely);

static int likely_init(void)
{
p = current;
printk(KERN_ALERT "Hello, world\n");
test();
test_likely();
test_unlikely();
return 0;
}

static void likely_exit(void)
{
printk(KERN_ALERT "Goodbey, cruel world\n");
}

module_init(likely_init);
module_exit(likely_exit);

objdump汇编

objdump汇编检查差异

-O2

-O2优化的情况下,unlikely将else的指令提前到条件指令后边

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//-O2
➜ 2-likely git:(master) ✗ objdump -d likely.ko

likely.ko: 文件格式 elf64-x86-64

Disassembly of section .text:

0000000000000000 <test_likely>:
0: e8 00 00 00 00 callq 5 <test_likely+0x5>
5: 48 8b 05 00 00 00 00 mov 0x0(%rip),%rax # c <test_likely+0xc>
c: f6 80 c0 05 00 00 01 testb $0x1,0x5c0(%rax)
13: 75 08 jne 1d <test_likely+0x1d> 如果不相等,那么跳转到1d, 即a--
15: 83 05 00 00 00 00 01 addl $0x1,0x0(%rip) # 1c <test_likely+0x1c> >>addl指令在前面
1c: c3 retq
1d: 83 2d 00 00 00 00 01 subl $0x1,0x0(%rip) # 24 <test_likely+0x24>
24: c3 retq
25: 90 nop
26: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
2d: 00 00 00

0000000000000030 <test_unlikely>:
30: e8 00 00 00 00 callq 35 <test_unlikely+0x5>
35: 48 8b 05 00 00 00 00 mov 0x0(%rip),%rax # 3c <test_unlikely+0xc>
3c: f6 80 c0 05 00 00 01 testb $0x1,0x5c0(%rax)
43: 74 08 je 4d <test_unlikely+0x1d> 如果相等,那么跳转到4d, 即a++
45: 83 2d 00 00 00 00 01 subl $0x1,0x0(%rip) # 4c <test_unlikely+0x1c> >>subl指令在前面
4c: c3 retq
4d: 83 05 00 00 00 00 01 addl $0x1,0x0(%rip) # 54 <test_unlikely+0x24>
54: c3 retq
55: 90 nop
56: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
5d: 00 00 00

0000000000000060 <test>:
60: e8 00 00 00 00 callq 65 <test+0x5>
65: 48 8b 05 00 00 00 00 mov 0x0(%rip),%rax # 6c <test+0xc>
6c: f6 80 c0 05 00 00 01 testb $0x1,0x5c0(%rax)
73: 74 08 je 7d <test+0x1d>
75: 83 2d 00 00 00 00 01 subl $0x1,0x0(%rip) # 7c <test+0x1c> >>为假subl
7c: c3 retq
7d: 83 05 00 00 00 00 01 addl $0x1,0x0(%rip) # 84 <test+0x24> >>为真addl
84: c3 retq
85: 90 nop
86: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
8d: 00 00 00

...

-O0

不开默认编译优化的情况下,发现指令数目明显增多

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
000000000000002f <test>:
2f: e8 00 00 00 00 callq 34 <test+0x5>
34: 55 push %rbp
35: 48 89 e5 mov %rsp,%rbp
38: 48 8b 05 00 00 00 00 mov 0x0(%rip),%rax # 3f <test+0x10>
3f: 8b 80 c0 05 00 00 mov 0x5c0(%rax),%eax
45: 83 e0 01 and $0x1,%eax
48: 85 c0 test %eax,%eax
4a: 75 11 jne 5d <test+0x2e> >>为假跳到5d
4c: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # 52 <test+0x23> >>为真add
52: 83 c0 01 add $0x1,%eax
55: 89 05 00 00 00 00 mov %eax,0x0(%rip) # 5b <test+0x2c>
5b: eb 0f jmp 6c <test+0x3d>
5d: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # 63 <test+0x34>
63: 83 e8 01 sub $0x1,%eax >>为假sub
66: 89 05 00 00 00 00 mov %eax,0x0(%rip) # 6c <test+0x3d>
6c: 90 nop
6d: 5d pop %rbp
6e: c3 retq

000000000000006f <test_likely>:
6f: e8 00 00 00 00 callq 74 <test_likely+0x5>
74: 55 push %rbp
75: 48 89 e5 mov %rsp,%rbp
78: 48 8b 05 00 00 00 00 mov 0x0(%rip),%rax # 7f <test_likely+0x10>
7f: 8b 80 c0 05 00 00 mov 0x5c0(%rax),%eax
85: 83 e0 01 and $0x1,%eax
88: 85 c0 test %eax,%eax
8a: 0f 94 c0 sete %al
8d: 0f b6 c0 movzbl %al,%eax
90: 48 85 c0 test %rax,%rax
93: 74 11 je a6 <test_likely+0x37>
95: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # 9b <test_likely+0x2c>
9b: 83 c0 01 add $0x1,%eax
9e: 89 05 00 00 00 00 mov %eax,0x0(%rip) # a4 <test_likely+0x35>
a4: eb 0f jmp b5 <test_likely+0x46>
a6: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # ac <test_likely+0x3d>
ac: 83 e8 01 sub $0x1,%eax
af: 89 05 00 00 00 00 mov %eax,0x0(%rip) # b5 <test_likely+0x46>
b5: 90 nop
b6: 5d pop %rbp
b7: c3 retq

00000000000000b8 <test_unlikely>:
b8: e8 00 00 00 00 callq bd <test_unlikely+0x5>
bd: 55 push %rbp
be: 48 89 e5 mov %rsp,%rbp
c1: 48 8b 05 00 00 00 00 mov 0x0(%rip),%rax # c8 <test_unlikely+0x10>
c8: 8b 80 c0 05 00 00 mov 0x5c0(%rax),%eax
ce: 83 e0 01 and $0x1,%eax
d1: 85 c0 test %eax,%eax
d3: 0f 94 c0 sete %al
d6: 0f b6 c0 movzbl %al,%eax
d9: 48 85 c0 test %rax,%rax
dc: 74 11 je ef <test_unlikely+0x37>
de: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # e4 <test_unlikely+0x2c>
e4: 83 c0 01 add $0x1,%eax
e7: 89 05 00 00 00 00 mov %eax,0x0(%rip) # ed <test_unlikely+0x35>
ed: eb 0f jmp fe <test_unlikely+0x46>
ef: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # f5 <test_unlikely+0x3d>
f5: 83 e8 01 sub $0x1,%eax
f8: 89 05 00 00 00 00 mov %eax,0x0(%rip) # fe <test_unlikely+0x46>
fe: 90 nop
ff: 5d pop %rbp
100: c3 retq

-O1

-O1 的情况下指令明显变少,同时发现和-O2的打印基本一致,思考为什么?

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
29
30
31
32
33
34
likely.ko:     文件格式 elf64-x86-64


Disassembly of section .text:

0000000000000000 <test>:hui bian
0: e8 00 00 00 00 callq 5 <test+0x5>
5: 48 8b 05 00 00 00 00 mov 0x0(%rip),%rax # c <test+0xc>
c: f6 80 c0 05 00 00 01 testb $0x1,0x5c0(%rax)
13: 74 08 je 1d <test+0x1d>
15: 83 2d 00 00 00 00 01 subl $0x1,0x0(%rip) # 1c <test+0x1c>
1c: c3 retq
1d: 83 05 00 00 00 00 01 addl $0x1,0x0(%rip) # 24 <test+0x24>
24: c3 retq

0000000000000025 <test_likely>:
25: e8 00 00 00 00 callq 2a <test_likely+0x5>
2a: 48 8b 05 00 00 00 00 mov 0x0(%rip),%rax # 31 <test_likely+0xc>
31: f6 80 c0 05 00 00 01 testb $0x1,0x5c0(%rax)
38: 75 08 jne 42 <test_likely+0x1d>
3a: 83 05 00 00 00 00 01 addl $0x1,0x0(%rip) # 41 <test_likely+0x1c>
41: c3 retq
42: 83 2d 00 00 00 00 01 subl $0x1,0x0(%rip) # 49 <test_likely+0x24>
49: c3 retq

000000000000004a <test_unlikely>:
4a: e8 00 00 00 00 callq 4f <test_unlikely+0x5>
4f: 48 8b 05 00 00 00 00 mov 0x0(%rip),%rax # 56 <test_unlikely+0xc>
56: f6 80 c0 05 00 00 01 testb $0x1,0x5c0(%rax)
5d: 74 08 je 67 <test_unlikely+0x1d>
5f: 83 2d 00 00 00 00 01 subl $0x1,0x0(%rip) # 66 <test_unlikely+0x1c>
66: c3 retq
67: 83 05 00 00 00 00 01 addl $0x1,0x0(%rip) # 6e <test_unlikely+0x24>
6e: c3 retq

gdb查看汇编

使用gdb来查看-O2下的模块汇编,同objdump显示一致

disassemble test
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
29
30
31
32
33
34
(gdb) disassemble test
Dump of assembler code for function test:
0x0000000000000090 <+0>: callq 0x95 <test+5>
0x0000000000000095 <+5>: mov 0x0(%rip),%rax # 0x9c <test+12>
0x000000000000009c <+12>: testb $0x1,0x5c0(%rax)
0x00000000000000a3 <+19>: je 0xad <test+29>
0x00000000000000a5 <+21>: subl $0x1,0x0(%rip) # 0xac <test+28>
0x00000000000000ac <+28>: retq
0x00000000000000ad <+29>: addl $0x1,0x0(%rip) # 0xb4 <test+36>
0x00000000000000b4 <+36>: retq
End of assembler dump.
(gdb) disassemble test_likely
Dump of assembler code for function test_likely:
0x0000000000000030 <+0>: callq 0x35 <test_likely+5>
0x0000000000000035 <+5>: mov 0x0(%rip),%rax # 0x3c <test_likely+12>
0x000000000000003c <+12>: testb $0x1,0x5c0(%rax)
0x0000000000000043 <+19>: jne 0x4d <test_likely+29>
0x0000000000000045 <+21>: addl $0x1,0x0(%rip) # 0x4c <test_likely+28>
0x000000000000004c <+28>: retq
0x000000000000004d <+29>: subl $0x1,0x0(%rip) # 0x54 <test_likely+36>
0x0000000000000054 <+36>: retq
End of assembler dump.
(gdb) disassemble test_unlikely
Dump of assembler code for function test_unlikely:
0x0000000000000060 <+0>: callq 0x65 <test_unlikely+5>
0x0000000000000065 <+5>: mov 0x0(%rip),%rax # 0x6c <test_unlikely+12>
0x000000000000006c <+12>: testb $0x1,0x5c0(%rax)
0x0000000000000073 <+19>: je 0x7d <test_unlikely+29>
0x0000000000000075 <+21>: subl $0x1,0x0(%rip) # 0x7c <test_unlikely+28>
0x000000000000007c <+28>: retq
0x000000000000007d <+29>: addl $0x1,0x0(%rip) # 0x84 <test_unlikely+36>
0x0000000000000084 <+36>: retq
End of assembler dump.

注意,gdb disassemble显示的都是symbol,需要EXPORT_SYMBOL导出,可以通过nm确认

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
29
30
31
2-likely git:(master) ✗ nm likely.ko                                                
0000000000000000 b a
00000000000000d0 T cleanup_module
U current_task
U __fentry__
0000000000000090 T init_module
0000000000000005 r __kstrtabns_test
0000000000000012 r __kstrtabns_test_likely
0000000000000021 r __kstrtabns_test_unlikely
0000000000000000 r __kstrtab_test
0000000000000006 r __kstrtab_test_likely
0000000000000013 r __kstrtab_test_unlikely
0000000000000000 r __ksymtab_test
000000000000000c r __ksymtab_test_likely
0000000000000018 r __ksymtab_test_unlikely
00000000000000d0 t likely_exit
0000000000000090 t likely_init
0000000000000018 r _note_8
0000000000000000 r _note_9
0000000000000008 b p
U _printk
0000000000000060 T test
0000000000000000 T test_likely
0000000000000030 T test_unlikely
0000000000000000 D __this_module
0000000000000038 r __UNIQUE_ID_depends121
0000000000000000 r __UNIQUE_ID_license172
000000000000004d r __UNIQUE_ID_name119
0000000000000041 r __UNIQUE_ID_retpoline120
0000000000000015 r __UNIQUE_ID_srcversion122
0000000000000059 r __UNIQUE_ID_vermagic118

深入

gcc内建函数的实现流程

既然要追踪gcc内建函数的实现,可是对gcc架构并不熟悉,因此想通过gdb来调试。但是本地的gcc是不带symbol的,gcc源代码默认编译方式是-g

1
2
➜  bin file /usr/bin/x86_64-linux-gnu-gcc-6
/usr/bin/x86_64-linux-gnu-gcc-6: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=3d6f4ce69fccf03dec42b3eea2fbc14a23b10ea3, stripped

下载最新的gcc编译替代本地后调试发现如下异常,应该是不影响gcc编译过程调试的

怀疑内建函数使用中 有用到add_builtin_function-->build_builtin_function,因此在此处设置断点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2-likely git:(master) ✗ gdb
(gdb) file /usr/local/bin/lto-dump
Reading symbols from /usr/local/bin/lto-dump...done.
...
(gdb) b add_builtin_function
Breakpoint 1 at 0x9d0280: file ../../gcc/langhooks.cc, line 735.


(gdb) make >>没有在断点处暂停,原因未知,继续追踪研究
KBUILD_CFLAGS is
make -C /github/linux M=/github/linux-driver/2-likely modules
make[1]: Entering directory '/github/linux'
warning: the compiler differs from the one used to build the kernel
The kernel was built by: gcc (Ubuntu 6.5.0-2ubuntu1~16.04) 6.5.0 20181026
You are using: gcc (GCC) 12.0.1 20220406 (experimental)

如果去匹配,那么需要重新用新的gcc来编译内核(同时也应该去编译模块),但是gcc版本太高也应该有问题,内核编译会失败,因此下载gcc 6.5.0版本编译带symbol的执行文件

1
2
3
4
5
6
7
8
9
wget https://ftp.gnu.org/gnu/gcc/gcc-6.5.0/gcc-6.5.0.tar.xz
xz -d gcc-6.5.0.tar.xz
tar -xvf gcc-6.5.0.tar
./contrib/download_prerequisites >>安装依赖
mkdir gcc-build
cd gcc-build
../configure -enable-languages=c,c++ -disable-multilib -enable-checking=release
make install >>默认安装在/usr/local下,生成新的/usr/local/bin/gcc
rm -rf /usr/bin/gcc 删除旧的软链接,可删可不删, which gcc都会指向新的

思考?gcc编译什么时会触发我设置的内建断点?编译模块likely.c生成likely.o吗?还是编译内核?或者还是gcc本身编译呢(不太可能,编译时内建代码部分?)

通过make编译模块没有触发断点,嗯…想办法直接执行对应的gcc命令

make编译模块生成.o

动态模块中make主要部分如下:

1
make -C $(KDIR) M=$(PWD) modules

等价于在linux 下执行

1
➜  linux git:(master) ✗ make  M=/github/linux-driver/2-likely modules

根据依赖规则,找到对应的实际生成likely.o的gcc编译部分,调试时将S(Q)去掉,这样可以打印make语句

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
//Makefile in kernel
modules: modules_check
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost

PHONY += modules_check
modules_check: $(MODORDER)
$(Q)$(CONFIG_SHELL) $(srctree)/scripts/modules-check.sh $<

>>依赖$(MODORDER), 这个是什么?
export extmod_prefix = $(if $(KBUILD_EXTMOD),$(KBUILD_EXTMOD)/)
export MODORDER := $(extmod_prefix)modules.order
>>$(MODORDER)是/github/linux-driver/2-likely/modules.order,但是这个文件没有?这个依赖什么?
$(MODORDER): descend
@:

descend: $(build-dirs)
$(build-dirs): prepare
$(info chejian make is $(MAKE) build is $(build) | $@)
$(MAKE) $(build)=$@ \
single-build=$(if $(filter-out $@/, $(filter $@/%, $(KBUILD_SINGLE_TARGETS))),1) \
need-builtin=1 need-modorder=1


>>打印为make -f ./scripts/Makefile.build obj=/github/linux-driver/2-likely \
single-build= \
need-builtin=1 need-modorder=1


>>进入scripts/Makefile.build执行编译,查找第一个目标
__build: $(if $(KBUILD_BUILTIN), $(targets-for-builtin)) \
$(if $(KBUILD_MODULES), $(targets-for-modules)) \
$(subdir-ym) $(always-y)
$(info sholck test 2 $(KBUILD_BUILTIN) | $(targets-for-builtin) | $(KBUILD_MODULES) | $(targets-for-modules) | $(subdir-ym)| $(always-y))
@:

endif


>>打印确认只有$(targets-for-modules)这个依赖不为空
targets-for-modules := $(patsubst %.o, %.mod, $(filter %.o, $(obj-m)))
>>确认targets-for-modules为/github/linux-driver/2-likely/likely.mod

>>查找.mod的依赖
$(obj)/%.mod: $(obj)/%$(mod-prelink-ext).o FORCE
$(call if_changed,mod)

>>查找.o的依赖
# Built-in and composite module parts
$(obj)/%.o: $(src)/%.c $(recordmcount_source) FORCE
$(if $(if-changed-cond),$(rule_cc_o_c),@:)
$(call if_changed_rule,cc_o_c)
$(info lynx if-changed-cond is $(if-changed-cond)) >>增加打印
$(info lynx rule_cc_o_c is $(rule_cc_o_c)) >>增加打印
$(call cmd,force_checksrc)
>>函数调用,检查打印if_changed_rule函数实现
if_changed_rule = $(if $(if-changed-cond),$(rule_$(1)),@:)

>>确定实际的gcc命令为
gcc -Wp,-MMD,/github/linux-driver/2-likely/.likely.o.d -nostdinc -I./arch/x86/include -I./arch/x86/include/generated -I./include -I./arch/x86/include/uapi -I./arch/x86/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__ -Wall -Wundef -Werror=strict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -Werror=implicit-function-declaration -Werror=implicit-int -Werror=return-type -Wno-format-security -std=gnu11 -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -m64 -falign-jumps=1 -falign-loops=1 -mno-80387 -mno-fp-ret-in-387 -mpreferred-stack-boundary=3 -mskip-rax-setup -mtune=generic -mno-red-zone -mcmodel=kernel -Wno-sign-compare -fno-asynchronous-unwind-tables -mindirect-branch=thunk-extern -mindirect-branch-register -fno-jump-tables -fno-delete-null-pointer-checks -Wno-frame-address -O2 --param=allow-store-data-races=0 -Wframe-larger-than=1024 -fstack-protector-strong -Wno-main -Wno-unused-but-set-variable -Wno-unused-const-variable -pg -mrecord-mcount -mfentry -DCC_USING_FENTRY -Wdeclaration-after-statement -Wvla -Wno-pointer-sign -Wno-maybe-uninitialized -fno-strict-overflow -fno-stack-check -fconserve-stack -Werror=date-time -Werror=incompatible-pointer-types -Werror=designated-init -g -gdwarf-4 -Wall -g -DMODULE -DKBUILD_BASENAME='"likely"' -DKBUILD_MODNAME='"likely"' -D__KBUILD_MODNAME=kmod_likely -c -o /github/linux-driver/2-likely/likely.o /github/linux-driver/2-likely/likely.c

gdb 调试gcc中运行该命令未见内建中断触发

1
2
3
4
5
6
7
8
9
10
11
12
➜  linux git:(master) ✗ gdb
(gdb) file gcc
Reading symbols from gcc...done.
(gdb) add-symbol-file /github/package/gcc-6.5.0/gcc-build/gcc/langhooks.o
add symbol table from file "/github/package/gcc-6.5.0/gcc-build/gcc/langhooks.o"
(y or n) y
Reading symbols from /github/package/gcc-6.5.0/gcc-build/gcc/langhooks.o...done.
(gdb) b add_builtin_function
...
(gdb) run -Wp,-MMD,/github/linux-driver/2-likely/.likely.o.d -nostdinc -I./arch/x86/include -I./arch/x86/include/generated -I./include -I./arch/x86/include/uapi -I./arch/x86/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__ -Wall -Wundef -Werror=strict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -Werror=implicit-function-declaration -Werror=implicit-int -Werror=return-type -Wno-format-security -std=gnu11 -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -m64 -falign-jumps=1 -falign-loops=1 -mno-80387 -mno-fp-ret-in-387 -mpreferred-stack-boundary=3 -mskip-rax-setup -mtune=generic -mno-red-zone -mcmodel=kernel -Wno-sign-compare -fno-asynchronous-unwind-tables -mindirect-branch=thunk-extern -mindirect-branch-register -fno-jump-tables -fno-delete-null-pointer-checks -Wno-frame-address -O2 --param=allow-store-data-races=0 -Wframe-larger-than=1024 -fstack-protector-strong -Wno-main -Wno-unused-but-set-variable -Wno-unused-const-variable -pg -mrecord-mcount -mfentry -DCC_USING_FENTRY -Wdeclaration-after-statement -Wvla -Wno-pointer-sign -Wno-maybe-uninitialized -fno-strict-overflow -fno-stack-check -fconserve-stack -Werror=date-time -Werror=incompatible-pointer-types -Werror=designated-init -g -gdwarf-4 -Wall -g -DMODULE -DKBUILD_BASENAME='"likely"' -DKBUILD_MODNAME='"likely"' -D__KBUILD_MODNAME=kmod_likely -c -o /github/linux-driver/2-likely/likely.o /github/linux-driver/2-likely/likely.c
...
[Inferior 1 (process 26045) exited normally] >>正常退出未见触发

测试编译内核/modules

编译内核和动态模块未见触发断点