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 # 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)), 那么将其他情况的指令提前到条件指令后面。
实践 通过代码汇编指令对比检查优化逻辑,汇编有两种方式:
通过objdump进行汇编 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 
修改编译优化等级有两种方式
修改linux Makefile编译方式 1 2 3 4 5 6 7 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 2 EXTRA_CFLAGS = -Wall -g -O2 
代码 在代码设计时,要注意三点
确保变量不能是定值,比如a = 1; if(likely(a == 1)),因此选择使用进程号 要确保不同条件下的指令有明显差异,不能都是printk,不然只能看到汇编流程上je和jne的差异,因此选择a++和a–。 因为使用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 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 #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++;                    }         else  {                 a--;                      } } EXPORT_SYMBOL(test); void  test_likely (void )   {        if (likely((p->pid % 2 ) == 0 )) {                 a++;                        }         else  {                 a--;                          } } EXPORT_SYMBOL(test_likely); void  test_unlikely (void )   {        if (unlikely((p->pid % 2 ) == 0 )) {                 a++;                          }         else  {                 a--;                          } } 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:	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:	c3                   	retq      1d:	83 2d 00 00 00 00 01 	subl   $0x1 ,0x0(%rip)           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:	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:	c3                   	retq      4d:	83 05 00 00 00 00 01 	addl   $0x1 ,0x0(%rip)           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:	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:	c3                   	retq      7d:	83 05 00 00 00 00 01 	addl   $0x1 ,0x0(%rip)           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   4 a:	75  11                 	jne    5 d <test+0x2e >   >>为假跳到5 d   4 c:	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    6 c <test+0x3d >   5 d:	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)        # 6 c <test+0x3d >   6 c:	90                    	nop   6 d:	5 d                   	pop    %rbp   6 e:	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   8 a:	0f  94  c0             	sete   %al   8 d:	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   9 e:	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:	5 d                   	pop    %rbp   b7:	c3                   	retq    00000000000000b 8 <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:	5 d                   	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     1 d <test+0x1d >   15 :	83  2 d 00  00  00  00  01  	subl   $0x1 ,0x0 (%rip)        # 1 c <test+0x1c >   1 c:	c3                   	retq      1 d:	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  2 a <test_likely+0x5 >   2 a:	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 >   3 a:	83  05  00  00  00  00  01  	addl   $0x1 ,0x0 (%rip)        # 41  <test_likely+0x1c >   41 :	c3                   	retq      42 :	83  2 d 00  00  00  00  01  	subl   $0x1 ,0x0 (%rip)        # 49  <test_likely+0x24 >   49 :	c3                   	retq    000000000000004 a <test_unlikely>:  4 a:	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)   5 d:	74  08                 	je     67  <test_unlikely+0x1d >   5f :	83  2 d 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)        # 6 e <test_unlikely+0x24 >   6 e:	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 a00000000000000 d0 T cleanup_module                 U current_task                  U __fentry__ 0000000000000090  T init_module0000000000000005  r __kstrtabns_test0000000000000012  r __kstrtabns_test_likely0000000000000021  r __kstrtabns_test_unlikely0000000000000000  r __kstrtab_test0000000000000006  r __kstrtab_test_likely0000000000000013  r __kstrtab_test_unlikely0000000000000000  r __ksymtab_test000000000000000 c r __ksymtab_test_likely0000000000000018  r __ksymtab_test_unlikely00000000000000 d0 t likely_exit0000000000000090  t likely_init0000000000000018  r _note_80000000000000000  r _note_90000000000000008  b p                 U _printk 0000000000000060  T test0000000000000000  T test_likely0000000000000030  T test_unlikely0000000000000000  D __this_module0000000000000038  r __UNIQUE_ID_depends1210000000000000000  r __UNIQUE_ID_license172000000000000004 d r __UNIQUE_ID_name1190000000000000041  r __UNIQUE_ID_retpoline1200000000000000015  r __UNIQUE_ID_srcversion1220000000000000059  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]=3 d6f4ce69fccf03dec42b3eea2fbc14a23b10ea3, 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 -2u buntu1~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: 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 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 编译内核和动态模块未见触发断点