鞏 琛 蔡 文
(上海師范大學(xué)信息與機(jī)電工程學(xué)院 上海 200234)
?
基于ARM的Linux驅(qū)動(dòng)調(diào)試技術(shù)研究
鞏琛蔡文
(上海師范大學(xué)信息與機(jī)電工程學(xué)院上海 200234)
在ARM上進(jìn)行Linux驅(qū)動(dòng)移植時(shí),要對(duì)Linux內(nèi)核代碼進(jìn)行修改、刪減或添加,但這樣做在運(yùn)行時(shí)可能會(huì)遇到很多意想不到的錯(cuò)誤,這時(shí)就需要去調(diào)試代碼以找到出錯(cuò)的原因和位置。針對(duì)這一需要,提出并實(shí)現(xiàn)兩種新的調(diào)試技術(shù):第一種構(gòu)造一個(gè)打印函數(shù),把添加的打印信息單獨(dú)存儲(chǔ),然后借助proc文件系統(tǒng)將其輸出,實(shí)現(xiàn)了外加打印信息與內(nèi)核自身打印信息的分離,使查找更加方便;其次利用系統(tǒng)時(shí)鐘中斷永不停息的特性確定系統(tǒng)僵死的位置。通過(guò)實(shí)驗(yàn)表明,該技術(shù)能快速有效地找到死循環(huán)的位置,省去了大量查找和分析代碼的工作。
Linux調(diào)試proc文件系統(tǒng)系統(tǒng)時(shí)鐘中斷
如今,以ARM為核心處理器,搭載Linux操作系統(tǒng)的嵌入式產(chǎn)品越來(lái)越多,應(yīng)用場(chǎng)景越來(lái)越廣泛[1]。但由于硬件平臺(tái)的多樣性,并沒(méi)有一套通用的Linux操作系統(tǒng)。開發(fā)相應(yīng)的嵌入式產(chǎn)品時(shí),需要工程師根據(jù)硬件平臺(tái)外圍設(shè)備的特性對(duì)Linux內(nèi)核源碼進(jìn)行增減。這樣一個(gè)移植過(guò)程中會(huì)出現(xiàn)各種各樣的bug,此時(shí)就需要開發(fā)者運(yùn)用各種調(diào)試手段去找到出錯(cuò)的地方和原因。本文基于Linux+ARM的實(shí)驗(yàn)平臺(tái)(Linux選取2.6.31.14的版本;ARM架構(gòu)芯片采用三星公司的s3c2440;BootLoader采用U-boot,根文件系統(tǒng)用Busybox創(chuàng)建[2,3]),提出并實(shí)現(xiàn)兩種新的驅(qū)動(dòng)調(diào)試技術(shù)。文中涉及到的交叉開發(fā)方面的知識(shí)不作詳述。
1.1整體思想概述
在調(diào)試Linux驅(qū)動(dòng)時(shí)在程序里用printk()添加打印語(yǔ)句是最常用的方法[4]。首先,Linux內(nèi)核會(huì)在內(nèi)核空間分配一段靜態(tài)緩沖區(qū),作為顯示用的空間。然后調(diào)用sprintf,格式化顯示字符串,最后調(diào)用tty_write向終端進(jìn)行信息的顯示。執(zhí)行dmesg命令,就可把緩沖區(qū)里面的打印信息重新顯示在硬件終端上。dmesg命令的實(shí)質(zhì)是打開/proc/kmsg文件。
Linux內(nèi)核源碼多用printk()打印信息,而開發(fā)人員在調(diào)試驅(qū)動(dòng)時(shí)也常常會(huì)用printk()添加調(diào)試信息。當(dāng)開發(fā)者想回頭查看自己添加的打印信息時(shí),用簡(jiǎn)單的cat命令去打開 /proc/kmsg文件即可。但此時(shí)顯示的是與內(nèi)核打印信息混雜在一起的結(jié)果,查找起來(lái)很不方便。cat命令的原理是先執(zhí)行open系統(tǒng)調(diào)用打開某個(gè)文件,然后再在一個(gè)while()里用read系統(tǒng)調(diào)用循環(huán)地讀取該文件里面的內(nèi)容,然后把內(nèi)容顯示到硬件輸出端。cat命令的機(jī)制原理不是本文的重點(diǎn),這里只是簡(jiǎn)單介紹一下,便于后面代碼的理解。
基于此問(wèn)題,本文構(gòu)造出一個(gè)私有打印函數(shù)pvt_printk(),把自己添加的打印語(yǔ)句單獨(dú)存儲(chǔ)到一段緩沖區(qū)pvt_buf里面去。然后利用內(nèi)核的proc機(jī)制創(chuàng)建一個(gè)與kmsg類似的文件pvt_kmsg。當(dāng)去執(zhí)行cat/proc/pvt_kmsg時(shí),可把pvt_buf緩沖區(qū)里面的數(shù)據(jù)顯示出來(lái)。
1.2proc文件系統(tǒng)
本調(diào)試技術(shù)用到了Linux內(nèi)核的proc虛擬文件系統(tǒng),它是一種在用戶態(tài)檢查內(nèi)核狀態(tài)的機(jī)制。里面的內(nèi)容是內(nèi)核動(dòng)態(tài)生成的。它的存在主要是想通過(guò)這樣一種渠道把內(nèi)核的一些狀態(tài)信息反饋給用戶,讓用戶能夠了解到內(nèi)核運(yùn)行的一些狀況[5]。
Linux內(nèi)核當(dāng)中使用structproc_dir_entry結(jié)構(gòu)體來(lái)表示和描述/proc目錄下的一個(gè)文件或者目錄。如下:
structproc_dir_entry
{
…… ……
conststructfile_operations*proc_fops;
…… ……
}
這個(gè)結(jié)構(gòu)里面成員比較多,其他的不用太關(guān)心,這里著重介紹file_operations這個(gè)結(jié)構(gòu)體,它與對(duì)proc文件的具體操作相對(duì)應(yīng)[5]。Linux內(nèi)核當(dāng)中是用create_proc_entry這個(gè)函數(shù)去創(chuàng)建一個(gè)proc文件,返回值為一個(gè)proc_dir_entry結(jié)構(gòu)體指針。
structproc_dir_entry*create_proc_entry(constchar
*name,mode_tmode,structproc_dir_entry*parent)
name: 要?jiǎng)?chuàng)建的文件名。
mode: 要?jiǎng)?chuàng)建的文件屬性。
parent: 這個(gè)文件的父目錄。如果為NULL,便直接在proc的根目錄下面去創(chuàng)建文件。
file_operations這個(gè)結(jié)構(gòu)體的成員也很多,本文只用到open、read。
structfile_operations
{
……;
ssize_t(*read) (structfile*,char__user*,size_t,loff_t*);
ssize_t(*write) (structfile*,constchar__user*,size_t,loff_t*);
int(*open) (structinode*,structfile*);
……;
}
當(dāng)應(yīng)用程序(本實(shí)驗(yàn)就是cat命令)對(duì)一個(gè)proc文件執(zhí)行open、read、write系統(tǒng)調(diào)用時(shí)[6],程序會(huì)通過(guò)SWI指令,最終調(diào)用到與此文件相對(duì)應(yīng)的file_operations結(jié)構(gòu)體里面的open、read、write函數(shù)。整個(gè)過(guò)程類似于一個(gè)典型的字符型設(shè)備驅(qū)動(dòng)[7],/proc目錄下的文件有點(diǎn)像字符型驅(qū)動(dòng)里面的設(shè)備節(jié)點(diǎn)。
1.3實(shí)現(xiàn)過(guò)程及步驟
創(chuàng)建proc-printk.c文件作為該實(shí)驗(yàn)的驅(qū)動(dòng)源碼文件。經(jīng)前面分析,用到proc文件的描述結(jié)構(gòu)體proc_dir_entry和創(chuàng)建函數(shù)create_proc_entry(),所以程序除了添加字符型驅(qū)動(dòng)所需的頭文件,還需要包含Linux/proc_fs.h。
在驅(qū)動(dòng)初始化函數(shù)里利用create_proc_entry()去創(chuàng)建一個(gè)proc_dir_entry結(jié)構(gòu)體指針pvt_entry,并將其設(shè)為全局變量。如果創(chuàng)建成功,把file_operations結(jié)構(gòu)體proc_pvt_entry_operations的指針賦給pvt_entry所指結(jié)構(gòu)體里面的proc_fops成員。
structproc_dir_entry*pvt_entry;
staticintpvt_kmsg_init(void)
{
pvt_entry=create_proc_entry("pvt_kmsg",S_IRUSR,NULL);
if(pvt_entry)
pvt_entry->proc_fops=&proc_pvt_kmsg_operations;
return0;
}
S_IRUSR是Linux內(nèi)核定義的宏,作用是讓創(chuàng)建的文件的屬性為只讀。
staticvoidpvt_kmsg_exit(void)
{
remove_proc_entry("pvt_kmsg",NULL);/*在出口函數(shù)做相應(yīng)的移除*/
}
module_init(pvt_kmsg_init);
module_exit(pvt_kmsg_exit);
MODULE_LICENSE("GPL");
接著構(gòu)造并填充file_operations結(jié)構(gòu)體proc_pvt_kmsg_operations。
structfile_operationsproc_pvt_kmsg_operations=
{
.read=pvt_kmsg_read,
.open=pvt_kmsg_open,
};
先實(shí)現(xiàn)pvt_kmsg_read函數(shù),pvt_kmsg_open后面再說(shuō)。
staticssize_tpvt_kmsg_read(structfile*file,char__user*buf,size_tcount,loff_t*ppos)
{
inti,ret;
charc;
//文件以非阻塞方式打開并且pvt_buf為空,立刻返回錯(cuò)誤
if((file->f_flags&O_NONBLOCK) &&empty())
return-EAGAIN;
//如果文件以阻塞方式打開又為空,用wait_event_interruptible()讓此進(jìn)程在queue等待隊(duì)列上睡眠[8]
//如果文件以阻塞方式打開有數(shù)據(jù),用__put_user把數(shù)據(jù)讀到用戶空間
if(!wait_event_interruptible(queue,!empty()))
{
for(i=0;i //count是要讀的字節(jié)數(shù) { if(read_pvt_buf(&c)) { ret=__put_user(c,buf); //返回值:0表成功,-EFAULT表錯(cuò)誤 buf++; } } } returnret; } 上述代碼用等待隊(duì)列來(lái)實(shí)現(xiàn)阻塞操作,所以需在文件開頭用宏DECLARE_QUEUEEUE_HEAD(queue)去定義并初始化一個(gè)等待隊(duì)列queue。 定義一個(gè)私有的存儲(chǔ)區(qū)域staticcharpvt_buf[2048],對(duì)它的存貯讀取方式采用數(shù)組環(huán)形緩沖區(qū)。在本設(shè)計(jì)里只有自定義的打印函數(shù)pvt_printk()往里寫數(shù)據(jù),只有cat應(yīng)用程序從里讀數(shù)據(jù)。在這樣僅有一個(gè)讀用戶和一個(gè)寫用戶的情況下,使用環(huán)形緩沖區(qū)的好處是可以不用添加互斥[9]保護(hù)機(jī)制就能保證數(shù)據(jù)的正確性。下面是對(duì)環(huán)形緩沖區(qū)空、滿判斷函數(shù)和讀寫函數(shù)的實(shí)現(xiàn)。 staticcharpvt_buf[2048]; staticintread_p; staticintwrite_p= 0; staticintreadstart_p=0; //每次讀的起始位置 staticintempty() { if(read_p==write_p)return1; elsereturn0; } staticintfull() { if((write_p+ 1)%2048 ==readstart_p)return1; elsereturn0; } staticvoidwrite_pvt_buf(charc) { /*如果數(shù)據(jù)滿了移動(dòng)一位讀的起始位,丟棄一個(gè)數(shù)據(jù) */ if(full()) readstart_p= (readstart_p+ 1) % 2048; pvt_buf[write_p] =c; write_p= (write_p+ 1) % 2048; wake_up_interruptible(&queue); //喚醒進(jìn)程 } staticintread_pvt_buf(char*p) { if(empty())return0; *p=pvt_buf[read_p]; read_p= (read_p+1) % 2048; return1; } 接著實(shí)現(xiàn)私有打印函數(shù)pvt_printk。這里借助于內(nèi)核提供的vsnprintf函數(shù)的存儲(chǔ)功能進(jìn)行改寫。intvsnprintf(char*buf,size_tsize,constchar*fmt,va_listargs)把fmt指向的打印內(nèi)容拷貝到*buf里面。 但為了實(shí)現(xiàn)環(huán)形緩沖區(qū)的順序?qū)懭耄@里不能直接將打印信息拷貝到pvt_buf。而是需先把打印信息存儲(chǔ)到這個(gè)臨時(shí)緩沖區(qū)temp[2048],再把臨時(shí)緩沖區(qū)的信息拷貝到pvt_buf。 staticchartemp[2048]; intpvt_printk(constchar*fmt,…) { va_listargs; intj,k= 0; va_start(args,fmt); /*把打印信息放到臨時(shí)緩沖區(qū)*/ j=vsnprintf(temp,INT_MAX,fmt,args); va_end(args); /*把臨時(shí)緩沖區(qū)的數(shù)據(jù)放到環(huán)形緩沖區(qū)pvt_buf里 */ while(k write_pvt_buf(temp[k]); k++; } returnj; } 到此最初的構(gòu)想已經(jīng)可以實(shí)現(xiàn)了,但是這樣做只能cat一次。因?yàn)樵赾at一次后讀位置已經(jīng)移動(dòng)到最后面沒(méi)有數(shù)據(jù)的地方。所以為了實(shí)現(xiàn)多次可讀,得在每次cat調(diào)用open的時(shí)候把讀位置調(diào)整到起始的地方。 staticintpvt_kmsg_open(structinode*inode,structfile*file) { read_p=readstart_p; return0; } 用EXPORT_SYMBOL(pvt_printk)將pvt_printk函數(shù)導(dǎo)出,供全部?jī)?nèi)核文件使用。 簡(jiǎn)單測(cè)試:在xx驅(qū)動(dòng)源碼里先用externintpvt_printk(constchar*fmt,…);聲明,然后在合適位置添加pvt_printk(“abcdefg”),將此驅(qū)動(dòng)和proc-printk.c都編譯進(jìn)內(nèi)核,把新內(nèi)核燒寫到ARM開發(fā)板并啟動(dòng)。在開發(fā)板的串口終端下先運(yùn)行與xx驅(qū)動(dòng)對(duì)應(yīng)的應(yīng)用程序test1,再去cat創(chuàng)建的proc文件pvt_kmsg,就可以看到添加的信息。如圖1所示。 圖1 第一種調(diào)試方法實(shí)驗(yàn)結(jié)果 2.1原理 當(dāng)驅(qū)動(dòng)程序某個(gè)地方不小心寫入了死循環(huán)的代碼。那么在運(yùn)行相應(yīng)app的時(shí)候Linux系統(tǒng)就會(huì)出現(xiàn)僵死的狀態(tài)。如果在多進(jìn)程的狀況話,直接追查代碼找尋僵死點(diǎn)那將顯得非常困難。 但Linux時(shí)鐘中斷確是永遠(yuǎn)都在發(fā)生的,進(jìn)程的僵死是不會(huì)屏蔽掉系統(tǒng)時(shí)鐘中斷的。就像人一樣,即便是在睡覺,心臟總是在跳動(dòng)的。 Linux系統(tǒng)下的中斷處理過(guò)程如圖2所示。t是時(shí)間軸,一個(gè)進(jìn)程在運(yùn)行著,發(fā)生中斷時(shí),先保存現(xiàn)場(chǎng),再執(zhí)行中斷函數(shù),最后回復(fù)現(xiàn)場(chǎng)[10],只不過(guò)系統(tǒng)時(shí)鐘中斷是周而復(fù)始的這樣執(zhí)行。 圖2 中斷發(fā)生過(guò)程 保存現(xiàn)場(chǎng)其實(shí)就是保存各個(gè)寄存器的值。其中pc寄存器(ARM里由r15寄存器充當(dāng))就保存了執(zhí)行中的驅(qū)動(dòng)程序被打斷處的地址。僵死狀態(tài)時(shí),進(jìn)程會(huì)重復(fù)執(zhí)行同一段代碼,這樣pc值會(huì)在一個(gè)很小的范圍里變動(dòng)。 本調(diào)試技術(shù)就是借助系統(tǒng)時(shí)鐘中斷永不停息這一特性,在中斷入口函數(shù)里添加一些打印語(yǔ)句,把保存的pc值打印出來(lái),再經(jīng)過(guò)分析判斷定位出僵死的大致位置了。 在本實(shí)驗(yàn)平臺(tái)的Linux系統(tǒng)當(dāng)中,一發(fā)生中斷,CPU就強(qiáng)制跳到0xffff0018處(此地址根據(jù)CPU的架構(gòu)不同而不同)執(zhí)行異常向量表里面的bvector_irq+stubs_offset。其中“stubs_offset”用來(lái)重定位跳轉(zhuǎn)的位置[11],這條匯編指令是去跳轉(zhuǎn)執(zhí)行vector_irq這個(gè)函數(shù),在這個(gè)函數(shù)里面保存了一些現(xiàn)場(chǎng),并經(jīng)過(guò)復(fù)雜的匯編代碼后會(huì)調(diào)用到asm_do_IRQ(archarmkernelIrq.c)這個(gè)函數(shù)。它是中斷處理函數(shù)的總?cè)肟赱12],在它內(nèi)部經(jīng)過(guò)層層的函數(shù)調(diào)用后,會(huì)最終執(zhí)行到時(shí)鐘中斷處理函數(shù)s3c2410_timer_interrupt()(archarmplat-s3c24xx ime.c)。選擇在s3c2410_timer_interrupt()或asm_do_IRQ()里加打印信息都可以,這里選擇asm_do_IRQ(),因?yàn)樵赼sm_do_IRQ()的傳入?yún)?shù)中有一個(gè)pt_regs結(jié)構(gòu)體,它作用就是保存發(fā)生中斷時(shí)的現(xiàn)場(chǎng)。pt_regs結(jié)構(gòu)體里面定義了一個(gè)長(zhǎng)度為18的數(shù)組uregs,其中uregs[15]保存的就是pc寄存器的值,正好可以利用它把pc值打印出來(lái)。但這里需注意一點(diǎn),中斷保存的pc值其實(shí)是實(shí)際指令地址+4。 asmlinkagevoid__exceptionasm_do_IRQ(unsignedintirq,structpt_regs*regs) structpt_regs{ longuregs[18]; }; …… #defineARM_pcuregs[15] …… 2.2實(shí)現(xiàn)步驟 先在一個(gè)正確的驅(qū)動(dòng)程序里加入一段死循環(huán)代碼。本文在mydrive.c這個(gè)驅(qū)動(dòng)源碼的讀函數(shù)mydrive_read()里加一句for(;;)。 staticssize_tmydrive_read(structfile*file,constchar__user*buf,size_tcount,loff_t*ppos) {…… for(;;); …… } 然后把mydrive.c編譯成模塊下載到開發(fā)板(這里把驅(qū)動(dòng)程序編譯成模塊,直接編譯進(jìn)內(nèi)核的情況比較簡(jiǎn)單,后面會(huì)敘述)。insmodmydrive.ko后,執(zhí)行于此驅(qū)動(dòng)相對(duì)應(yīng)的測(cè)試程序mytest,就會(huì)看到系統(tǒng)完全卡死了。 在asm_do_IRQ()函數(shù)里添加如下一段代碼,把僵死進(jìn)程號(hào)和pc值打印出來(lái)。 staticpid_tpid; //之前進(jìn)程號(hào) staticintcount=0; If(irq== 30) //系統(tǒng)時(shí)鐘中斷用的是定時(shí)器4,對(duì)應(yīng)的中斷號(hào)是30 { //若當(dāng)前進(jìn)程不等于之前進(jìn)程,計(jì)數(shù)值清零 if(pid!=current->pid) { pid=current->pid; count= 0; } //當(dāng)前進(jìn)程等于之前的進(jìn)程,計(jì)數(shù)值累加 else count++; if(count==15*HZ)若15秒內(nèi)都是同一個(gè)進(jìn)程 { count= 0; printk(“PID=%d,name=%s,pc=%08x
”,current->pid,current->comm,regs->uregs[15]); } } 在Linux內(nèi)核里面每一個(gè)進(jìn)程都由一個(gè)task_struct結(jié)構(gòu)體來(lái)表示[13,14],里面包含了進(jìn)程的相關(guān)屬性和信息。其中pid_tpid表示進(jìn)程號(hào);charcomm表示進(jìn)程的名字。 current是一個(gè)全局宏,用來(lái)獲取表示當(dāng)前進(jìn)程的task_struct結(jié)構(gòu)體指針。HZ也是一個(gè)宏定義,表示時(shí)鐘中斷發(fā)生的頻率,即一秒發(fā)生中斷的次數(shù)。 把修改的內(nèi)核源碼在Linux服務(wù)器下編譯后下載到開發(fā)板并啟動(dòng)。然后裝載驅(qū)動(dòng)模塊mydrive.ko并運(yùn)行與之相應(yīng)的測(cè)試程序mytest,如圖3所示。 圖3執(zhí)行步驟 系統(tǒng)僵死,等待15秒左右之后打印出僵死進(jìn)程號(hào)、僵死進(jìn)程名、 pc 值。如圖4所示。 圖4 僵死進(jìn)程相關(guān)信息 從打印信息可知是763號(hào)進(jìn)程mytest出現(xiàn)了僵死,從而可以知道問(wèn)題出在與mytest對(duì)應(yīng)的驅(qū)動(dòng)程序mydrive。在開發(fā)板的串口終端下打開system.map(里面是內(nèi)核的地址空間),發(fā)現(xiàn)bf000068不在其中,說(shuō)明僵死驅(qū)動(dòng)mydrive是個(gè)外加驅(qū)動(dòng)模塊,這也與實(shí)際操作相符。 至于死循環(huán)代碼的具體位置還得用pc值去反推。在Linux2.6版本內(nèi)核中引入了kallsyms,kallsyms抽取了內(nèi)核用到的所有函數(shù)地址(全局的、靜態(tài)的)和非棧數(shù)據(jù)變量地址,生成一個(gè)數(shù)據(jù)塊,作為只讀數(shù)據(jù)鏈接進(jìn)kernelimage。當(dāng)然外加驅(qū)動(dòng)模塊的地址也在其中重啟開發(fā)板,在終端下執(zhí)行: insmodmydrive.ko cat/proc/kallsyms 在里面尋找地址bf000068,就找到與bf000068相近的一條bf000000。 bf000000tmydrive_open[mydrive] 在Linux服務(wù)器下把mydrive.ko模塊反匯編arm-Linux-cbjdump-Dmydrive.ko>mydrive.dis 打開mydrv.dis,找到有mydrive_open的那一行00000000 00000000 …… 64:ebfffffebl64 68:ea00001fbf8 …… 前面說(shuō)過(guò),中斷保存的pc值其實(shí)是實(shí)際指令地址+4,所以0x00000064才是中斷函數(shù)執(zhí)行前保存的真正地址,也即僵死的位置。再看對(duì)應(yīng)的名字mydrive_read+0x3c,可知具體代碼在mydrive_read()函數(shù)入口地址偏移0x3c。至此回到源碼文件mydrive.c的mydrive_read()函數(shù)便可找到具體的僵死處。這正好與之前故意添加的死循環(huán)for(;;)的位置相一致。比照著相應(yīng)的匯編指令[16]bl64:永遠(yuǎn)跳轉(zhuǎn)到64。這也剛好和死循環(huán)的C語(yǔ)言是相吻合的。 這里的僵死點(diǎn)在外加的驅(qū)動(dòng)模塊中,如果僵死點(diǎn)在內(nèi)核里,就會(huì)發(fā)現(xiàn)打印出來(lái)的pc值在system.map文件所列出的地址范圍里面。這時(shí)只需要把使用的Linux內(nèi)核文件反匯編,在里面找到pc-4地址所在那一行代碼,自然就是僵死點(diǎn)的位置。 第一種調(diào)試方法中將驅(qū)動(dòng)源碼文件proc-printk.c編譯進(jìn)了內(nèi)核。當(dāng)然為了裝卸載方便,也可以將其編譯成模塊。在第二種調(diào)試方法中,為了測(cè)試需要,故意在驅(qū)動(dòng)文件里添加for(;;)語(yǔ)句,造成程序僵死在一點(diǎn),只打印出一個(gè)pc值。而在實(shí)際中,死循環(huán)的代碼可能是一段,這時(shí)用上述方法在一段時(shí)間內(nèi)打印出來(lái)的pc值可能會(huì)不同。但這不要緊,因?yàn)榇藭r(shí)的pc值雖不同,但分布密集。程序員只須分析這幾個(gè)pc值指定的匯編代碼就可確定僵死的大致位置。 [1] 霍玲玲,王世君,徐曉卉,等.嵌入式Linux系統(tǒng)的設(shè)計(jì)與實(shí)現(xiàn)[J]. 計(jì)算機(jī)技術(shù)與發(fā)展,2014,24(5):87-89. [2] 馮開林,劉春艷,韓東旭.基于S3C2440平臺(tái)搭建Linux環(huán)境[J]. 通信技術(shù),2013,46(11):120-124. [3] 付陽(yáng).基于ARM9的嵌入式Linux移植和驅(qū)動(dòng)程序設(shè)計(jì)[D].武漢:華中科技大學(xué),2012. [4] 韋東山.嵌入式Linux應(yīng)用開發(fā)完全手冊(cè)[M].北京:人民郵電出版社,2008. [5] 趙付強(qiáng),李允俊,宮彥磊.Proc文件系統(tǒng)的研究與應(yīng)用[J]. 計(jì)算機(jī)系統(tǒng)應(yīng)用,2013,22(1):87-90. [6] 郭銳.基于覆蓋測(cè)試的Linux內(nèi)核裁剪[D].太原:中北大學(xué),2014. [7] 宋寶華.Linux設(shè)備驅(qū)動(dòng)開發(fā)詳解[M].北京:人民郵電出版社,2010. [8] 王維,李濤,韓俊剛. 一種多線程輕核機(jī)器中進(jìn)程管理的硬件實(shí)現(xiàn)[J].電子技術(shù)應(yīng)用,2013,29(3):40-43. [9] 唐富強(qiáng),于鴻洋,張萍.Linux下通用線程池的改進(jìn)與實(shí)現(xiàn)[J].計(jì)算機(jī)工程與應(yīng)用,2012,48(28):77-83. [10] 周峰,胡軍山,朱宗玖.基于CK810LINUX3.0內(nèi)核的移植實(shí)現(xiàn)[J].計(jì)算機(jī)應(yīng)用與軟件,2014,31(1):252-255,267. [11] 鄭強(qiáng).Linux驅(qū)動(dòng)開發(fā)入門與實(shí)戰(zhàn)[M].北京:清華大學(xué)出版社,2011. [12] 毛德操,胡希明.Linux內(nèi)核源代碼情景分析[M].杭州:浙江大學(xué)出版社,2001. [13] 楊興強(qiáng),劉翔鵬,劉毅.Linux進(jìn)程狀態(tài)演化過(guò)程的圖形學(xué)表示[J].系統(tǒng)仿真學(xué)報(bào),2013,25(10):2444-2448. [14] 龍飛.嵌入式Linux系統(tǒng)內(nèi)核實(shí)時(shí)性研究[D].沈陽(yáng):沈陽(yáng)工業(yè)大學(xué),2012. [15] 奚琪,曾勇軍,王清賢,等.一種動(dòng)靜結(jié)合的代碼反匯編框架[J].小型微型計(jì)算機(jī)系統(tǒng),2013(10):2251-2255. [16] 黃奉孝,高艷華,張學(xué)軍.基于嵌入式構(gòu)件的編程語(yǔ)言融合技術(shù)研究[J].計(jì)算機(jī)工程與設(shè)計(jì),2012,33(11):4138-4141. RESEARCHONARM-BASEDLINUXDRIVERDEBUGGINGTECHNOLOGY GongChenCaiWen (School of Information,Mechanical and Electrical Engineering,Shanghai Normal University,Shanghai 200234,China) WhenperformingLinuxdrivertransplantationonARM,itisneededtomodify,deleteoraddLinuxkernelcodes,butwhichmayresultinmanyunexpectederrorsatruntime.Atthistimetodebugcodessoastofindthecauseandpositionoftheerrorarenecessary.Forthisrequirement,weproposeandimplementtwonewdebuggingtechniques.Thefirstoneistoconstructaprintingfunctiontostoreadditionalprintinformationinabufferseparately,andtooutputitwiththehelpofprocfilesystem.Itachievestheseparationbetweentheadditionalprintinformationandtheprintinformationofthekernelitself,andmakesthesearchmoreconvenient.Thesecondoneistodeterminethepositionofsystemdeadbymakinguseoftheunceasingcharacteristicofsystemclockinterrupt.Itisshownthroughexperimentthatthistechniquefindsthepositionofendlessloopquickly,andsavesalargeamountofcodesearchandanalysiswork. LinuxDebugProcfilesystemSystemclockinterrupt 2014-08-26。鞏琛,碩士生,主研領(lǐng)域:嵌入式系統(tǒng)與通信控制系統(tǒng)。蔡文,副教授。 TP314 ADOI:10.3969/j.issn.1000-386x.2016.03.0542 利用內(nèi)核時(shí)鐘中斷確定僵死位置
3 結(jié) 語(yǔ)