多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > php開源 > 綜合技術 > E:安桌層及文件系統層的PRINTf輸出原理

E:安桌層及文件系統層的PRINTf輸出原理

來源:程序員人生   發布時間:2016-10-12 09:35:12 閱讀次數:2967次

/device/console操控臺原理分析,通過調用此操控臺來輸出信息,同時這兒觸及到/device/console調用TTY,然后TTY調用低層串口的分析 安桌LOG輸出原理)

LINUX內核源碼版本:linux⑶.0.86  

 

/dev/console即控制臺,是與操作系統交互的裝備,系統將1些調試信息直接輸出到控制臺上,是TTY裝備的1個子集

 

Tty:teltypewriters  Teletypes簡稱電傳打印機:終端是1種字符型裝備,它有多種類型,通常使用tty來簡稱各種類型的終端裝備ConsoleTTY裝備的1種。Console調用到最低層就是TTY裝備驅動。

TTY CONSOLE UART關系圖(由我的平臺有4路串口,要分析到USER空間/dev/console最后是通過那1路口輸出的,注意和內核空間的printkconsole操控臺輸出端口是不是1致,分析原理。由前面幾節分析可知printk輸出是由uboot傳入的參數來肯定那1路串口輸出的的。Console=ttySAC0,115200n80)

分析代碼前根據網絡資料得出的1張圖:

 

自己分析代碼后得到的圖:

 

 

紅色部份是分析了所有代碼后得出的結論:

下面進程很雜亂,到現在還是沒有分析清楚,只能是明白了個大概,由于結構體太多,沒交繁復雜。現在還是亂。只能再次分析1下流程(只針對/dev/console的寫進程。我們要知道用戶空間的console是支持輸入輸出的。Kernel空音的console只支持輸出信息。這是很重要的區分。)。

/dev/console

  1. 裝備打開。得到tty_struct結構體,并且賦值在file中1個private_data中。同時由于

    struct tty_driver *console_driver = console_device(&index);得來是由uboot傳入的參數來決定的。因此這兒/dev/console輸出0串口來決定。由其它節部份的參數解析部份得知。

    struct tty_driver *console_driver = console_device(&index)->console_drivers(內核層register_console注冊函數時1個指針鏈表)->driver = c->device(c, index);

  2. 寫操作//把數據寫入file文件中的private_data指向的tty_struct中的1xmit變量(這兒有好層轉換),同時打開中斷。產生中斷條件。

    Write->[ld->ops->write=n_tty_write]->[tty->ops=driver->ops]=[tty_operations uart_ops]=[.write= uart_write,]

     

    3.中斷發送://中斷的注冊是在open函數中履行的。這兒自動過來履行的。

    ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0,

      s3c24xx_serial_portname(port), ourport);---->

    s3c24xx_serial_tx_chars->wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);發送數據

     

    //下面是上面結論分析進程。

     

    /dev/console字符裝備注冊流程分析:

    #define fs_initcall(fn)__define_initcall("5",fn,5)

    chr_dev_init->tty_init->MKDEV(TTYAUX_MAJOR, 1)->console_fops操作函數

    static const struct file_operationsconsole_fops = {

    .llseek= no_llseek,

    .read= tty_read,

    .write= redirected_tty_write,

    .poll= tty_poll,

    .unlocked_ioctl= tty_ioctl,

    .compat_ioctl= tty_compat_ioctl,

    .open= tty_open,

    .release= tty_release,

    .fasync= tty_fasync,

    };

    完成了對裝備的注冊,然后是打開裝備和讀寫裝備了,這個部份應當是用戶空間來完成的。我們不去看相干代碼,只從通用的打開讀寫通用操作方法來作分析。Open write read這3個函數由用戶空間來調用。當用戶空間調打開讀寫函數時會調用上面注冊的驅動的操作函數對應的函數。所以接下來分析Open write read

    Open:tty_open

    struct tty_driver *console_driver = console_device(&index)->console_drivers(內核層register_console注冊函數時1個指針鏈表)->driver = c->device(c, index);

    retval = tty_alloc_file(filp);

    struct tty_driver *console_driver = console_device(&index);

  • tty = tty_init_dev(driver, index, 0);//這個函數里去構建LDISC結構體了,叫做線規程/線路規程line discipline

     

    /*

    //ldisc構建進程:

    tty_init_dev(driver, index, 0);//tty_driver  0  0

    initialize_tty_struct(tty, driver, idx);//tty_struct tty_driver 0

    tty_ldisc_init(tty);

    Ld->ops=tty_ldiscs[disc]=tty_ldiscs[0]

    /*

     

    ty_ldiscs怎樣得來的是關鍵:

    start_kernel->console_init()->/* Setup the default TTY line discipline. */

    tty_ldisc_begin();->/* Setup the default TTY line discipline. */

    (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);->->

    N_TTY=0

    struct tty_ldisc_ops tty_ldisc_N_TTY = {

    .magic           = TTY_LDISC_MAGIC,

    .name            = "n_tty",

    .open            = n_tty_open,

    .close           = n_tty_close,

    .flush_buffer    = n_tty_flush_buffer,

    .chars_in_buffer = n_tty_chars_in_buffer,

    .read            = n_tty_read,

    .write           = n_tty_write,

    .ioctl           = n_tty_ioctl,

    .set_termios     = n_tty_set_termios,

    .poll            = n_tty_poll,

    .receive_buf     = n_tty_receive_buf,

    .write_wakeup    = n_tty_write_wakeup

    };

    tty_ldiscs[disc] = new_ldisc;

    tty_ldiscs[0] = tty_ldisc_N_TTY ;等于上面TTY裝備

    通過上面分析可知

    Ld->ops=tty_ldiscs[disc]=tty_ldiscs[0]=tty_ldisc_N_TTY

     

     

    */

    tty->ldisc = ld;

    tty_add_file(tty, filp);

    struct tty_file_private *priv = file->private_dat=tty_struct;因此通過file文件中的private_data來操作tty_sruct。這個結構體時里面包括tty_driver tty->ldisc多個變量。

     

     

    */

    tty_add_file(tty, filp);

    上面代碼完成通過文件的struct tty_file_private *priv = file->private_data變量來保存tty_struct(同時TTY結構體包括tty_drivers)驅動變量。

     

    我們代碼的console_drivers以下:

    static struct console s3c24xx_serial_console = {

    .name= S3C24XX_SERIAL_NAME,

    .device=uart_console_device,

    .flags= CON_PRINTBUFFER,

    .index= ⑴,

    .write= s3c24xx_serial_console_write,

    .setup= s3c24xx_serial_console_setup,

    .data= &s3c24xx_uart_drv,

    };

    struct tty_driver *uart_console_device(struct console *co, int *index)

    {

    struct uart_driver *p = co->data;

    *index = co->index;//0通過UBOOT傳入參數等到0

    returnp->tty_driver;

    }

    static struct uart_driver s3c24xx_uart_drv = {

    .owner= THIS_MODULE,

    .driver_name= "s3c2410_serial",

    .nr= CONFIG_SERIAL_SAMSUNG_UARTS,

    #ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE_SWITCH

    .cons= NULL,

    #else

    .cons= S3C24XX_SERIAL_CONSOLE,

    #endif

    .dev_name= S3C24XX_SERIAL_NAME,

    .major= S3C24XX_SERIAL_MAJOR,

    .minor= S3C24XX_SERIAL_MINOR,

    };

    p->tty_drivers3c24xx_uart_drv結構體的成員,但在上面數組的初始化中并沒有初始化此值,因此可以肯定是在在其它地方初始化的tty_driver的。

    下面來分析tty_driver的初始化流程:

    s3c24xx_serial_modinit->uart_register_driver(&s3c24xx_uart_drv)->:

    struct tty_driver *normal;

    drv->tty_driver = normal;

    tty_set_operations(normal, &uart_ops); //driver->ops = op;

    /*

    static const struct tty_operations uart_ops = {

    .open= uart_open,

    .close= uart_close,

    .write= uart_write,

    .put_char= uart_put_char,

    .flush_chars= uart_flush_chars,

    .write_room= uart_write_room,

    .chars_in_buffer= uart_chars_in_buffer,

    .flush_buffer= uart_flush_buffer,

    .ioctl= uart_ioctl,

    .throttle= uart_throttle,

    .unthrottle= uart_unthrottle,

    .send_xchar= uart_send_xchar,

    .set_termios= uart_set_termios,

    .set_ldisc= uart_set_ldisc,

    .stop= uart_stop,

    .start= uart_start,

    .hangup= uart_hangup,

    .break_ctl= uart_break_ctl,

    .wait_until_sent= uart_wait_until_sent,

    #ifdef CONFIG_PROC_FS

    .proc_fops= &uart_proc_fops,

    #endif

    .tiocmget= uart_tiocmget,

    .tiocmset= uart_tiocmset,

    .get_icount= uart_get_icount,

    #ifdef CONFIG_CONSOLE_POLL

    .poll_init= uart_poll_init,

    .poll_get_char= uart_poll_get_char,

    .poll_put_char= uart_poll_put_char,

    #endif

    };

    tty->ops = driver->ops;

    struct tty_driver *driver;

    struct tty_driver *console_driver = console_device(&index);

    tty->ops=driver->ops->console_drivers->driver = c->device(c, index)->

     tty_operations uart_ops=tty_operations uart_ops;--->tty->ops=driver->ops=tty_operations uart_ops

     

    */

    通過上面代碼分析可知:用操作/dev/console寫函數時

    .write= redirected_tty_write,

    static ssize_t tty_write(struct file *file, const char __user *buf,

    size_t count, loff_t *ppos)

    struct tty_struct *tty = file_tty(file);//包括tty_driver結構體。

    ld = tty_ldisc_ref_wait(tty);

    ld->ops->write//////因此找到這個函數才是我們的根本,看是如何實現的。????????????????

    通過前面分析知:

    Ld->ops=tty_ldiscs[disc]=tty_ldiscs[0]=tty_ldisc_N_TTY

    因此ld->ops->write=n_tty_write//tty_ldisc_N_TTY中的函數完成線程規程檢查

    最后還是調用tty->ops->write(tty, b, nr);來輸出數據。規則檢查部份不能把數據輸出的串口

    綜上:對/dev/console裝備的寫函數的履行流程以下:

    Write->[ld->ops->write=n_tty_write]->[tty->ops=driver->ops]=[tty_operations uart_ops]=[.write= uart_write,]

    static int uart_write(struct tty_struct *tty,

    const unsigned char *buf, int count)

    __uart_start(tty);

    port->ops->start_tx(port);//ops= s3c24xx_serial_ops;

    static struct uart_ops s3c24xx_serial_ops = {

    .pm= s3c24xx_serial_pm,

    .tx_empty= s3c24xx_serial_tx_empty,

    .get_mctrl= s3c24xx_serial_get_mctrl,

    .set_mctrl= s3c24xx_serial_set_mctrl,

    .stop_tx= s3c24xx_serial_stop_tx,

    .start_tx= s3c24xx_serial_start_tx,

    .stop_rx= s3c24xx_serial_stop_rx,

    .enable_ms= s3c24xx_serial_enable_ms,

    .break_ctl= s3c24xx_serial_break_ctl,

    .startup= s3c24xx_serial_startup,

    .shutdown= s3c24xx_serial_shutdown,

    .set_termios= s3c24xx_serial_set_termios,

    .type= s3c24xx_serial_type,

    .release_port= s3c24xx_serial_release_port,

    .request_port= s3c24xx_serial_request_port,

    .config_port= s3c24xx_serial_config_port,

    .verify_port= s3c24xx_serial_verify_port,

    .wake_peer= s3c24xx_serial_wake_peer,

    };

    s3c24xx_serial_start_tx//這個函數是打開中斷  我們/dev/console應當是通過中斷來把數據發送出去的。數據存在struct circ_buf xmit結構體中。這個函數只是把數據緩存在變量中和打開串口,發送數據是在中斷中進行的。tty->ops->open(tty, filp);====s3c24xx_serial_startup打開中斷ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0,

      s3c24xx_serial_portname(port), ourport);注冊串口發送函數。

    static irqreturn_t

    static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)

    struct s3c24xx_uart_port *ourport = id;

    struct circ_buf *xmit = &port->state->xmit;//這個是在s3c24xx_serial_start_tx中去指定的。

    /*

    ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0,

      s3c24xx_serial_portname(port), ourport);*/

    上面中斷函數的id/dev/consoletty_drvier是同1個變量。所以在觸發中斷發送時能正確的發送數據。wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);

     

     

     

     

    /dev/tty字符裝備注冊流程分析:

    #define fs_initcall(fn)__define_initcall("5",fn,5)

    chr_dev_init->tty_init->MKDEV(TTYAUX_MAJOR, 0)->tty_fops

    上面只是注冊了1個tty裝備。Tty實際上是1多個的。最后是tty_register_driver-for (i = 0; i < driver->num; i++) {

    d = tty_register_device(driver, i, NULL);-device_create(tty_class, device, dev, NULL, name);

    上面完成多個裝備的注冊tty裝備的注冊,這兒主要是串串的。所有的tty1字符輸輸入輸出裝備都可以用tty_register_driver來注冊??梢宰远鄠€。上面/dev/console只是利用了其中的1類來解決問題。Tty裝備是可以單獨來解決問題的。用tty_register_driver注冊。但我們分析的串口部份是用的tty_init來注冊裝備。然后通過打開函數來與tty_structtty_drvier關聯解決問題。大致分析了代碼發/dev/conosle/  /dev/tty邏輯差不多。細節就不去分析了??梢悦鞔_的是對這兩個裝備的輸入輸出都是由uboot傳入參數來決是那1個口的。前1個用于系統信息調試 ,后1個應當是作其它功能。對這裝備當作1般裝備來理解。只是加入很多中間層,所以分析起來比較麻煩。暫不去細究。分析了好幾天還是亂。不是清晰的邏輯。

     

    static const struct file_operations tty_fops = {

    .llseek= no_llseek,

    .read= tty_read,

    .write= tty_write,

    .poll= tty_poll,

    .unlocked_ioctl 生活不易,碼農辛苦
    如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
    程序員人生

------分隔線----------------------------
分享到:
------分隔線----------------------------
為碼而活
積分:4237
15粉絲
7關注
欄目熱點
關閉
程序員人生
主站蜘蛛池模板: www.俺去| 青青草原在线视频 | 日本一区二区不卡久久入口 | 秋霞午夜视频在线观看 | 亚洲成a人片在线观看中文!!! | 中文乱码在线观看 | 午夜在线视频免费 | 国产亚洲精品激情都市 | 动漫羞羞网站 | 波多野结衣视频在线免费观看 | jizz.日本 | www.91成人| 国产福利精品一区二区 | 成人精品一区二区三区校园激情 | 欧美日韩国产在线观看 | 精品网站| 欧美性一区二区三区 | 精品久久中文字幕有码 | 久久99久久99精品免观看麻豆 | 免费一级欧美片在线观免看 | 日本aa在线 | 图片区另类小说 | 肉视频在线观看 | 国产一级做a爰片久久毛片99 | 国产精品亚洲欧美大片在线看 | 国产一区私人高清影院 | 国产成人一区二区三区视频免费蜜 | 欧美精品一区二区三区视频 | 亚洲欧美在线免费观看 | 亚洲欧美性另类春色 | 性做久久久久久久久老女人 | 91精品国产高清久久久久 | 欧美综合久久 | 国产午夜亚洲精品久久www | 伊人国产精品 | 日本一区二区不卡久久入口 | 国产综合成人久久大片91 | 一区二区三区高清不卡 | 久九色 | xx在线视频| 亚洲理论视频 |