Linux驱动调试之printk的原理

本文详细介绍了Linux驱动程序的调试方法,包括使用printk进行打印、自制proc文件、内核段错误信息分析等。同时深入探讨了printk的工作原理,从内核处理U-Boot传入参数、硬件驱动注册控制台参数到printk函数的实现过程,揭示了内核打印消息的内部机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1Linux驱动程序调试方法

2printk原理

2.1 内核处理uboot传入的参数

2.2 硬件驱动的入口函数里注册控制台参数 

2.3 printk函数实现

2.4 printk原理总结


1Linux驱动程序调试方法

Linux驱动程序调试主要有以下几种方法

  • 打印:pirintk,自制proc文件
  • 自制工具
  • 根据内核打印的段错误信息分析
  • 修改内核

2printk原理

2.1 内核处理uboot传入的参数

我们首先启动开发板,进入uboot,然后在uboot命令行print打印出uboot的环境变量,

 然后我们改一下参数,console不写了,

 然后boot启动,发现启动后内核没有打印任何信息,只要设置了console之后,内核才会打印出信息来。

我们在代码中搜索 sonsole= 然后找到

我们看一下console_setup这个函数

 然后调用了add_preferred_console函数。

 这里的console_cmdline是在别的地方定义的一个全局的静态变量。

2.2 硬件驱动的入口函数里注册控制台参数 

然后我们看一下console_cmdline还在哪个地方被使用,然后发现了一个register_console函数

/*
 * The console driver calls this routine during kernel initialization
 * to register the console printing procedure with printk() and to
 * print any messages that were printed by the kernel before the
 * console driver was initialized.
 */
void register_console(struct console *console)
{
	int i;
	unsigned long flags;
	struct console *bootconsole = NULL;

	if (console_drivers) {
		if (console->flags & CON_BOOT)
			return;
		if (console_drivers->flags & CON_BOOT)
			bootconsole = console_drivers;
	}

	if (preferred_console < 0 || bootconsole || !console_drivers)
		preferred_console = selected_console;

	/*
	 *	See if we want to use this console driver. If we
	 *	didn't select a console we take the first one
	 *	that registers here.
	 */
	if (preferred_console < 0) {
		if (console->index < 0)
			console->index = 0;
		if (console->setup == NULL ||
		    console->setup(console, NULL) == 0) {
			console->flags |= CON_ENABLED | CON_CONSDEV;
			preferred_console = 0;
		}
	}

	/*
	 *	See if this console matches one we selected on
	 *	the command line.
	 */
	for (i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0];
			i++) {
		if (strcmp(console_cmdline[i].name, console->name) != 0)
			continue;
		if (console->index >= 0 &&
		    console->index != console_cmdline[i].index)
			continue;
		if (console->index < 0)
			console->index = console_cmdline[i].index;
		if (console->setup &&
		    console->setup(console, console_cmdline[i].options) != 0)
			break;
		console->flags |= CON_ENABLED;
		console->index = console_cmdline[i].index;
		if (i == selected_console) {
			console->flags |= CON_CONSDEV;
			preferred_console = selected_console;
		}
		break;
	}

	if (!(console->flags & CON_ENABLED))
		return;

	if (bootconsole) {
		printk(KERN_INFO "console handover: boot [%s%d] -> real [%s%d]\n",
		       bootconsole->name, bootconsole->index,
		       console->name, console->index);
		unregister_console(bootconsole);
		console->flags &= ~CON_PRINTBUFFER;
	}

	/*
	 *	Put this console in the list - keep the
	 *	preferred driver at the head of the list.
	 */
	acquire_console_sem();
	if ((console->flags & CON_CONSDEV) || console_drivers == NULL) {
		console->next = console_drivers;
		console_drivers = console;
		if (console->next)
			console->next->flags &= ~CON_CONSDEV;
	} else {
		console->next = console_drivers->next;
		console_drivers->next = console;
	}
	if (console->flags & CON_PRINTBUFFER) {
		/*
		 * release_console_sem() will print out the buffered messages
		 * for us.
		 */
		spin_lock_irqsave(&logbuf_lock, flags);
		con_start = log_start;
		spin_unlock_irqrestore(&logbuf_lock, flags);
	}
	release_console_sem();
}

这个register_console是在 drivers/serial/s3c2410.c里面被调用的,register_console函数是注册struct console这样一个结构体,

这个结构体里面有名字,有write函数, 

2.3 printk函数实现

代码中搜索printk,

 然后继续看vprintk

asmlinkage int vprintk(const char *fmt, va_list args)
{
	unsigned long flags;
	int printed_len;
	char *p;
	static char printk_buf[1024];
	static int log_level_unknown = 1;

	preempt_disable();
	if (unlikely(oops_in_progress) && printk_cpu == smp_processor_id())
		/* If a crash is occurring during printk() on this CPU,
		 * make sure we can't deadlock */
		zap_locks();

	/* This stops the holder of console_sem just where we want him */
	raw_local_irq_save(flags);
	lockdep_off();
	spin_lock(&logbuf_lock);
	printk_cpu = smp_processor_id();

	/* Emit the output into the temporary buffer */
	printed_len = vscnprintf(printk_buf, sizeof(printk_buf), fmt, args);

	/*
	 * Copy the output into log_buf.  If the caller didn't provide
	 * appropriate log level tags, we insert them here
	 */
	for (p = printk_buf; *p; p++) {
		if (log_level_unknown) {
                        /* log_level_unknown signals the start of a new line */
			if (printk_time) {
				int loglev_char;
				char tbuf[50], *tp;
				unsigned tlen;
				unsigned long long t;
				unsigned long nanosec_rem;

				/*
				 * force the log level token to be
				 * before the time output.
				 */
				if (p[0] == '<' && p[1] >='0' &&
				   p[1] <= '7' && p[2] == '>') {
					loglev_char = p[1];
					p += 3;
					printed_len -= 3;
				} else {
					loglev_char = default_message_loglevel
						+ '0';
				}
				t = printk_clock();
				nanosec_rem = do_div(t, 1000000000);
				tlen = sprintf(tbuf,
						"<%c>[%5lu.%06lu] ",
						loglev_char,
						(unsigned long)t,
						nanosec_rem/1000);

				for (tp = tbuf; tp < tbuf + tlen; tp++)
					emit_log_char(*tp);
				printed_len += tlen;
			} else {
				if (p[0] != '<' || p[1] < '0' ||
				   p[1] > '7' || p[2] != '>') {
					emit_log_char('<');
					emit_log_char(default_message_loglevel
						+ '0');
					emit_log_char('>');
					printed_len += 3;
				}
			}
			log_level_unknown = 0;
			if (!*p)
				break;
		}
		emit_log_char(*p);
		if (*p == '\n')
			log_level_unknown = 1;
	}

	if (!down_trylock(&console_sem)) {
		/*
		 * We own the drivers.  We can drop the spinlock and
		 * let release_console_sem() print the text, maybe ...
		 */
		console_locked = 1;
		printk_cpu = UINT_MAX;
		spin_unlock(&logbuf_lock);

		/*
		 * Console drivers may assume that per-cpu resources have
		 * been allocated. So unless they're explicitly marked as
		 * being able to cope (CON_ANYTIME) don't call them until
		 * this CPU is officially up.
		 */
		if (cpu_online(smp_processor_id()) || have_callable_console()) {
			console_may_schedule = 0;
			release_console_sem();
		} else {
			/* Release by hand to avoid flushing the buffer. */
			console_locked = 0;
			up(&console_sem);
		}
		lockdep_on();
		raw_local_irq_restore(flags);
	} else {
		/*
		 * Someone else owns the drivers.  We drop the spinlock, which
		 * allows the semaphore holder to proceed and to call the
		 * console drivers with the output which we just produced.
		 */
		printk_cpu = UINT_MAX;
		spin_unlock(&logbuf_lock);
		lockdep_on();
		raw_local_irq_restore(flags);
	}

	preempt_enable();
	return printed_len;
}

2.4 printk原理总结

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈 洪 伟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值