看printk引发的一点思考

    技术2026-04-18  1

    在源码位置

    kernel/printk/

    函数原型

    asmlinkage __visible int printk(const char *fmt, ...) {  printk_func_t vprintk_func;  va_list args;  int r;  va_start(args, fmt);  /*   * If a caller overrides the per_cpu printk_func, then it needs   * to disable preemption when calling printk(). Otherwise   * the printk_func should be set to the default. No need to   * disable preemption here.   */  vprintk_func = this_cpu_read(printk_func);  r = vprintk_func(fmt, args);  va_end(args);  return r; } EXPORT_SYMBOL(printk);

    fmt,...用法举个例子

    #include "stdio.h" void tfunc(const int fmt,...) {  printf("tfunc...\n"); }  int main(void) {  tfunc(12);  tfunc(12,13);  tfunc(12,13,14);  return 0; } 

    输出

    tfunc... tfunc... tfunc... -------------------------------- Process exited after 0.04347 seconds with return value 0 请按任意键继续. . .

    再举个例子

    #include <stdlib.h> #include <stdio.h> #include <stdarg.h> int maxof(int, ...); void f(void); /*主函数*/ int main() {     f();     exit(EXIT_SUCCESS); } int maxof(int n_args, ...) {     register int i;     int max=0, a=0;     va_list ap;     va_start(ap, n_args);     max = va_arg(ap, int);  printf("max:%d a:%d n_args:%d\n",max,a,n_args);     for(i = 1; i < n_args; i++)  {         if((a = va_arg(ap, int)) > max){          max = a;   }   printf("[%d] max:%d a:%d\n",i,max,a);     }     va_end(ap);     return max; } void f(void)  {     int i = 5;  int j = 26;     printf("\nmax:%d\n",maxof(3,i,j,13)); }

    输出

    max:5 a:0 n_args:3 [1] max:26 a:26 [2] max:26 a:13 max:26 -------------------------------- Process exited after 0.0439 seconds with return value 0 请按任意键继续. . .

    说点自己的理解

    作为初学者,第一次看到

    fmt,...

    这样的写法,心里就会有点懵逼,但是实际上,你把它作为C语言的一个知识点,记下来了,就没有那么困难了。既然作为可变参数标识,那么函数体里面,自然也需要解析的方法,我上面写的那个例子,就是解析的方法。

    不管是Linux 内核里面的printk函数,还是我们平时调试打印的printf函数,他们都是一样的原理。

    解析可变参数

    解析就离不开这三个宏

    va_list ap; va_start(ap, n_args); va_end(ap);

    这里面需要涉及到一些技巧,这篇文章里面就不解析说明了,printf 和printk里面使用的还有些差异,无非就是C语言的奇淫异巧,把这种普通人理解不了的东西形容为降龙十八掌,九阴真经,我觉得并不为过。

    Linux 驱动打印日志加上自己的TAG

    好了,直接上代码就好了,我们平时打印的时候都是直接用一个printk,也没有经过封装,代码这种东西,你要是把它看作是一个艺术品也并不为过,所以很多人谈到一个词,叫做技艺,你自己把这种知识点都掌握了,自己的技艺也就会得到提升了。

    #define LOG_TAG "[ES7243]: %s() line: %d "  #define Log(fmt, args...)  printk(KERN_INFO LOG_TAG fmt, __FUNCTION__, __LINE__,  ##args)

    ## 这个符号的作用

    在C语言里面,## 有两个作用

    在函数里面和可变参数一起使用,如果可变参数没有传参,这个符号就把前面的 「,」去掉,这样编译就不会出错。

    如果是宏里面使用,就起到一个拼接字符的作用

    举个例子

    #include<stdio.h> #define LOG_TAG "[ES7243]: %s() line: %d "  #define func(fmt, ...) printf(LOG_TAG fmt, __FUNCTION__, __LINE__,  ##__VA_ARGS__) #define acpi_handle_debug( fmt, ...) printf( fmt, ##__VA_ARGS__) #define debug(...) printf(__VA_ARGS__) #define _TEST_(x) x##2 int main() {  int i = 23;  func("adf:%d\n",++i);  debug("123:%d\n",++i);  acpi_handle_debug("4444\n");    func("%d\n",_TEST_(1));  return (0); }

    输出

    [ES7243]: main() line: 12 adf:24 123:25 4444 [ES7243]: main() line: 16 12 -------------------------------- Process exited after 0.03505 seconds with return value 0 请按任意键继续. . .

    推荐阅读:

    专辑|Linux文章汇总

    专辑|程序人生

    专辑|C语言

    嵌入式Linux

    微信扫描二维码,关注我的公众号

    Processed: 0.011, SQL: 9