• <div id="0yoao"><tr id="0yoao"></tr></div>
    <dl id="0yoao"></dl>
  • <sup id="0yoao"></sup>
    <div id="0yoao"><tr id="0yoao"></tr></div>
  • <div id="0yoao"><tr id="0yoao"></tr></div>
  • ?

    Linux TTY framework(5)_System console driver

    作者:wowo 发布于:2016-10-29 22:43 分类:TTY子系统

    1. 前言

    由[1]中的介绍可知,Linux kernel的console框架,主要提供“控制台终端”的功能,用于:

    1)kernel日志信息(printk)的输出。

    2)实现基础的、基于控制台的人机交互。

    本文将从console driver开发者的视角,介绍:console有关的机制;编写一个console驱动需要哪些步骤;从用户的?#23884;?#24590;么使用;等等。

    2. 设计思路介绍

    不知?#26469;?#23478;是否有这样的疑问:既然已经有了TTY框架,为什么要多出来一个console框架,为什么不能直接使用TTY driver的接口实现console功能?

    确实,由[2]中的介绍可知,TTY框架的核心功能,就是管理TTY设备,并提供访问TTY设备的API(如数据收发)。而console的两个功能需求,“日志输出?#26412;?#26159;向TTY设备发送数据,“控制台人机交互?#26412;?#26159;标准的TTY功能。因此从功能上看,完全可以直接使用TTY框架的API啊。

    不过,既然存在,一定有其意义。内核之所以要抽象出console框架,思路如下:

    1)Linux kernel有一个很强烈的隐性规则----内核空间的代码不应该直接利用用户空间接口访问某些资源,例如kernel代码不应该直接使用文件系统接口访问文件(虽然它可以)。回到本文的场景里面,TTY框架通过字符设备(?#24067;?#25991;件系统)向用户空间提供接口,那么kernel的代码(如printk),就不能直接使用TTY的接口访问TTY设备,怎么办呢?开一个口子,从kernel里面再拉出一套接口,这就是console框架,如下图所示:

    tty_console

    2)console框架构建在TTY框架之上,大部分的实现(特别是访?#35270;布?#30340;部分)都和TTY框架复用。

    3)系统中可以有多个TTY设备,只有那些附加了console驱动的设备,才有机会成为kernel日志输出的目的地,有机会成为控制台终端。因此,console框架变相的成为管理TTY设备的一个框架。

    4)驱动工程师在为某个TTY设备编写TTY driver的时候,会根据实际的需求,评估该TTY设备是否可能成为控制台设备,如果可能,则同时为其编写system console driver,使其成为候选的控制台设备。系统工程师在系统启动的时候,可以通过kernel命令行?#38382;?#20915;定printk会在哪些候选设备上输出,那个候选设备最终会成为控制台设备。示意?#26082;?#19979;:

    console_overview

    3. 核心数据结构

    理解了console框架的设计思?#20998;?#21518;,再来?#27492;?#30340;实现,就很简单了。其核心数据结构为struct console,如下:

    /* include/linux/console.h */

    struct console {
            char    name[16];
            void    (*write)(struct console *, const char *, unsigned);
            int     (*read)(struct console *, char *, unsigned);
            struct tty_driver *(*device)(struct console *, int *);
            void    (*unblank)(void);
            int     (*setup)(struct console *, char *);
            int     (*match)(struct console *, char *name, int idx, char *options);
            short   flags;
            short   index;
            int     cflag;
            void    *data;
            struct   console *next;
    };

    该数据结构中经常被使用的字段有:

    name,该console的名称,配合index字段,可用来和命令行中的“console=xxx”中的“xxx”匹配,例如:

    如果name为“ttyXS”,index为大于等于0的数字(例如2),则可以和“console=ttyXS2”匹配;

    如果name为“ttyXS”,index为小于0的数字(例如-1),则可以和“console=ttySXn“(n=0,1,2…)?#25105;?#19968;个匹配。

    write,如果某个console被选中作为printk的输出,则kernel printk模块会调用write回调函数,将日志信息输出到。

    device,获取该console对应的TTY driver,用于将console和对应的TTY设备绑定,这样控制台终端就可以和console共用同一个TTY设备了。

    setup,用于初始化console的回调函数,console driver可以在该回调函数中对?#24067;?#20570;出现动作。可以不实现,如果实现,则必须返回0,否则该console不可用。

    flags,指示属性的flags,常用的包括:

    CON_BOOT,该console是一个临时console,只在启动的时候使用,kernel会在真正的console注册后,把它注销掉。

    CON_CONSDEV,表示该console会被用作控制台终端(和/dev/console对应),对应命令行中的最后一个,例如“console=ttyXS0 console=ttyUSB2”中的ttyUSB2。

    CON_PRINTBUFFER,如果设置了该flag,kernel在该console被注册的时候,会将那些被缓存到buffer中的之前的日志,统统输出到该console上。通常注册的console,如串口console,都会设置该flag,以便可以看到console注册前的日志输出。

    CON_ENABLED,表示该console正在被使用。

    4. 接口说明

    4.1 console driver的注册接口

    对具体的console driver来说,只需要关心console的注册/注销接口即可:

    /* include/linux/console.h */

    extern void register_console(struct console *);
    extern int unregister_console(struct console *);

    在正确填充struct console变量之后,通过register_console接口将其注册到kernel中即可。该接口将会完成如下的事情:

    检查该console的name和index,确?#29616;?#21069;没有注册过,否则注册失败;

    如果该console为boot console(CON_BOOT),确认是第一个注册的boot console,否则注册失败;

    如果系统中从来没有注册过console,则将第一个被注册的、可以setup成功(.setup为NULL或者返回0)的console作为正在使用的console,并使能之(CON_ENABLED);

    和command line中的“console=xxx”最对比,使能那些在命令行中指定的console;

    查?#20197;赾ommand line中指定的最后一个console,并置位其CON_CONSDEV flag,表明选择它为控制台console。

    4.2 用户层面接口

    系统console的使用控制,主要由命令行?#38382;?#19968;般都是bootloader传递而来的)指定,总结如下:

    1)如果某个console只需要在启动的时候使用,则需要在注册console的时候,置位其CON_BOOT标志,例如[3]中介绍的early console。

    2)如果系统中注册了多个console,可以通过命令行?#38382;?#25351;定使用哪个或者哪些,kernel的日志将会输出到所有在命令行指定了的console上面。

    3)命令行中指定的最后一个console,将会作为控制台console,应用程序打开/dev/console将会打开该console对应的TTY设备(由.device回调返回的tty driver指定)。

    5. console driver的编写步骤

    理解了system console的原理之后,编写一个console driver就比较简单了,包括:

    1)如果希望该console可以作为系统控制台(/dev/console),则必须先实现该console对应的TTY设备的TTY driver。

    2)定义一个console变量,并根据实?#26159;?#20917;填充对应的字段,包括name、index、setup(可选)、write、device(可选)等。

    3)调用register_console将其注册到kernel中即可。

    6. 参考文档

    [1] Linux TTY framework(3)_从应用的?#23884;?#30475;TTY设备

    [2] Linux TTY framework(4)_TTY driver

    [3] X-012-KERNEL-serial early console的移植

     

    原创文章,转发请注明出处。蜗窝科技www.71402172.com

    标签: Linux Kernel 内核 driver console

    评论:

    lucky
    2017-11-08 17:42
    Hi wowo,
    你上面有提到:Linux kernel在启动时会打开“/dev/console”,然后dup出来3个文件句柄(我们熟悉的0,1,2)交给init进程使用,然后一直被继承(除非被应用程序显式的修?#27169;?
    有个问题想请教下, 就console的打印log功能来讲, 内核空间的log printk和用户空间的log都是输出到 console?#19979;?也就?#23884;?#20250;最终调用到 struct console 的write函数? 最终log是输出到哪里呢? 内存里还是某块存储空间? 这个log和平时android平台常抓的串口log(串口log是最终写到物理串口设备上)是一回事吗?
    wowo
    2017-11-09 18:23
    @lucky:用户空间的日志输出,是通过VFS接口,经由TTY driver,给到具体的console driver。和kernel printk( struct console 的write函数)的路径不一样。
    至于log输出到哪里,由相应的driver决定。
    至于android的logcat,不是一个层次的事情,不要搅在一起理解……
    xmj
    2016-12-29 15:56
    hi,wowo
    看了你这篇文字有疑问,能否解答一下呢
    1. console的write和它里面的tty_driver write有什么区别吗
    2. 般情况下我们看串口log,手机bootloader起来就可以看了,这个时候console没初始化完成,是根据什么来输出log的吗,还有JIGTAG也可以在很早输出log? 能说下这几个的差别吗
    wowo
    2016-12-29 21:36
    @xmj:1. 我觉得这两个没有本质的区别,是向同一个通道里写数据的两个路径,参考本文的?#35745;?.
    2. 手机bootloader一般会自己初始化串口屏输出信息;JTAG的log,我也不是特别了解,估计类似一个虚拟串口。
    electrlife
    2016-10-31 13:58
    另外对于这?#38382;?#38388;的学习,我也总结下我对相关的几个概念的理解(请?#38468;?#29702;解的是否正确):

        tty
        从时间轴上看tty仅是电传打印机,由于广泛的作为终端设备来使用,因此成了终端的代名词。请先忘记当前你手中使用的PC机及当前使用的终端,因为我们现在所讲的是终端还是远在时代无图形界面的计算机时代。

        Pty 伪终端
        构造出的一对通信链路,这个链路服务于本机上的应用程序。如telnet/ssh远程登录程序,大概的过程可以描述如下:
        sshd 等待client的socket connect request,如果有client申请登录,则sshd daemon会通过系统调用创建两个伪终端,然后通过getty来对相应的终端进行设置。
        sshd只需要从其中特定的一个终端读出或写入数据并通过socket转发给client。此时sshd只负责数据转发,相应的prompt和登录相应的还可以由标准的getty和login来完成。
        因getty login默认使用tty作为它们的输入输出,因此我们可以通过伪造一对假的终端来供它们使用。

        仿真终端
        随着时间的流逝,计算机发展出图形界面,于是仿真的终端应运而生,如大名鼎鼎的xterm。 仿真终端运行在用户空间的一个程序,它使用软件模拟的方式来仿真一个真的终端所具有的所有功能。比如响应调节相应的显示size等。 对于终端另一端的应用程序来说,它们甚至无法识别对面是一个假的终端应用程序。xterm捕捉用户的输入并发送到终端另一边的应用,收集终端另一边的应用的数据,并通过图形接口显示到我们LCD上。因此对于仿真终端来说也使用伪终端来构建这样的数据通路。

    另外对于vt这块,暂时还不能和tty及console在源码上联系起来。
    wowo
    2016-10-31 14:21
    @electrlife:总结的很好啊,不过我觉得:从TTY技术本身看,应该没有仿真终端的概念(你这里提到的仿真终端,本质上还是一个伪终端,因为shell等传统应用一定需要一个tty设备,你不能不给它)。
    electrlife
    2016-10-31 20:16
    @wowo:对的!

    对于vt是否会有相应的专题?感觉从代码上现在完全没有头绪!
    electrlife
    2016-10-31 13:54
    Hi wowo, 这?#38382;?#38388;一直跟着你们的文章还有自己的理解,对于console发表下自己的看法,(PS 不一定正确)

    首先对于console,我的理解就现在的Linux而言似乎已经退化到打印Log的用途,简而理解仅仅作为printk输出的对象。如果这个理解正确,那么对console我个人觉得当前似乎和tty没有什么关系,从printk的代码我仅仅看出console的write等函数被调用,完全不见terminal的足迹。
    如果非要说console和终端的联系,我觉得仅仅是和uart有联系。和tty没有什么关联。当然在处理tty时可能需要考虑下如果该tty如果作为console需要额外的特殊处理,仅此而矣!
    因此,个人觉得在当下的Linux系统,console仅仅抽象了一个log输出的底层接口而矣!
    wowo
    2016-10-31 14:19
    @electrlife:其实我对TTY的熟悉程度也不高,我们刚好可以一起讨论:-)
    console一直是两个功能,一个是我们熟悉的printk,这里就不提了。
    另一个,console真的代表了正宗的“控制台终端”。要理解这个概念,需要围绕“/dev/console”和“console->device”来说:
    “/dev/console”代表了Linux/Unix系统中正宗的“控制台终端”,不需要知道为什么,就当是一种定义;
    “/dev/console”是由tty core(tty_io.c)在初始化时创建的,是一个虚拟的设备;
    Linux kernel在启动时会打开“/dev/console”,然后dup出来3个文件句柄(我们熟悉的0,1,2)交给init进程使用,然后一直被继承(除非被应用程序显式的修?#27169;?
    既然“/dev/console”是一个虚拟的设备,它对应的物理设备是什么呢?你应该猜到了,根据命令行?#38382;?#35843;用“console->device?#34987;?#21462;对应的struct tty_driver之后,就得到了实际的物理设备。
    最后总结,如果你不想kernel在open(“/dev/console”)报错,就应该实现当前所使用的console的“console->device”回调。当然,就算打不开,也没关系,后果顶多是有些应用无法输入输出(例如init进程)。
    electrlife
    2016-10-31 19:31
    @wowo:1、我试着从你的这个?#23884;?#29702;解了下,感觉有道理!我也试着简单跟了下open(/dev/console)代码,发现对于找到tty_driver这个数据结构时会区分console是tty device。但?#23884;?#20110;console_device这个函数,有点疑惑?这个函数会遍历所有注册的console driver只要driver存在便返回。我的理解是console应该只会有一个,这里为什么会使用遍历!
    2、对于启动?#38382;?#25105;们传入的console=xxx, console=yyy, ...这?#20013;?#24335;,系统会打印logs到所有我们注册的console,但系统真正的console只会是最后一个。是不是意味着前面的几个console其实是不需要tty_driver这样的数据结构的?
    3、我的理解 Open(/dev/console)其实打开?#23884;?#24212;应用程序的controlling terminal?
    wowo
    2016-11-01 08:57
    @electrlife:问题1:关于console_device,你可以去printk.c中检查一下它遍历的那个链表(console_drivers),其实是排序过的,最先拿到的,就是那些preferred driver(带CON_CONSDEV标记的)。话说这一段代码逻辑也是很巧妙的,?#26723;?#23398;习一下。
    问题2:是的。但是本着console和tty分离的原则,我们注册console的时候,其实是不假设它是否会作为控制台的,所以如果能提供,最好都提供,如果觉得没必要,不提供也ok。
    问题3:(/dev/console)不是controlling terminal,(/dev/tty)才是。说实话这几个术语的中文翻译有点绕,我倒觉得,(/dev/console)翻译成工作台反而更容易理解,它的操作者是人。(/dev/tty)是控制终端,它的操作者是软件(shell)。
    electrlife
    2016-11-01 10:26
    @wowo:抱歉!能提示下排序的关键点在哪些代码?#19979;穡空?#30340;没有看出来!
    wowo
    2016-11-01 11:29
    @electrlife:这一段代码逻辑我还没有细看,还是你看看之后给我?#27493;?#19968;下吧^V^

    发表评论:

    Copyright @ 2013-2015 蜗窝科技 All rights reserved. Powered by emlog
    连码三全中是什么
  • <div id="0yoao"><tr id="0yoao"></tr></div>
    <dl id="0yoao"></dl>
  • <sup id="0yoao"></sup>
    <div id="0yoao"><tr id="0yoao"></tr></div>
  • <div id="0yoao"><tr id="0yoao"></tr></div>
  • <div id="0yoao"><tr id="0yoao"></tr></div>
    <dl id="0yoao"></dl>
  • <sup id="0yoao"></sup>
    <div id="0yoao"><tr id="0yoao"></tr></div>
  • <div id="0yoao"><tr id="0yoao"></tr></div>
  • 香港赛马会大楼图片 今天的码报是什么意思 新时时彩攻略 大乐透周三走势图表图 江苏快3真假 黑龙江p62综合走势图 多多乐彩泥模具 7天娱乐平台 360足彩 北京pk10冠军直播 小学足球手抄报图片大全 四川金7乐基本走势图 彩票极速飞艇怎么玩 炸金花怎样提高手气 下载福建快三助手下载安装到手机