注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Tsecer的回音岛

Tsecer的博客

 
 
 

日志

 
 

终端一些功能引申  

2013-08-06 22:35:18|  分类: Linux内核 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
一、无处不在的终端
这个东西真的还是挺常见,至少最近很长时间一直在见。而且在最早的计算机早期的时候,有人能够在终端上看到一个字符是彩色界面的应该都会很惊喜吧。很多我们经常使用的东西,一方面觉得很熟悉,但是由于这个东西非常的常见,所以人们不断的对这个东西进行扩充,扩充之后的很多功能引发了更多的有意思的功能。
二、常见工具的影响
在之前的一篇日志中大致说明了很多工具是udongqufen了自己交互的终端,这些工具包含比较常见的mysql、login等工具。这些工具的特点就是它们大部分时间都是和用户主动交互的,在交互的过程中,用户根据工具的输出会进行不同的变化。比较明显的就是mysql工具,它在开始就会判断此时输入是否为一个tty设备,如果是,它的输出就比较的详细,例如会对每列加上框框信息,从而看起来更加直观;反过来说,如果输入不是终端,此时的输出就言简意赅,便于进行自动化处理。
还有一些程序,例如login,它们压根不允许自己的输入是一个非终端设备。因为对于login来说,它需要禁止回显用户的密码,而这个功能只有在tty设备的条件下才能完成。所以对于自动登录,通常使用标准的expect工具,而不是简单的使用echo passwrd就可以完成自动登录的。
之前说过的printf对于文件的识别,则更为潜移默化的影响很多的程序。
但是对于bash和mysql来说,如果没有在命令行指明执行的命令,则可以从标准输入中读取脚本内容。对于mysql来说,它的man手册开始就说的比较明确

DESCRIPTION
       mysql is a simple SQL shell (with GNU readline capabilities). It
       supports interactive and noninteractive use. When used interactively,
       query results are presented in an ASCII-table format. When used
       noninteractively (for example, as a filter), the result is presented in
       tab-separated format. The output format can be changed using command
       options.
bash的man手册
DESCRIPTION
       Bash  is  an  sh-compatible  command language interpreter that executes
       commands read from the standard input or from a file.  Bash also incor-
       porates useful features from the Korn and C shells (ksh and csh).

三、如何判断文件是一个tty
这个C库中有一个标准函数,函数名称为isatty,
/* Return 1 if FD is a terminal, 0 if not.  */
int
__isatty (fd)
     int fd;
{
  struct termios term;

  return __tcgetattr (fd, &term) == 0;
}
方法也比较巧妙,就是执行一个获取tty设备终端配置的系统调用,如果非tty设备,没有该ioctrl接口,调用失败。
四、readline的字符编辑功能
readline提供了大量的字符编辑工具,例如将光标定位到一行的开始,删除当前位置到行结尾字符等功能,通过bash的bind -p命令可以看到当前bash快捷键的绑定情况。不知道大家有没有想到过这些功能是怎么实现的。
通过strace看下执行的命令
下面是执行了 左方向键、然后执行ctrl+A的strace输出
rt_sigprocmask(SIG_BLOCK, [INT], [], 8) = 0
write(2, "\33[C\33[C\33[C\33[Cd\10\10\10\10", 17 ) = 17
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
read(0, "\33", 1)                       = 1
read(0, "[", 1)                         = 1
read(0, "D", 1)                         = 1
rt_sigprocmask(SIG_BLOCK, [INT], [], 8) = 0
write(2, "\10", )                      = 1
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
read(0, "\1", 1)                        = 1
rt_sigprocmask(SIG_BLOCK, [INT], [], 8) = 0
write(2, "\10\10\10")                = 3
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
read(0,
由于我之前输入了4个d,所以方向键转换为一个序列\33[D三个字节的字符串,而对于回到行首,则bash向终端输出了三个回退键(backspace)。内核中控制台对于这些字符串有特定的解析,位于vt.c中,其中对于高亮的颜色处理也在这里,这里不再继续展开。
五、如果没有bash的readline
按下左方向键之后的输出,可以看到这就是按键对应的输出内容
[root@Harry ~]# sleep 1234
^[[C^[[C
对于方向键的处理为
static void k_cur(struct vc_data *vc, unsigned char value, char up_flag)
{
    static const char cur_chars[] = "BDCA";

    if (up_flag)
        return;
    applkey(vc, cur_chars[value], vc_kbd_mode(kbd, VC_CKMODE));
}
其它功能键的处理则位于
linux-2.6.21\drivers\char\defkeymap.c_shipped
char func_buf[] = {
    '\033', '[', '[', 'A', 0,
    '\033', '[', '[', 'B', 0,
    '\033', '[', '[', 'C', 0,
    '\033', '[', '[', 'D', 0,
    '\033', '[', '[', 'E', 0,
    '\033', '[', '1', '7', '~', 0,
    '\033', '[', '1', '8', '~', 0,
    '\033', '[', '1', '9', '~', 0,
    '\033', '[', '2', '0', '~', 0,
    '\033', '[', '2', '1', '~', 0,
    '\033', '[', '2', '3', '~', 0,
    '\033', '[', '2', '4', '~', 0,
    '\033', '[', '2', '5', '~', 0,
    '\033', '[', '2', '6', '~', 0,
    '\033', '[', '2', '8', '~', 0,
    '\033', '[', '2', '9', '~', 0,
    '\033', '[', '3', '1', '~', 0,
    '\033', '[', '3', '2', '~', 0,
    '\033', '[', '3', '3', '~', 0,
    '\033', '[', '3', '4', '~', 0,
    '\033', '[', '1', '~', 0,
    '\033', '[', '2', '~', 0,
    '\033', '[', '3', '~', 0,
    '\033', '[', '4', '~', 0,
    '\033', '[', '5', '~', 0,
    '\033', '[', '6', '~', 0,
    '\033', '[', 'M', 0,
    '\033', '[', 'P', 0,
};
那么内核中对于方向键是'^[A'序列,而显示中是"^[[A"序列呢?这是由于内核在显示的时候加了一个额外处理
linux-2.6.21\drivers\char\n_tty.c
static void echo_char(unsigned char c, struct tty_struct *tty)
{
    if (L_ECHOCTL(tty) && iscntrl(c) && c != '\t') {
        put_char('^', tty);
        put_char(c ^ 0100, tty);
        tty->column += 2;
    } else
        opost(c, tty);
}
这些具有_C的都满足这些条件
unsigned char _ctype[] = {
_C,_C,_C,_C,_C,_C,_C,_C,            /* 0-7 */
_C,_C|_S,_C|_S,_C|_S,_C|_S,_C|_S,_C,_C,        /* 8-15 */
_C,_C,_C,_C,_C,_C,_C,_C,            /* 16-23 */
_C,_C,_C,_C,_C,_C,_C,_C,            /* 24-31 */
对于上面的例子,如果在sleep之后输入 ctrl + A 则显示内容为
[root@Harry ~]# sleep 1124
^A^A^A
对于\033来说,它先输出^,之后该字符加上0100之后变成一个[,加上之前的两个字符,即为展示的内容。
六、echo进入console之后为什么没被展示出来
其实原因已经很明显了,由于n_tty中将控制字符通过^0100的方式转换为了可显示字符,所以在console中是无法收到控制字符的,而console只有在收到控制字符的时候才能执行额外的光标移动等操作。
七、内核内置的字符编辑功能
它们的特点是可以不依赖于用户态readline库而发挥作用
[root@Harry ~]# stty -a
speed 38400 baud; rows 24; columns 80; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = M-^?; eol2 = M-^?;
swtch = M-^?; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W;
lnext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd cs8 hupcl -cstopb cread -clocal -crtscts
-ignbrk brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff
-iuclc ixany imaxbel iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt
echoctl echoke
[root@Harry ~]#
对于之前的例子,在sleep之后,通过crtl + W 同样可以删除之前输入的字符。
static void eraser(unsigned char c, struct tty_struct *tty)
        if (kill_type == WERASE) {
            /* Equivalent to BSD's ALTWERASE. */
            if (isalnum(c) || c == '_')
                seen_alnums++;
            else if (seen_alnums)
                break;
        }
[root@Harry ~]# sleep 11111
sdfslfksf^A^A^B^B^B  dksfkksdfksdkfsfsdjfskdfs ^R
  dksfkksdfksdkfsfsdjfskdfs ^R
  dksfkksdfksdkfsfsdjfskdfs ^R
  dksfkksdfksdkfsfsdjfskdfs ^R
  dksfkksdfksdkfsfsdjfskdfs ^R
  dksfkksdfksdkfsfsdjfskdfs ^R
  dksfkksdfksdkfsfsdjfskdfs

其实内核对于字符的删除很简单
                    put_char('\b', tty);
                    put_char(' ', tty);
                    put_char('\b', tty);
  评论这张
 
阅读(609)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017