问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

linux kernel 没有输出信息 怎么调试

发布网友 发布时间:2022-04-24 12:57

我来回答

2个回答

热心网友 时间:2022-04-28 05:05

最近工作在调试usb虚拟串口,让其作为kernel启动的调试串口,以及user空间的输入输出控制台。
利用这个机会,学习下printk如何选择往哪个console输出以及user空间下控制台如何选择,记录与此,与大家共享,也方便自己以后翻阅。
Kernel版本号:3.4.55
依照我的思路(还是时间顺序)分了4部分,指定kernel调试console , kernel下printk console的选择 ,kernel下console的注册,user空间console的选择。

一 指定kernel调试console
首先看kernel启动时如何获取和处理指定的console参数。
kernel的启动参数cmdline可以指定调试console,如指定‘console=ttyS0,115200’,
kernel如何解析cmdline,我之前写了一篇博文如下:
http://blog.csdn.net/skyflying2012/article/details/41142801

根据之前的分析,cmdline中有console=xxx,start_kernel中parse_args遍历.init.setup段所有obs_kernel_param。
kernel/printk.c中注册了‘console=’的解析函数console_setup(注册了obs_kernel_param),所以匹配成功,会调用console_setup来解析,如下:

[cpp] view plain copy
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);

参数是console=的值字符串,如“ttyS0,115200”,console_setup对console=参数值做解析,以ttyS0,115200为例,最后buf=“ttyS”,idx=0,options="115200",brl_options=NULL。调用__add_preferred_console如下:

[cpp] view plain copy
/*
* If exclusive_console is non-NULL then only this console is to be printed to.
*/
static struct console *exclusive_console;

/*
* Array of consoles built from command line options (console=)
*/
struct console_cmdline
{
char name[8]; /* Name of the driver */
int index; /* Minor dev. to use */
char *options; /* Options for the driver */
#ifdef CONFIG_A11Y_BRAILLE_CONSOLE
char *brl_options; /* Options for braille driver */
#endif
};

#define MAX_CMDLINECONSOLES 8

static struct console_cmdline console_cmdline[MAX_CMDLINECONSOLES];
static int selected_console = -1;
static int preferred_console = -1;
int console_set_on_cmdline;
EXPORT_SYMBOL(console_set_on_cmdline);
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;
}

kernel利用结构体数组console_cmdline[8],最多可支持8个cmdline传入的console参数。
__add_preferred_console将name idx options保存到数组下一个成员console_cmdline结构体中,如果数组中已有重名,则不添加,并置selected_console为最新添加的console_cmdline的下标号。
比如cmdline中有“console=ttyS0,115200 console=ttyS1,9600”
则在console_cmdline[8]数组中console_cmdline[0]代表ttyS0,console_cmdline[1]代表ttyS1,而selected_console=1.

二 kernel下printk console的选择
kernel下调试信息是通过printk输出,如果要kernel正常打印,则需要搞明白printk怎么选择输出的设备。
关于printk的实现原理,我在刚工作的时候写过一篇博文,kernel版本是2.6.21的,但是原理还是一致的,可供参考:
http://blog.csdn.net/skyflying2012/article/details/7970341
printk首先将输出内容添加到一个kernel缓冲区中,叫log_buf,log_buf相关代码如下:

[cpp] view plain copy
#define MAX_CMDLINECONSOLES 8

static struct console_cmdline console_cmdline[MAX_CMDLINECONSOLES];
static int selected_console = -1;
static int preferred_console = -1;
int console_set_on_cmdline;
EXPORT_SYMBOL(console_set_on_cmdline);

/* Flag: console code may call schele() */
static int console_may_schele;

#ifdef CONFIG_PRINTK

static char __log_buf[__LOG_BUF_LEN];
static char *log_buf = __log_buf;
static int log_buf_len = __LOG_BUF_LEN;
static unsigned logged_chars; /* Number of chars proced since last read+clear operation */
static int saved_console_loglevel = -1;

log_buf的大小由kernel menuconfig配置,我配置的CONFIG_LOG_BUF_SHIFT为17,则log_buf为128k。

printk内容会一直存在log_buf中,log_buf满了之后则会从头在开始存,覆盖掉原来的数据。
根据printk的实现原理,printk最后调用console_unlock实现log_buf数据刷出到指定设备。
这里先不关心printk如何处理log buf数据(比如添加内容级别),只关心printk如何一步步找到指定的输出设备,根据printk.c代码,可以找到如下线索。
printk->vprintk->console_unlock->call_console_drivers->_call_console_drivers->_call_console_drivers->__call_console_drivers
看线索最底层__call_console_drivers代码。如下:

[cpp] view plain copy
/*
* Call the console drivers on a range of log_buf
*/
static void __call_console_drivers(unsigned start, unsigned end)
{
struct console *con;

for_each_console(con) {
if (exclusive_console && con != exclusive_console)
continue;
if ((con->flags & CON_ENABLED) && con->write &&
(cpu_online(smp_processor_id()) ||
(con->flags & CON_ANYTIME)))
con->write(con, &LOG_BUF(start), end - start);
}
}
for_each_console定义如下:

[cpp] view plain copy
/*
* for_each_console() allows you to iterate on each console
*/
#define for_each_console(con) \
for (con = console_drivers; con != NULL; con = con->next)
遍历console_drivers链表所有console struct,如果有exclusive_console,则调用与exclusive_console一致console的write,
如果exclusive_console为NULL,则调用所有ENABLE的console的write方法将log buf中start到end的内容发出。
可以看出,execlusive_console来指定printk输出唯一console,如果未指定,则向所有enable的console写。
默认情况下execlusive_console=NULL,所以printk默认是向所有enable的console写!
只有一种情况是指定execlusive_console,就是在console注册时,下面会讲到。
到这里就很明了了,kernel下每次printk打印,首先存log_buf,然后遍历console_drivers,找到合适console(execlusive_console或所有enable的),刷出log。
console_drivers链表的成员是哪里来的,谁会指定execulsive_console?接着来看下一部分,kernel下console的注册

热心网友 时间:2022-04-28 06:23

内核中的bug也是多种多样的。它们的产生有无数的原因,同时表象也变化多端。从隐藏在源代码中的错误到展现在目击者面前的bug,其发作往往是一系列连锁反应的事件才可能出发的。虽然内核调试有一定的困难,但是通过你的努力和理解,说不定你会喜欢上这样的挑战。
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
高启强与陈书婷结婚是哪一集 陈舒婷和高启强第几集结婚 高启强陈书婷结婚是第几集 高启强和陈书婷结婚是第几集 高启强和陈书婷第几集结的婚 狂飙高启强第几集和大嫂在一起的 2021年清明节时间(是公历几月几号) js中类似`${xx,xxxy}`的语句是什么意思? 用支付宝帐号注册的淘宝帐号但是淘宝显示未绑定支付宝帐号如图 银行黑户影响子女什么 用EXCEL编制表格,应用函数过多,运算的速度为什么很慢? ubuntu怎么进入GRUB菜单选择那里 我在EXCEL表格中运用了函数后,为什么用表时反应特别特别的慢 ,有没有解决方案 如何在虚拟机中只启动linux的命令行? 表格比较大,里面函数多,电脑计算太慢,请求广大网友支招 为什么用了函数使windows系统变慢? linux系统怎么快速切换文本模式和X环境 linux驱动(内核态)里面如何把&#47;proc&#47;cmdline的内容读出来,fp-&gt;f_op-&gt;read和kernel_read都报错,用不了 cat /proc/cmdline 怎么修改 腾讯视频怎么更换登录 手电钻上的小电容可以保护开关的原理是什么? 电钻上面的开关学名叫什么 手电钻 钻头上的符号是什么意思? 腾讯视频会员怎么更改 手电钻钻头中的1厘,2厘,。。。到底是是多大 手电钻1档和2档的区别 手电钻1到15是什么意思 bosch手电钻1挡2挡代表什么 手电钻上的符号十,一有作用途 无线手电钻上有一环数字1、3、5、7代表什么意思 excel插入函数反应慢怎么办 excel公式太多影响速度 使用excel2016表格时反应很慢怎么办 怎么写入/proc/cmdline中的kernel启动参数 在EXCEL中使用函数计算超过几千行以后,计算或者使用过程都非常的慢。怎么办? excel中如果涉及大量的数据和公式的时候,电脑反应就很慢很慢,并且很容易死机,求帮助. EXCEL中函数多时运行速度慢怎么办? Excel中的函数太多导致运算慢,能不能在一个文件里再分开建立工作表 梦见很久不见的大学同学强行拥抱自己 电脑EXCEL使用COUNTIF函数时反应慢 Excel,打开输入内容反映很慢啊,里面有比较多的函数的。怎么解决啊? 晚上做梦突然间梦到一个平时几乎不接触的同学是怎么回事啊?梦中他还抱着我!? EXCEL表中,采用的函数较多,所以在打开和修改表格时反应很慢,有什么好的解决方法吗? 做梦梦到和男同学睡觉,他还抱着我,什么意思,重点是我们不熟阿,只是一... 用函数多了导致excel文件变大,运行慢了怎么办呀 梦见小时候的同学抱着我,吻我 excel太大,运行缓慢该怎么办? 梦见老同学抱着我转了好几个圈好开心 梦到同学从后面抱着我 梦见老同学小学同桌成了我大学同学,有爬楼梯竟然从背后抱着我让我和他交往,我说我我们这么多年都没来电