當前位置:編程學習大全網 - 源碼下載 - linux 啟動時何時初始化console,串口等

linux 啟動時何時初始化console,串口等

1、LINUX下TTY、CONSOLE、串口之間是怎樣的層次關系?具體的函數接口是怎樣的?串口是如何被調用的??

2、printk函數是把信息發送到控制臺上吧?如何讓PRINTK把信息通過串口送出?或者說系統在什麽地方來決定是將信息送到顯示器還是串口??

3、start_kernel中壹開始就用到了printk函數(好象是printk(linux_banner什麽的),在 這個時候整個內核還沒跑起來呢那這時候的printk是如何被調用的?在我們的系統中,系統啟動是用的現代公司的BOOTLOADER程序,後來好象跳到了LINUX下的head-armv.s, 然後跳到start_kernel,在bootloader 裏串口已經是可用的了,那麽在進入內核後是不是要重新設置??

以上問題可能問的比較亂,因為我自己腦子裏也比較亂,主要還是對tty,console,serial之間的關系,特別是串口是如何被調用的沒搞清這方面的資料又比較少(就情景分析中講了壹點),希望高手能指點壹二,非常謝!

我最近也在搞這方面的東西,也是寫壹個串口設備的驅動?

搞了將近壹個月了,其中上網找資料,看源代碼,什麽都做了?

但還是壹蹋糊塗的,有些問題還是不明白,希望壹起討論討論?

在/proc/device(沒記錯應該是這個文件)?

裏面有壹個叫serial的驅動,其主設備號是4,次設備號是64-12X(沒記錯應該是這個範圍)?

大家都知道,串口的次設備號是從64開始的,串口1 /dev/ttyS0就對應次設備號64,串口2就對應65?

問題是現在我機上只有兩個串口,它註冊這麽多次設備號來幹什麽??

對於壹個接在串口1的設備,在我註冊驅動的時候?

我是需要自己找壹個主設備號呢??

還是就用主設備號4,次設備號從上面12X的後面選??

還是就用主設備號4,次設備號64??

在linux的內核中有壹個tty層,我看好像有些串口驅動是從這裏開始的?

例如調用tty_register_driver()來註冊驅動?

就像在pci子系統裏調用pci_register_driver()那樣的?

那麽,用這種機制來註冊的驅動,?

它是直接對串口的端口操作呢(例如用inb(),outb()....之類的)?

還是某些更底層的驅動接口呢??

這些問題纏了我很久都沒解決,搞得最後不得不放棄?

現在轉向用戶空間的應用程序,看能不能有些更高效的方法來實現?

(在用戶空間只能用open("/dev/ttyS0", O_RDWR)來實現了)

另外還有,系統裏已經為我們實現了串口的驅動?

所以我們在用戶空間的程序裏直接open("/dev/ttyS0")就可用了?

但是現在要寫的是接在串口上的設備的驅動?

在內核模塊中可不可以包含某個頭文件,然後就可以直接用串口驅動中的接口呢?

看到妳們的問題後,感覺很有典型性,因此花了點工夫看了壹下,做了壹些心得貼在這裏,歡迎討論並指正:?

1、LINUX下TTY、CONSOLE、串口之間是怎樣的層次關系?具體的函數接口是怎樣的?串口是如何被調用的??

tty和console這些概念主要是壹些虛設備的概念,而串口更多的是指壹個真正的設備驅動Tty實際是壹類終端I/O設備的抽象,它實際上更多的是壹個管理的概念,它和tty_ldisc(行規程)和tty_driver(真實設備驅動)組合在壹起,目的是向上層的VFS提供壹個統壹的接口通過file_operations結構中的tty_ioctl可以對其進行配置。查tty_driver,妳將得到n個結果,實際都是相關芯片的驅動因此,可以得到的結論是(實際情況比這復雜得多):每個描述tty設備的tty_struct在初始化時必然掛如了某個具體芯片的字符設備驅動(不壹定是字符設備驅動),可以是很多,包括顯卡或串口chip不知道妳的ARM Soc是那壹款,不過看情況妳們應該用的是常見的chip,這些驅動實際上都有而console是壹個緩沖的概念,它的目的有壹點類似於tty實際上console不僅和tty連在壹起,還和framebuffer連在壹起,具體的原因看下面的鍵盤的中斷處理過程Tty的壹個子集需要使用console(典型的如主設備號4,次設備號1―64),但是要註意的是沒有console的tty是存在的

而串口則指的是tty_driver舉個典型的例子:?

分析壹下鍵盤的中斷處理過程:?

keyboard_interrupt―>handle_kbd_event―>handle_keyboard_event―>handle_scancode?

void handle_scancode(unsigned char scancode, int down)?

{?

……..?

tty = ttytab? ttytab[fg_console]: NULL;?

if (tty && (!tty->driver_data)) {?

……………?

tty = NULL;?

}?

………….?

schedule_console_callback();?

}?

這段代碼中的兩個地方很值得註意,也就是除了獲得tty外(通過全局量tty記錄),還進行了console 回顯schedule_console_callbackTty和console的關系在此已經很明了!!!?

2、printk函數是把信息發送到控制臺上吧?如何讓PRINTK把信息通過串口送出?或者說系統在什麽地方來決定是將信息送到顯示器還是串口??

具體看壹下printk函數的實現就知道了,printk不壹定是將信息往控制臺上輸出,設置kernel的啟動參數可能可以打到將信息送到顯示器的效果。函數前有壹段英文,很有意思:?

/*This is printk. It can be called from any context. We want it to work.?

*?

* We try to grab the console_sem. If we succeed, it's easy - we log the output and?

* call the console drivers. If we fail to get the semaphore we place the output?

* into the log buffer and return. The current holder of the console_sem will?

* notice the new output in release_console_sem() and will send it to the?

* consoles before releasing the semaphore.?

*?

* One effect of this deferred printing is that code which calls printk() and?

* then changes console_loglevel may break. This is because console_loglevel?

* is inspected when the actual printing occurs.?

*/?

這段英文的要點:要想對console進行操作,必須先要獲得console_sem信號量如果獲得console_sem信號量,則可以“log the output and call the console drivers”,反之,則“place the output into the log buffer and return”,實際上,在代碼:?

asmlinkage int printk(const char *fmt, ...)?

{?

va_list args;?

unsigned long flags;?

int printed_len;?

char *p;?

static char printk_buf[1024];?

static int log_level_unknown = 1;?

if (oops_in_progress) { /*如果為1情況下,必然是系統發生crush*/?

/* If a crash is occurring, make sure we can't deadlock */?

spin_lock_init(&logbuf_lock);?

/* And make sure that we print immediately */?

init_MUTEX(&console_sem);?

}?

/* This stops the holder of console_sem just where we want him */?

spin_lock_irqsave(&logbuf_lock, flags);?

/* Emit the output into the temporary buffer */?

va_start(args, fmt);?

printed_len = vsnprintf(printk_buf, sizeof(printk_buf), fmt, args);/*對傳入的buffer進行處理,註意還不是?

真正的對終端寫,只是對傳入的string進行格式解析*/?

va_end(args);?

/*Copy the output into log_buf. If the caller didn't provide appropriate log level tags, we insert them here*/?

/*註釋很清楚*/?

for (p = printk_buf; *p; p++) {?

if (log_level_unknown) {?

if (p[0] != '<' || p[1] < '0' || p[1] > '7' || p[2] != '>') {?

emit_log_char('<');?

emit_log_char(default_message_loglevel + '0');?

emit_log_char('>');?

}?

log_level_unknown = 0;?

}?

emit_log_char(*p);?

if (*p == '\n')?

log_level_unknown = 1;?

}?

if (!arch_consoles_callable()) {?

/*On some architectures, the consoles are not usable on secondary CPUs early in the boot process.*/?

spin_unlock_irqrestore(&logbuf_lock, flags);?

goto out;?

}?

if (!down_trylock(&console_sem)) {?

/*We own the drivers. We can drop the spinlock and let release_console_sem() print the text*/?

spin_unlock_irqrestore(&logbuf_lock, flags);?

console_may_schedule = 0;?

release_console_sem();?

} else {?

/*Someone else owns the drivers. We drop the spinlock, which allows the semaphore holder to?

proceed and to call the console drivers with the output which we just produced.*/?

spin_unlock_irqrestore(&logbuf_lock, flags);?

}?

out:?

return printed_len;?

}?

實際上printk是將format後的string放到了壹個buffer中,在適當的時候再加以show,這也回答了在start_kernel中壹開始就用到了printk函數的原因?

3、start_kernel中壹開始就用到了printk函數(好象是printk(linux_banner什麽的),在這個時候整個內核還沒跑起來呢。那這時候的printk是如何被調用的?在我們的系統中,系統啟動是用的現代公司的BOOTLOADER程序,後來好象跳到了LINUX下的head-armv.s, 然後跳到start_kernel,在bootloader 裏串口已經是可用的了,那麽在進入內核後是不是要重新設置??

Bootloader壹般會做壹些基本的初始化,將kernel拷貝物理空間,然後再跳到kernel去執行。可以肯定的是kernel肯定要對串口進行重新設置,原因是Bootloader有很多種,有些不壹定對串口進行設置,內核不能依賴於bootloader而存在。

多謝樓上大俠,分析的很精辟。我正在看printk函數。

我們用的CPU是hynix的hms7202。在評估板上是用串口0作?

控制臺,所有啟動過程中的信息都是通過該串口送出的。?

在bootloader中定義了函數ser_printf通過串口進行交互。

但我還是沒想明白在跳轉到linux內核而console和串口尚未?

初始化時printk是如何能夠工作的?我看了start_kernel?

的過程(並通過超級終端作了壹些跟蹤),console的初始化?

是在console_init函數裏,而串口的初始化實際上是在1號?

進程裏(init->do_basic_setup->do_initcalls->rs_init),?

那麽在串口沒有初始化以前prink是如何工作的?特別的,在?

start_kernel壹開始就有printk(linux_banner),而這時候?

串口和console都尚未初始化呢。

在start_kernel壹開始就有printk(linux_banner),而這時候串口和console都尚未初始化?

仔細分析printk可以對該問題進行解答代碼中的:?

/* Emit the output into the temporary buffer */?

va_start(args, fmt);?

printed_len = vsnprintf(printk_buf, sizeof(printk_buf), fmt, args);?

va_end(args);?

將輸入放到了printk_buf中,接下來的?

for (p = printk_buf; *p; p++) {?

if (log_level_unknown) {?

if (p[0] != '<' || p[1] < '0' || p[1] > '7' || p[2] != '>') {?

emit_log_char('<');?

emit_log_char(default_message_loglevel + '0');?

emit_log_char('>');?

}?

log_level_unknown = 0;?

}?

emit_log_char(*p);?

if (*p == '\n')?

log_level_unknown = 1;?

}?

則將printk_buf中的內容進行解析並放到全局的log_buf(在emit_log_char函數)中if (!down_trylock(&console_sem)) {?

/*?

* We own the drivers. We can drop the spinlock and let?

* release_console_sem() print the text?

*/?

spin_unlock_irqrestore(&logbuf_lock, flags);?

console_may_schedule = 0;?

release_console_sem();?

} else {?

/*?

* Someone else owns the drivers. We drop the spinlock, which?

* allows the semaphore holder to proceed and to call the?

* console drivers with the output which we just produced.?

*/?

spin_unlock_irqrestore(&logbuf_lock, flags);?

}?

則是根據down_trylock(&console_sem)的結果調用release_console_sem(),在release_console_sem()中才真正的對全局的log_buf中的內容相應的console設備驅動進行處理。至此,可以得到如下的壹些結論:?

(1)printk的主操作實際上還是針對壹個buffer(log_buf),該buffer中的內容是否顯示(或者說向終端輸出),則要看是否可以獲得console_sem(2)printk所在的文件為printk.c,是和體系結構無關的,因此對任何平臺都壹樣。 可以推測的結論是:?

(1)kernel在初始化時將console_sem標為了locked,因此在start_kernel壹開始的printk(linux_banner)中實際只將輸入寫入了緩沖,等在串口和console初始化後,對printk的調用才壹次將緩沖中的內容向串口和console輸出。 (2)在串口和console的初始化過程中,必然有對console_sem的up操作。?

(3)因此,在embedded的調試中,如果在console的初始化之前系統出了問題,不會有任何的輸出。 唯壹可以使用的只能是led或jtag了。(4)因此,妳的問題可以看出解答。2.console的初始化.?

不知道妳用的是那壹個內核版本,在我看的2.4.18和2.4.19中,都是在start_kernel中就對console進行的初始化。從前面的分析來看,console的初始化不應該太晚,否則log_buf有可能溢出。

多謝樓上,分析的很精彩!?

我們用的內核版本是2.4.18,console的初始化確實是在?

start_kernel->console->init。關於tty和串口,我這裏還想再問壹下tty設備的操作的總入口?

是?

static struct file_operations tty_fops = {?

llseek: no_llseek,?

read: tty_read,?

write: tty_write,?

poll: tty_poll,?

ioctl: tty_ioctl,?

open: tty_open,?

release: tty_release,?

fasync: tty_fasync,?

};?

而對串口的操作定義在:?

static struct tty_driver serial_driver 這個結構中

serial.c中的多數函數都是填充serial_driver中的函數指針

那麽在對串口操作時,應該是先調用tty_fops中的操作(比如?

tty_open等),然後再分流到具體的串口操作(rs_open等)吧??

但tty_driver(對串口就是serial_driver)中有很多函數指針?

並不跟file_operations中的函數指針對應,不知道這些對應?

不上的操作是如何被執行的?比如put_char,flush_char,read_proc,?

write_proc,start,stop等。

以下是我對這個問題的壹些理解:?

這實際上還是回到原先的老問題,即tty和tty_driver之間的關系。從實現上看,tty_driver實際上是tty機制的實現組件之壹,借用面向對象設計中的常用例子,這時的tty_driver就象是tty這部汽車的輪胎,tty這部汽車要正常運行,還要tty_ldisc(行規程),termios,甚至struct tq_struct tq_hangup(看tty_struct)等基礎設施。它們之間的關系並非繼承。至於tty_driver中的函數指針,再打個C++中的比喻,它們實際上很象虛函數,也就是說,可以定義它們,但並不壹定實現它們、實際上還不用說tty_driver,只要查壹下serial_driver都會發現n多個具體的實現,但對各個具體的設備,其tty_driver中的函數不壹定全部實現、所以put_char,flush_char,read_proc, write_proc,start,stop這些函數的情況是有可能實現,也有可能不實現 即使被實現,也不壹定為上層(VFS層)所用.

  • 上一篇:網站優化如何優化URL
  • 下一篇:汽車外觀的尺寸參數有哪些?哪些指標對使用性能影響較大
  • copyright 2024編程學習大全網