Linux print system
linux中的調(diào)試方法有很多種涩澡,但我們最常用的也是最關(guān)鍵的調(diào)試工具應(yīng)該就是使用print函數(shù)與console進(jìn)行信息交互隐砸。在內(nèi)核驅(qū)動(dòng)的調(diào)試過(guò)程我們一般會(huì)使用內(nèi)核提供的printk函數(shù)來(lái)進(jìn)行調(diào)試寂嘉,在應(yīng)用層時(shí)我們會(huì)使用標(biāo)準(zhǔn)的c庫(kù)函數(shù)printf,在shell腳本中我們會(huì)使用echo/logger等工具,接下來(lái)就讓我們逐步了解這些工具吧虏肾。
1.內(nèi)核printk函數(shù)工作原理
printk函數(shù)是內(nèi)核提供的格式化打印函數(shù)冠蒋,它幾乎可以在內(nèi)核的任意時(shí)刻使用(中斷上下文羽圃、進(jìn)程上下文、持有鎖時(shí)抖剿、多處理器處理時(shí))朽寞。但也有一些地方不能使用printk函數(shù)進(jìn)行打印,如系統(tǒng)啟動(dòng)過(guò)程中和console_init之前斩郎,就不能直接調(diào)用printk脑融,如果確實(shí)需要調(diào)試這段代碼,內(nèi)核提供了一個(gè)新的接口函數(shù)early_printk缩宜,需要我們?cè)?config中將其打開(kāi)肘迎,具體實(shí)現(xiàn)方式自行分析下,這邊對(duì)printk函數(shù)進(jìn)行具體分析锻煌。
printk的實(shí)現(xiàn)函數(shù)位于 kernel/printk.c
中(較新版本的內(nèi)核位于kernel/printk/printk.c
中妓布,原理一樣),如下:
asmlinkage int printk(const char *fmt, ...)
{
va_list args;
int r;
va_start(args, fmt);
r = vprintk(fmt, args);
va_end(args);
return r;
}
va_start
和va_end
是在讀取可變參數(shù)中常用的函數(shù)宋梧,主要就是將*fmt中的數(shù)據(jù)保存到args中匣沼,接著看vprintk(fmt, args)
函數(shù),如下:
asmlinkage int vprintk(const char *fmt, va_list args)
{
int printed_len = 0;
int current_log_level = default_message_loglevel;
unsigned long flags;
int this_cpu;
char *p;
boot_delay_msec();
printk_delay();
/*關(guān)閉內(nèi)核搶占*/
preempt_disable();
/*保存中斷信息捂龄,獲取cpu id*/
raw_local_irq_save(flags);
this_cpu = smp_processor_id();
/*
* Ouch, printk recursed into itself!
*/
/*CPU crash時(shí)的保護(hù)處理機(jī)制*/
if (unlikely(printk_cpu == this_cpu)) {
/*
* If a crash is occurring during printk() on this CPU,
* then try to get the crash message out but make sure
* we can't deadlock. Otherwise just return to avoid the
* recursion and return - but flag the recursion so that
* it can be printed at the next appropriate moment:
*/
if (!oops_in_progress) {
recursion_bug = 1;
goto out_restore_irqs;
}
zap_locks();
}
lockdep_off();
spin_lock(&logbuf_lock);
printk_cpu = this_cpu;
if (recursion_bug) {
recursion_bug = 0;
strcpy(printk_buf, recursion_bug_msg);
printed_len = strlen(recursion_bug_msg);
}
/* Emit the output into the temporary buffer */
/*將要輸出的字符串放入printk_buf中释涛,并返回字符長(zhǎng)度*/
printed_len += vscnprintf(printk_buf + printed_len,
sizeof(printk_buf) - printed_len, fmt, args);
p = printk_buf;
/* Do we have a loglevel in the string? */
/*如果調(diào)用者沒(méi)有提供合適的日志級(jí)別加叁,則插入默認(rèn)級(jí)別*/
if (p[0] == '<') {
unsigned char c = p[1];
if (c && p[2] == '>') {
switch (c) {
case '0' ... '7': /* loglevel */
current_log_level = c - '0';
/* Fallthrough - make sure we're on a new line */
case 'd': /* KERN_DEFAULT */
if (!new_text_line) {
emit_log_char('\n');
new_text_line = 1;
}
/* Fallthrough - skip the loglevel */
case 'c': /* KERN_CONT */
p += 3;
break;
}
}
}
/*
* Copy the output into log_buf. If the caller didn't provide
* appropriate log level tags, we insert them here
*/
/*拷貝printk_buf數(shù)據(jù)到環(huán)形緩沖區(qū)中,拷貝過(guò)程由函數(shù)emit_log_char實(shí)現(xiàn)唇撬,每次拷貝一個(gè)字節(jié)*/
for ( ; *p; p++) {
if (new_text_line) {
/* Always output the token */
emit_log_char('<');
emit_log_char(current_log_level + '0');
emit_log_char('>');
printed_len += 3;
new_text_line = 0;
if (printk_time) {
/* Follow the token with the time */
char tbuf[50], *tp;
unsigned tlen;
unsigned long long t;
unsigned long nanosec_rem;
t = cpu_clock(printk_cpu);
nanosec_rem = do_div(t, 1000000000);
tlen = sprintf(tbuf, "[%5lu.%06lu] ",
(unsigned long) t,
nanosec_rem / 1000);
for (tp = tbuf; tp < tbuf + tlen; tp++)
emit_log_char(*tp);
printed_len += tlen;
}
if (!*p)
break;
}
emit_log_char(*p);
if (*p == '\n')
new_text_line = 1;
}
/*
* Try to acquire and then immediately release the
* console semaphore. The release will do all the
* actual magic (print out buffers, wake up klogd,
* etc).
*
* The acquire_console_semaphore_for_printk() function
* will release 'logbuf_lock' regardless of whether it
* actually gets the semaphore or not.
*/
/*獲取console結(jié)構(gòu)的信號(hào)量*/
if (acquire_console_semaphore_for_printk(this_cpu))
/*將信息傳給console驅(qū)動(dòng)*/
release_console_sem();
lockdep_on();
out_restore_irqs:
/*恢復(fù)中斷信息*/
raw_local_irq_restore(flags);
/*開(kāi)啟內(nèi)核搶占*/
preempt_enable();
return printed_len;
}
vprintk函數(shù)比較長(zhǎng)一點(diǎn)殉农,但其執(zhí)行的流程其實(shí)比較直觀:
- 關(guān)閉內(nèi)核搶占,保存中斷信息局荚;
- 將數(shù)據(jù)保存到臨時(shí)緩沖
printk_buf
中超凳,并將日志級(jí)別信息添加進(jìn)buf,如果沒(méi)定義則以默認(rèn)的級(jí)別添加進(jìn)去耀态,注意printk_buf只有1KB轮傍; - 通過(guò)
emit_log_char()
函數(shù)將printk_buf
中的數(shù)據(jù)以一個(gè)一個(gè)char的形式傳給log_buf; - 獲取console信號(hào)量首装,然后調(diào)用
release_console_sem()
函數(shù)创夜,該函數(shù)主要就是調(diào)用console驅(qū)動(dòng),將buf的內(nèi)容通過(guò)console的write接口傳輸出去仙逻; - 最后就是恢復(fù)中斷信息驰吓,打開(kāi)內(nèi)核搶占。
通過(guò)上面的流程我們應(yīng)該已經(jīng)了解printk函數(shù)的實(shí)現(xiàn)系奉,不過(guò)還是有幾個(gè)細(xì)節(jié)下面再提及:
1.環(huán)形緩沖區(qū)
環(huán)形緩沖區(qū)的定義#define LOG_BUF(idx) (log_buf[(idx) & LOG_BUF_MASK])
檬贰,接著追蹤下去,可以發(fā)現(xiàn)#define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT)
缺亮,其中CONFIG_LOG_BUF_SHIFT
用來(lái)限制環(huán)形緩沖區(qū)的大小翁涤,默認(rèn)是17,即128KB萌踱,可以在.config中進(jìn)行配置調(diào)整葵礼。
這邊的log_buf(128KB)
和上面的printk_buf(1KB)
是不一樣的,printk_buf
更像是臨時(shí)緩沖區(qū)并鸵,每次調(diào)用printk函數(shù)最大量是1KB鸳粉,當(dāng)重新調(diào)用printk函數(shù)時(shí)會(huì)將上次的清空,寫(xiě)入新的數(shù)據(jù)到printk_buf
园担,log_buf
是一個(gè)環(huán)形緩沖區(qū)届谈,它的數(shù)據(jù)是printk_buf
給它的,但是當(dāng)新的數(shù)據(jù)寫(xiě)入時(shí)粉铐,原本的數(shù)據(jù)不會(huì)被清空疼约,而是一直往后寫(xiě),當(dāng)log_buf寫(xiě)滿128KB時(shí)就環(huán)形寫(xiě)入蝙泼,從頭部開(kāi)始覆蓋寫(xiě)入程剥。
2.emit_log_char()函數(shù)
該函數(shù)實(shí)現(xiàn)的是每次寫(xiě)入一個(gè)字符,即對(duì)于每一個(gè)字符的賦值log_end則只管++,在加一之后進(jìn)行判斷织鲸,如果log_end的值大于log_start舔腾,則表示緩沖區(qū)的長(zhǎng)度已經(jīng)達(dá)到最大,下一次的寫(xiě)入就將覆蓋之前最舊的位置搂擦,因此log_start = log_end - log_buf_len
,將log_start的位置向后移一位(因?yàn)槊看沃粚?xiě)入一個(gè)字符稳诚,不可能超過(guò)一位)實(shí)現(xiàn)環(huán)形緩存區(qū),代碼如下:
static void emit_log_char(char c)
{
LOG_BUF(log_end) = c;
log_end++;
if (log_end - log_start > log_buf_len)
log_start = log_end - log_buf_len;
if (log_end - con_start > log_buf_len)
con_start = log_end - log_buf_len;
if (logged_chars < log_buf_len)
logged_chars++;
}
3.release_console_sem()函數(shù)
該函數(shù)完成console相關(guān)的操作瀑踢,主要過(guò)程就是將con_start
與log_end
間的數(shù)據(jù)通過(guò)call_console_drivers()
函數(shù)來(lái)完成數(shù)據(jù)往console的傳遞扳还,并且在最后環(huán)形klogd進(jìn)程。
void release_console_sem(void)
{
unsigned long flags;
unsigned _con_start, _log_end;
unsigned wake_klogd = 0;
if (console_suspended) {
up(&console_sem);
return;
}
console_may_schedule = 0;
for ( ; ; ) {
spin_lock_irqsave(&logbuf_lock, flags);
wake_klogd |= log_start - log_end;
if (con_start == log_end)
break; /* Nothing to print */
_con_start = con_start;
_log_end = log_end;
con_start = log_end; /* Flush */
spin_unlock(&logbuf_lock);
stop_critical_timings(); /* don't trace print latency */
call_console_drivers(_con_start, _log_end);
start_critical_timings();
local_irq_restore(flags);
}
console_locked = 0;
up(&console_sem);
spin_unlock_irqrestore(&logbuf_lock, flags);
if (wake_klogd)
wake_up_klogd();
}
call_console_drivers()
函數(shù)最終會(huì)調(diào)用__call_console_drivers
函數(shù)橱夭,該函數(shù)指向struct console *con氨距;
設(shè)備,最終通過(guò)設(shè)備寫(xiě)函數(shù)棘劣,進(jìn)行寫(xiě)如數(shù)據(jù)俏让,如下:
static void __call_console_drivers(unsigned start, unsigned end)
{
struct console *con;
for_each_console(con) {
if ((con->flags & CON_ENABLED) && con->write &&
(cpu_online(smp_processor_id()) ||
(con->flags & CON_ANYTIME)))
con->write(con, &LOG_BUF(start), end - start);
}
}
至此printk函數(shù)的實(shí)現(xiàn)流程已經(jīng)完成,當(dāng)然中間有很多細(xì)節(jié)之處還不是很了解茬暇,需要進(jìn)一步深入首昔。
2.內(nèi)核printk函數(shù)打印優(yōu)先級(jí)
上面我們提到了printk函數(shù)日志的優(yōu)先級(jí),下面進(jìn)行分析糙俗,在console上我們通過(guò)cat /proc/sys/kernel/printk
文件來(lái)查看內(nèi)核模塊打印的優(yōu)先級(jí)
# cat /proc/sys/kernel/printk
7 4 1 7
該文件里面有四個(gè)數(shù)值勒奇,這四個(gè)值是在kernel/printk.c
中被定義的,如下:
int console_printk[4] = {
DEFAULT_CONSOLE_LOGLEVEL, /* console_loglevel */
DEFAULT_MESSAGE_LOGLEVEL, /* default_message_loglevel */
MINIMUM_CONSOLE_LOGLEVEL, /* minimum_console_loglevel */
DEFAULT_CONSOLE_LOGLEVEL, /* default_console_loglevel */
};
分別對(duì)應(yīng):
- 控制臺(tái)日志級(jí)別:優(yōu)先級(jí)高于該值的消息將被打印至控制臺(tái)
- 默認(rèn)的消息日志級(jí)別:將用該優(yōu)先級(jí)來(lái)打印沒(méi)有優(yōu)先級(jí)的消息
- 最低的控制臺(tái)日志級(jí)別:控制臺(tái)日志級(jí)別可被設(shè)置的最小值(最高優(yōu)先級(jí))
- 默認(rèn)的控制臺(tái)日志級(jí)別:控制臺(tái)日志級(jí)別的缺省值
在include/linux/printk.h
中內(nèi)核一共定義了8個(gè)printk級(jí)別臼节,數(shù)值越大優(yōu)先級(jí)越低撬陵,如下:
#define KERN_EMERG "<0>" /* system is unusable */
#define KERN_ALERT "<1>" /* action must be taken immediately */
#define KERN_CRIT "<2>" /* critical conditions */
#define KERN_ERR "<3>" /* error conditions */
#define KERN_WARNING "<4>" /* warning conditions */
#define KERN_NOTICE "<5>" /* normal but significant condition */
#define KERN_INFO "<6>" /* informational */
#define KERN_DEBUG "<7>" /* debug-level messages */
實(shí)際應(yīng)用時(shí):
- 我們會(huì)先把
DEFAULT_CONSOLE_LOGLEVEL
設(shè)置為8,這樣優(yōu)先級(jí)高于8的都會(huì)被打印到console(數(shù)值小于8)网缝;因?yàn)閜rintk的最低級(jí)為7,則代表所有的log都會(huì)print到console上進(jìn)行調(diào)試蟋定,待我們調(diào)試完成后粉臊,將DEFAULT_CONSOLE_LOGLEVEL
的值設(shè)置為低于我們要打印的級(jí)別,這樣就不會(huì)有那么多沒(méi)必要的log了驶兜。 - 可以通過(guò)echo的方式來(lái)直接修改log的優(yōu)先級(jí)扼仲,如
echo 4 4 1 7 > /proc/sys/kernel/printk
- 當(dāng)輸入
printk("Hello, world!\n")
沒(méi)有帶任何優(yōu)先級(jí),則會(huì)采用默認(rèn)級(jí)別抄淑,默認(rèn)級(jí)別為4屠凶,和輸入printk(KERN_WARNING"Hello, world!\n")
的效果是一致的。 - Debug信息設(shè)置為KERN_DEBUG肆资,即
printk(KERN_DEBUG"Hello, world!\n")
矗愧,但我們觀察到內(nèi)核中都不會(huì)直接調(diào)用這個(gè)接口,而是重新提供了一個(gè)debug的接口函數(shù)郑原,pr_debug(fmt, ...)
唉韭,這個(gè)將在下一部分進(jìn)行講解夜涕。
在cat /proc/sys/kernel/printk
文件的時(shí)候,發(fā)現(xiàn)該文件下面還有幾個(gè)與printk相關(guān)的文件属愤,如下:
cat /proc/sys/kernel/printk
7 4 1 7
cat /proc/sys/kernel/printk_delay
0
cat /proc/sys/kernel/printk_ratelimit
5
cat /proc/sys/kernel/printk_ratelimit_burst
10
這些文件分別表示:
-
printk_delay
中的值表示printk消息之間延遲的毫秒數(shù)女器,用于提高某些場(chǎng)景的可讀寫(xiě),如打印的太快了住诸,我們也不可能從這種狂刷屏幕上讀取到什么有效信息驾胆,所以減慢打印速度。 -
printk_ratelimit
和printk_ratelimit_burst
也有點(diǎn)這個(gè)調(diào)慢速度的作用贱呐,如上面的printk_ratelimit = 5,printk_ratelimit_burst = 10
俏拱,則表示當(dāng)內(nèi)核中由printk_ratelimit()
函數(shù)限制的打印信息每打印10次就會(huì)停止5s不再向console輸出。
下面是一個(gè)測(cè)試的例子:
for (i = 0 ; i < 1000; i ++){
if(printk_ratelimit()){
printk(KERN_DEBUG "Test for ratelimte i = %d j = %d\n",i ,++j);
}
}
printk(KERN_NOTICE "After Test i = %d j = %d\n",i , j);
printk_ratelimit()
根據(jù)打印的頻繁程度返回的一個(gè)值決定是否將debug信息打印出來(lái)吼句。整個(gè)處理機(jī)制是锅必,系統(tǒng)根據(jù)printk_ratelimit_burst
的值設(shè)置一個(gè)隊(duì)列長(zhǎng)度,如果這個(gè)隊(duì)列滿惕艳,則值printk_ratelimit()
為0搞隐,禁止新的消息加入隊(duì)列,等待printk_ratelimit
秒設(shè)定的時(shí)間远搪,將 printk_ratelimit()
設(shè)為1劣纲,即允許新的消息加入隊(duì)列,進(jìn)行再次打印谁鳍。所以在上面的例子癞季,我們看到輸出為10次。
3.內(nèi)核動(dòng)態(tài)debug機(jī)制pr_debug函數(shù)的使用
pr_debug(fmt, ...)
函數(shù)的定義位于include/linux/printk.h中倘潜,如下:
/* If you are writing a driver, please use dev_dbg instead */
#if defined(DEBUG)
#define pr_debug(fmt, ...) \
printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
#elif defined(CONFIG_DYNAMIC_DEBUG)
/* dynamic_pr_debug() uses pr_fmt() internally so we don't need it here */
#define pr_debug(fmt, ...) \
dynamic_pr_debug(fmt, ##__VA_ARGS__)
#else
#define pr_debug(fmt, ...) \
no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
#endif
從上面可以看到pr_debug(fmt, ...)
函數(shù)一共會(huì)有三種方式:
- 如果定義了DEBUG绷柒,則使用
printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
,該函數(shù)即上面所說(shuō)的方式涮因。 - 如果沒(méi)定義DEBUG而定義了
CONFIG_DYNAMIC_DEBUG
废睦,則使用dynamic_pr_debug(fmt, ##__VA_ARGS__)
,該函數(shù)為內(nèi)核動(dòng)態(tài)debug機(jī)制养泡。 - 如果都沒(méi)定義嗜湃,則使用
no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
,該函數(shù)會(huì)直接返回0澜掩。
在實(shí)際應(yīng)用中我們都使用第二種方法购披,內(nèi)核動(dòng)態(tài)debug機(jī)制,這種方法可以按模塊來(lái)確認(rèn)是否打印該模塊的調(diào)試信息肩榕,而第一種方式會(huì)將所有模塊的debug信息都打印出來(lái)刚陡,我們可以做一個(gè)實(shí)驗(yàn),在include/linux/printk.h
的最上面定義#define DEBUG,并把DEFAULT_CONSOLE_LOGLEVEL
設(shè)置為8橘荠,啟動(dòng)后我們會(huì)看到console上有成千上萬(wàn)的log屿附,想找到有用的信息都難,這就是因?yàn)槎x了DEBUG后內(nèi)核中所有的模塊debug都被開(kāi)放了哥童。
下面就說(shuō)說(shuō)怎樣添加需要調(diào)試的模塊添加動(dòng)態(tài)debug機(jī)制挺份,以打開(kāi)char驅(qū)動(dòng)的debug機(jī)制為例:
- 配置Kconfig,添加要調(diào)試驅(qū)動(dòng)模塊的DEBUG選項(xiàng)贮懈,在driver/char目錄下的Kconfig中添加如下代碼:
config OPEN_CHAR_DEBUG
. bool "Support OPEN CHAR DEBUG"
. depends on ARM
. help
. this is DEBUG function
- 配置Makefile匀泊,添加編譯支持驅(qū)動(dòng)模塊
EXTRA_CFLAGS += -DDEBUG
在driver/char目錄下的Makefile中添加如下代碼:
. ifeq($(CONFIG_OPEN_CHAR_DEBUG), y)
. EXTRA_CFLAGS += ‐DDEBUG
. endif
- 在內(nèi)核的.config文件中添加該模塊的支持
CONFIG_LINYE_CHAR_DEBUG=y
,或者通過(guò)make configure來(lái)選中新增加的這個(gè)模塊朵你。
通過(guò)這三步設(shè)置我們就可以在driver/char下面使用pr_debug(fmt, ...)
函數(shù)了各聘。
4.printf與printk函數(shù)的區(qū)別
在文章的開(kāi)頭已經(jīng)有說(shuō)道“內(nèi)核驅(qū)動(dòng)的調(diào)試過(guò)程我們一般會(huì)使用內(nèi)核提供的printk函數(shù)來(lái)進(jìn)行調(diào)試,在應(yīng)用層時(shí)我們會(huì)使用標(biāo)準(zhǔn)的c庫(kù)函數(shù)printf“所以它們之間最大的區(qū)別就是抡医,在Linux內(nèi)核是不支持printf函數(shù)躲因,在應(yīng)用層也是無(wú)法使用printk函數(shù)。
1.下面列舉出一些區(qū)別
- printk()函數(shù)是直接使用了向終端設(shè)備寫(xiě)函數(shù)tty_write()忌傻。而printf()函數(shù)是調(diào)用write()系統(tǒng)調(diào)用函數(shù)向標(biāo)準(zhǔn)輸出設(shè)備寫(xiě)大脉。所以在用戶態(tài)(如進(jìn)程0)不能夠直接使用printk()函數(shù),而在內(nèi)核態(tài)由于他已是特權(quán)級(jí)水孩,所以無(wú)需系統(tǒng)調(diào)用來(lái)改變特權(quán)級(jí)镰矿,因而能夠直接使用 printk()函數(shù)。
- printk可以以日志等級(jí)的形式進(jìn)行打印信息俘种。
- printk無(wú)法打印浮點(diǎn)型數(shù)據(jù)秤标,printf可以打印浮點(diǎn)型數(shù)據(jù)。
- printk是“行驅(qū)動(dòng)”的宙刘,說(shuō)只有收到一個(gè)換行符數(shù)據(jù)才會(huì)真正輸出到終端苍姜,否則就不會(huì)有任何信息輸出。
2.幾個(gè)常用的字符格式化函數(shù)
- sprintf
把格式化的字符串輸出到指定字符串中(char buffer[50])荐类,例:
sprintf( buffer,"String:%s", "abc");`則`buffer = "String:abc"
- vsprintf
把參數(shù)可變的字符串進(jìn)行格式化后輸出到指定字符串中怖现,例:
void log(const char* format, ... )
{
char buf[50]
va_list args;
va_start (args, format);
vfprintf (buf, format, args);
va_end (args);
}
- fprintf
把格式化的字符串輸出到指定文件設(shè)備中(FILE* stream),例:
void log(void)
{
FILE* stream;
stream = fopen("fprintf.out", "w");
fprintf(stream, "%s%d", "aa", 1);
fclose(stream);
}
- vfprintf
把參數(shù)可變的字符串進(jìn)行格式化后輸出到指定文件設(shè)備中玉罐,例:
void log(FILE *file, const char* format, ... )
{
va_list args;
va_start (args, format);
vfprintf (file, format, args);
va_end (args);
}
5.shell 中打印調(diào)試信息的方法
在寫(xiě)shell腳本的時(shí)候,有時(shí)也需要打印log到console上進(jìn)行觀察潘拨,下面列出幾種方法:
1.echo
最直接的方式就是這節(jié)在需要打印的位置使用echo函數(shù)寫(xiě)數(shù)據(jù)到/dev/console進(jìn)行顯示吊输,例:
echo "this is debug meg!" > /dev/console
num=1
echo $num > /dev/console
2.logger
logger其實(shí)使用的是系統(tǒng)日志模塊syslog,這里面感覺(jué)內(nèi)容比較多铁追,后面單獨(dú)做一個(gè)總結(jié)季蚂,這邊先不過(guò)多介紹。
3.shell調(diào)試
shell也有一個(gè)真實(shí)的調(diào)試模式:
1、如果在腳本"test.sh" 中有錯(cuò)誤扭屁,可以這樣來(lái)進(jìn)行調(diào)試:
sh -n test.sh算谈,這將返回所有語(yǔ)法錯(cuò)誤。
2料滥、如果想看腳本"test.sh" 中所有變量的值然眼,可以這樣來(lái)進(jìn)行調(diào)試:
sh -x test.sh,這將執(zhí)行該腳本并顯示所有變量的值葵腹,每5秒刷新一次高每。
3、sh -v test.sh践宴,一邊執(zhí)行腳本鲸匿,一邊將執(zhí)行過(guò)的腳本命令打印到標(biāo)準(zhǔn)錯(cuò)誤輸出,如果哪邊出錯(cuò)則停止阻肩。
6.tips
1带欢、如何在每條打印信息前添加時(shí)間參數(shù)
在.comfig里面添加CONFIG_PRINTK_TIME = y
2、通過(guò)dmesg可以查看log_buf內(nèi)核環(huán)形緩存區(qū)的數(shù)據(jù)
Linux print system的分析就到這邊烤惊,有感悟時(shí)會(huì)持續(xù)會(huì)更新乔煞。
注:以上內(nèi)容都是本人在學(xué)習(xí)過(guò)程積累的一些心得,難免會(huì)有參考到其他文章的一些知識(shí)撕氧,如有侵權(quán)瘤缩,請(qǐng)及時(shí)通知我,我將及時(shí)刪除或標(biāo)注內(nèi)容出處伦泥,如有錯(cuò)誤之處也請(qǐng)指出剥啤,進(jìn)行探討學(xué)習(xí)。文章只是起一個(gè)引導(dǎo)作用不脯,詳細(xì)的數(shù)據(jù)解析內(nèi)容還請(qǐng)查看Linux相關(guān)教程府怯,感謝您的查閱。