printk如何定向输出到UART的?


我们通常在config里面配置这么一句“CONFIG_CMDLINE="console=tty0 console=ttyMT3,921600n1 root=/dev/ram"”,然后修改console的赋值就可以改变输出的位置,代码中如何实现的呢?

这里紧接着上文中的printk.c文件中,有这么一个函数先看一下:
/*
 * Set up a list of consoles.  Called from init/main.c
 */
拉风的注释直接告诉了我们这个函数被init/main.c文件中某个函数调用。
static int __init console_setup(char *str)
{
	char buf[sizeof(console_cmdline[0].name) + 4]; /* 4 for index */
	char *s, *options, *brl_options = NULL;
	int idx;


#ifdef CONFIG_A11Y_BRAILLE_CONSOLE
	if (!memcmp(str, "brl,", 4)) {
		brl_options = "";
		str += 4;
	} else if (!memcmp(str, "brl=", 4)) {
		brl_options = str + 4;
		str = strchr(brl_options, ',');
		if (!str) {
			printk(KERN_ERR "need port name after brl=\n");
			return 1;
		}
		*(str++) = 0;
	}
#endif


	/*
	 * Decode str into name, index, options.
	 */
	if (str[0] >= '0' && str[0] <= '9') {
		strcpy(buf, "ttyS");
		strncpy(buf + 4, str, sizeof(buf) - 5);
	} else {
		strncpy(buf, str, sizeof(buf) - 1);
	}
	buf[sizeof(buf) - 1] = 0;
	if ((options = strchr(str, ',')) != NULL)
		*(options++) = 0;
#ifdef __sparc__
	if (!strcmp(str, "ttya"))
		strcpy(buf, "ttyS0");
	if (!strcmp(str, "ttyb"))
		strcpy(buf, "ttyS1");
#endif
	for (s = buf; *s; s++)
		if ((*s >= '0' && *s <= '9') || *s == ',')
			break;
	idx = simple_strtoul(s, NULL, 10);
	*s = 0;


	__add_preferred_console(buf, idx, options, brl_options);
	console_set_on_cmdline = 1;
	return 1;
}


可以看到这里面有对字符串的解析,获得tty的name,index,options。加入链表是由下面的宏来做的。
__setup("console=", console_setup);

好像是不是缺少了点东东,在main.c中调用的的链表呢?在这里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.
 *
 * This can happen pretty early during the boot process (because of
 * early_printk) - sometimes before setup_arch() completes - be careful
 * of what kernel features are used - they may not be initialised yet.
 *
 * There are two types of consoles - bootconsoles (early_printk) and
 * "real" consoles (everything which is not a bootconsole) which are
 * handled differently.
 *  - Any number of bootconsoles can be registered at any time.
 *  - As soon as a "real" console is registered, all bootconsoles
 *    will be unregistered automatically.
 *  - Once a "real" console is registered, any attempt to register a
 *    bootconsoles will be rejected
 */
void register_console(struct console *newcon)
{
	............................
	/*
	 *	See if this console matches one we selected on
	 *	the command line.
	 */
	 //看上面的注释我们也可以知道了command line中的定义在这里也起作用了
	for (i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0];
			i++) {
		if (strcmp(console_cmdline[i].name, newcon->name) != 0)
			continue;
		if (newcon->index >= 0 &&
		    newcon->index != console_cmdline[i].index)
			continue;
		if (newcon->index < 0)
			newcon->index = console_cmdline[i].index;
#ifdef CONFIG_A11Y_BRAILLE_CONSOLE
		if (console_cmdline[i].brl_options) {
			newcon->flags |= CON_BRL;
			braille_register_console(newcon,
					console_cmdline[i].index,
					console_cmdline[i].options,
					console_cmdline[i].brl_options);
			return;
		}
#endif
		if (newcon->setup &&
		    newcon->setup(newcon, console_cmdline[i].options) != 0)/在这里可以看到setup的过程了
			break;
		newcon->flags |= CON_ENABLED;
		newcon->index = console_cmdline[i].index;
		if (i == selected_console) {
			newcon->flags |= CON_CONSDEV;
			preferred_console = selected_console;
		}
		break;
	}


	if (!(newcon->flags & CON_ENABLED))//其他的console driver屏蔽掉
		return;


........................................
}


当然了,上面的函数在源码中的注释很是详细,如果需要看的,读源码是最佳的选择。


到了这里,so command line的tty端口的指定就到了这里了。


Have Fun!
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`printk()` 函数是 Linux 内核中用于输出信息的函数,它的实现非常复杂,涉及到多个模块和多个文件。下面简单介绍一下 `printk()` 函数的实现过程。 1. `printk()` 函数的定义 在 `include/linux/kernel.h` 文件中,定义了 `printk()` 函数的原型: ```c int printk(const char *fmt, ...); ``` 其中,`fmt` 参数是格式化字符串,`...` 表示可变参数列表。 2. `printk()` 函数的实现 `printk()` 函数的实现定义在 `kernel/printk/printk.c` 文件中。该文件中包含了多个函数和数据结构,用于实现 `printk()` 函数。 具体来说,`printk()` 函数的实现过程如下: (1)调用 `vprintk_emit()` 函数将格式化字符串和可变参数列表转换成一段文本内容。 (2)调用 `log_store()` 函数将文本内容存储到内核日志缓冲区中。 (3)调用 `console_trylock()` 函数获取控制台锁。 (4)调用 `console_unlock()` 函数释放控制台锁。 (5)返回输出的字符数。 其中,`vprintk_emit()` 函数是将格式化字符串和可变参数列表转换成一段文本内容的函数,它的实现定义在 `kernel/printk/printk.c` 文件中。该函数主要使用 `vsnprintf()` 函数将格式化字符串和可变参数列表转换成一段文本内容,并将结果存储在 `log_buf` 缓冲区中。 `log_store()` 函数是将文本内容存储到内核日志缓冲区中的函数,它的实现定义在 `kernel/printk/printk.c` 文件中。该函数主要将文本内容存储在 `log_buf` 缓冲区中,并更新相关的内部状态信息。 `console_trylock()` 函数是获取控制台锁的函数,它的实现定义在 `kernel/printk/console.c` 文件中。该函数主要是尝试获取控制台锁,如果获取成功,则返回 0,否则返回 -EBUSY。 `console_unlock()` 函数是释放控制台锁的函数,它的实现定义在 `kernel/printk/console.c` 文件中。该函数主要是释放控制台锁,并唤醒等待输出的进程。 综上所述,`printk()` 函数的实现非常复杂,它涉及到多个模块和多个文件,其中包括了内核日志缓冲区、控制台锁、格式化字符串转换等多个功能的实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值