linux console

1.system.map映射表

参考之前写的一篇博客 点击打开链接

 

2.从内核第二阶段启动分析控制台console注册流程

路径: linux-3.10.101/init/main.c

asmlinkage void __init start_kernel(void)
{
	//......
	pr_notice("Kernel command line: %s\n", boot_command_line);
	parse_early_param(); //解析控制台参数
	parse_args("Booting kernel", static_command_line, __start___param, __stop___param - __start___param, -1, -1, &unknown_bootoption);
	//......
	console_init();	//控制台初始化
	//......
}

 

3. parse_early_param()

路径:linux-3.10.101/init/main.c/start_kernel()

 

void __init parse_early_param(void)
{
	static __initdata int done = 0;
	static __initdata char tmp_cmdline[COMMAND_LINE_SIZE];

	if (done)
		return;

	/* All fall through to do_early_param. */
	strlcpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE);
	parse_early_options(tmp_cmdline);
	done = 1;
}

 

void __init parse_early_options(char *cmdline)
{
	parse_args("early options", cmdline, NULL, 0, 0, 0, do_early_param);
}

static int __init do_early_param(char *param, char *val, const char *unused)
{
	const struct obs_kernel_param *p;

	for (p = __setup_start; p < __setup_end; p++) {
		if ((p->early && parameq(param, p->str)) ||
		    (strcmp(param, "console") == 0 &&
		     strcmp(p->str, "earlycon") == 0)
		) {
			if (p->setup_func(val) != 0)
				pr_warn("Malformed early option '%s'\n", param);
		}
	}
	/* We accept everything at this stage. */
	return 0;
}

 

 

 

分析:这里主要了解__setup_start和__setup_end的意义,可以参考之前写的一篇博客“linux _setup” 击打开链接,这里不在赘述!所以for循环遍历system.map表中__setup_start、__setup_end区间内映射表,

 

 

c04373e0 T __setup_start
c04373ec t __setup_init_setup
c04373f8 t __setup_loglevel
c0437404 t __setup_quiet_kernel
//......
c0437560 t __setup_console_setup //注意这里
//......
c0437788 T __setup_end

 

 

 

 

 

 

从而找到__setup_console_setup对应的结构体内容,最终调用console_setup函数:

 

 

  1. struct obs_kernel_param {  
  2.     const char *str=“console”  
  3.     int (*setup_func)(char *)=console_setup  
  4.     int early=0  
  5. }; 

 

 

 

 

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;
}
__setup("console=", console_setup);

 

 

static int __add_preferred_console(char *name, int idx, char *options,
   char *brl_options)
{
	struct console_cmdline *c;
	int i;

	/*
	 *	See if this tty is not yet registered, and
	 *	if we have a slot free.
	 */
	for (i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0]; i++)
		if (strcmp(console_cmdline[i].name, name) == 0 &&
			  console_cmdline[i].index == idx) {
				if (!brl_options)
					selected_console = i;
				return 0;
		}
	if (i == MAX_CMDLINECONSOLES)
		return -E2BIG;
	if (!brl_options)
		selected_console = i;
	c = &console_cmdline[i]; //注意这里,最终将控制台内容加入到该全局变量结构体中
	strlcpy(c->name, name, sizeof(c->name));
	c->options = options;
#ifdef CONFIG_A11Y_BRAILLE_CONSOLE
	c->brl_options = brl_options;
#endif
	c->index = idx;
	return 0;
}


start_kernel-->parse_args-->parse_early_options-->parse_args-->do_early_param-->console_setup-->__add_preferred_console-->console_cmdline[i],在该函数内部,将控制台参数保存到到了全局变量结构体console_cmdline[i]中,接下来分析控制台的注册流程。

 

 

4.console_init

 

 

 

void __init console_init(void)
{
	initcall_t *call;

	/* Setup the default TTY line discipline. */
	tty_ldisc_begin(); //设置默认线路规程


	/*
	 * set up the console device so that later boot sequences can
	 * inform about problems etc..
	 */
	call = __con_initcall_start; //通过查找system.map,这里会调用console_initcall(con_init);函数进行ttyS0控制台初始化
	while (call < __con_initcall_end) {
		(*call)();
		call++;
	}
}

 

根据之前写的一篇博客“linux _setup” 击打开链接,很容易确定__con_initcall_start在system.map的位置,内容如下:

c0437ad0 T __con_initcall_start
c0437ad0 t __initcall_con_init
c0437ad0 T __initcall_end
c0437ad4 t __initcall_nuc970serial_console_init //ttyS0控制台
c0437ad8 T __con_initcall_end 


在映射表中有__initcall_con_init和__initcall_nuc970serial_console_init两个控制台程序。

 

 

 

__initcall_con_init有点不好找,我是通过内核启动时打印的报文找到的,报文内容如下:
......
Console: colour dummy device 80x30//linux-3.10.x/drivers/tty/vt/vt.c  line=2905 
console [ttyS0] enabled//linux-3.10.x/kernel/prink.c line=2387
......

路径:linux-3.10.x/drivers/tty/vt/vt.c

 

static int __init con_init(void)
{
	const char *display_desc = NULL;
	struct vc_data *vc;
	unsigned int currcons = 0, i;

	console_lock();

	if (conswitchp)
		display_desc = conswitchp->con_startup();
	if (!display_desc) {
		fg_console = 0;
		console_unlock();
		return 0;
	}

	for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
		struct con_driver *con_driver = &registered_con_driver[i];

		if (con_driver->con == NULL) {
			con_driver->con = conswitchp;
			con_driver->desc = display_desc;
			con_driver->flag = CON_DRIVER_FLAG_INIT;
			con_driver->first = 0;
			con_driver->last = MAX_NR_CONSOLES - 1;
			break;
		}
	}

	for (i = 0; i < MAX_NR_CONSOLES; i++)
		con_driver_map[i] = conswitchp;

	if (blankinterval) {
		blank_state = blank_normal_wait;
		mod_timer(&console_timer, jiffies + (blankinterval * HZ));
	}

	for (currcons = 0; currcons < MIN_NR_CONSOLES; currcons++) {
		vc_cons[currcons].d = vc = kzalloc(sizeof(struct vc_data), GFP_NOWAIT);
		INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK);
		tty_port_init(&vc->port);
		visual_init(vc, currcons, 1);
		vc->vc_screenbuf = kzalloc(vc->vc_screenbuf_size, GFP_NOWAIT);
		vc_init(vc, vc->vc_rows, vc->vc_cols,
			currcons || !vc->vc_sw->con_save_screen);
	}
	currcons = fg_console = 0;
	master_display_fg = vc = vc_cons[currcons].d;
	set_origin(vc);
	save_screen(vc);
	gotoxy(vc, vc->vc_x, vc->vc_y);
	csi_J(vc, 0);
	update_screen(vc);
	pr_info("Console: %s %s %dx%d\n",
		vc->vc_can_do_color ? "colour" : "mono",
		display_desc, vc->vc_cols, vc->vc_rows);
	printable = 1;

	console_unlock();

#ifdef CONFIG_VT_CONSOLE
	register_console(&vt_console_driver); //这里很重要,内核启动时打印的信息“Console: colour dummy device 80x30”, “console [ttyS0] enabled”就在里面
#endif
	return 0;
}
console_initcall(con_init);

static struct console vt_console_driver = {
	.name		= "tty",
	.write		= vt_console_print,
	.device		= vt_console_device,
	.unblank	= unblank_screen,
	.flags		= CON_PRINTBUFFER,
	.index		= -1,
};

#endif
	return 0;
}
console_initcall(con_init);

static struct console vt_console_driver = {
	.name		= "tty",
	.write		= vt_console_print,
	.device		= vt_console_device,
	.unblank	= unblank_screen,
	.flags		= CON_PRINTBUFFER,
	.index		= -1,
};


路径:linux-3.10.101/drivers/tty/serial/nuc970_serial.c

 

 

 

 

static int __init nuc970serial_console_init(void)
{
	nuc970serial_init_ports();		
	register_console(&nuc970serial_console);
	
	return 0;
}
console_initcall(nuc970serial_console_init);
static struct console nuc970serial_console = {
	.name		= "ttyS",
	.write		= nuc970serial_console_write,
	.device		= uart_console_device,
	.setup		= nuc970serial_console_setup,
	.flags		= CON_PRINTBUFFER,
	.index		= -1,
	.data		= &nuc970serial_reg,
};		


con_init和nuc970serial_console_init最终都会调用register_console注册控制台,我们继续分析register_console,源码如下:

 

 

 

void register_console(struct console *newcon)
{
	int i;
	unsigned long flags;
	struct console *bcon = NULL;

	/*
	 * before we register a new CON_BOOT console, make sure we don't
	 * already have a valid console
	 */
	if (console_drivers && newcon->flags & CON_BOOT) {
		/* find the last or real console */
		for_each_console(bcon) {
			if (!(bcon->flags & CON_BOOT)) {
				printk(KERN_INFO "Too late to register bootconsole %s%d\n",
					newcon->name, newcon->index);
				return;
			}
		}
	}

	if (console_drivers && console_drivers->flags & CON_BOOT)
		bcon = console_drivers;

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

	if (newcon->early_setup)
		newcon->early_setup();

	/*
	 *	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 (newcon->index < 0)
			newcon->index = 0;
		if (newcon->setup == NULL ||
			newcon->setup(newcon, NULL) == 0) {
			newcon->flags |= CON_ENABLED;
			if (newcon->device) {
				newcon->flags |= CON_CONSDEV;
				preferred_console = 0;
			}
		}
	}

	/*
	*add by CL 20171129 23:16
	*下面的console_cmdline[i]变量是在static int __init console_setup(char *str)函数内部增加的,
	*而console_setup函数是通过__setup("console=", console_setup);添加到system.map表中,至于ttyS0
	*设备是什么时候通过console_setup添加到console_cmdline[i]中,关于添加是在start_kernel->parse_early_param()->parse_early_options(tmp_cmdline);
	*里操作的。
	*/
	
	/*
	 *	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, newcon->name) != 0) //�Ƚϵ���ttyS0 ttyUSB0,�����ں���������������������̨
			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)
			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))
		return;

	/*
	 * If we have a bootconsole, and are switching to a real console,
	 * don't print everything out again, since when the boot console, and
	 * the real console are the same physical device, it's annoying to
	 * see the beginning boot messages twice
	 */
	if (bcon && ((newcon->flags & (CON_CONSDEV | CON_BOOT)) == CON_CONSDEV))
		newcon->flags &= ~CON_PRINTBUFFER;

	/*
	 *	Put this console in the list - keep the
	 *	preferred driver at the head of the list.
	 */
	console_lock();
	if ((newcon->flags & CON_CONSDEV) || console_drivers == NULL) {
		newcon->next = console_drivers;
		console_drivers = newcon;
		if (newcon->next)
			newcon->next->flags &= ~CON_CONSDEV;
	} else {
		newcon->next = console_drivers->next;
		console_drivers->next = newcon;
	}
	if (newcon->flags & CON_PRINTBUFFER) {
		/*
		 * console_unlock(); will print out the buffered messages
		 * for us.
		 */
		raw_spin_lock_irqsave(&logbuf_lock, flags);
		console_seq = syslog_seq;
		console_idx = syslog_idx;
		console_prev = syslog_prev;
		raw_spin_unlock_irqrestore(&logbuf_lock, flags);
		/*
		 * We're about to replay the log buffer.  Only do this to the
		 * just-registered console to avoid excessive message spam to
		 * the already-registered consoles.
		 */
		exclusive_console = newcon;
	}
	console_unlock();
	console_sysfs_notify();

	/*
	 * By unregistering the bootconsoles after we enable the real console
	 * we get the "console xxx enabled" message on all the consoles -
	 * boot consoles, real consoles, etc - this is to ensure that end
	 * users know there might be something in the kernel's log buffer that
	 * went to the bootconsole (that they do not see on the real console)
	 */
	if (bcon &&
		((newcon->flags & (CON_CONSDEV | CON_BOOT)) == CON_CONSDEV) &&
		!keep_bootcon) {
		/* we need to iterate through twice, to make sure we print
		 * everything out, before we unregister the console(s)
		 */
		printk(KERN_INFO "console [%s%d] enabled, bootconsole disabled\n",
			newcon->name, newcon->index);
		for_each_console(bcon)
			if (bcon->flags & CON_BOOT)
				unregister_console(bcon);
	} else {
		printk(KERN_INFO "%sconsole [%s%d] enabled\n",
			(newcon->flags & CON_BOOT) ? "boot" : "" ,
			newcon->name, newcon->index);
	}
}
EXPORT_SYMBOL(register_console);


到这里控制台就完成了注册。

 

 

 

5.总结

通过第2点将控制台相应的真是串口如ttyS0\ttyUSB0的信息存储到全局结构体console_cmdline[]中。
通过第4点注册控制台,其实就是从注册的控制台设备(.name = "ttyS",.name = "tty",)与全局结构体console_cmdline中的内容比较,成功就是创建该设备作为控制台

本文最需了解的是__setup_start、 __setup_end,__con_initcall_start、 __con_initcall_end在system.map的意义、功能,同时需清楚__setup("console=", console_setup)、 console_initcall(con_init)、console_initcall(nuc970serial_console_init);

理解这几个函数的意义对于内核的架构设计还是很有意义的!

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值