Chinaunix首页 | 论坛 | 博客
  • 博客访问: 15535912
  • 博文数量: 2005
  • 博客积分: 11986
  • 博客等级: 上将
  • 技术积分: 22535
  • 用 户 组: 普通用户
  • 注册时间: 2007-05-17 13:56
文章分类

全部博文(2005)

文章存档

2014年(2)

2013年(2)

2012年(16)

2011年(66)

2010年(368)

2009年(743)

2008年(491)

2007年(317)

分类: LINUX

2009-07-03 10:04:18

浅析terminal创建时ptmx和pts关系

  我们打开一个terminal,那么将会在devpts文件系统/dev/pts下创建一个对应的pts字符文件,
该pts字符文件节点直接由/dev/ptmx节点的驱动函数ptmx_open()
调用devpts_pty_new(tty->link)
[tty对应ptmx,tty->link对应/dev/pts/xxx,那么tty->link->link又对应回ptmx
同样ptm_driver->other等于pts_driver,pts_driver->other等于ptm_driver]主动创建,
而非通过netlink的udev或者hotplug配合创建[luther.gliethttp]

1.首先我们打开3个新的terminal终端
使用who am i查询当前终端对应的pts号
luther@gliethttp:~$ who am i
luther   pts/3        2009-07-03 09:05 (:0.0)
luther@gliethttp:~$ who am i
luther   pts/4        2009-07-03 09:08 (:0.0)
luther@gliethttp:~$ who am i
luther   pts/5        2009-07-03 09:08 (:0.0)
他们分别对应pts 3,4和5.
2.在pts/4终端中执行如下命令
luther@gliethttp:~$ cat /dev/pts/3
llllllllls
3.在pts/3终端中输入平常的命令ls
你会发现输入的数据并不能被完全显示,而2步骤中运行的cat  /dev/pts/3
命令确出现了不完整命令,这是怎么回事呢,接下来我们讲一讲该现象背后的故事[luther.gliethttp].
luther@gliethttp:~$ l
4.讲讲现象背后的故事
当ubuntu系统创建一个新的terminal时(比如上面的pts/3)
4.1 首先执行ptm = open('/dev/ptmx',...)操作
4.2 接下来fork(),然后child将打开'/dev/pts/3',dup2到0,1和2句柄上,随后执行execl启动一个shell.
    pts = open('/dev/pts/3',...);
    dup2(pts, 0); // 对应lib库中stdin
    dup2(pts, 1); // 对应lib库中stdout
    dup2(pts, 2); // 对应lib库中stderr
    close(pts);
    execl("/system/bin/sh", "/system/bin/sh", NULL);
    // 这样sh输入数据将全部来自pts,
    // sh的输出数据也都全部输送到pts,也就直接送到了打开ptmx的新terminal中.
4.3 新terminal将启动GUI,捕获按键数据,然后写入ptm,这样pts将收到数据,进而sh将从stdin中获得数据,
    于是sh将作进一步运算,将结果送给stdout或stderr,进而送给pts,于是ptm获得数据,然后terminal的GUI
    将数据显示出来.

    具体流程图如下[luther.gliethttp]:
    terminal捕获到key按键值 <--> ptm <--> pts/3 <--> stdin <--> shell读到数据
    shell数据结果 <--> stdout <--> pts/3 <--> ptm <--> terminal显示
4.4 好了,正常的启动流程图已经有了,来看看,我们试验时数据出现显示异常的现象背后到底是怎么发生的.
    与上面正常流程不同的时,我们在另外一个地方执行了cat.

    这种情况下的流程图为[luther.gliethttp]:
    terminal <--> ptm <--> pts/3 <------> shell
                                 |
                                 <------> 运行在pts/4上的 cat /dev/pts/3
    很明显terminal发送数据到pts/3之后,
    因为有2个独立的进程都在等待pts/3的数据,所以他们之间就发生了对pts/3数据抢夺现象,
    因为linux内核调度器根据当时情况随时都会将他们中的一个调出或者调入,因此数据
    就出现了一部分被送到了pts/4的cat命令,另一部分被送到了shell,
    因为shell具有回显能力,shell将它接收到的所有字符串一一回显给terminal,所以terminal显示
    到的数据就是shell与cat命令争抢数据时,shell自己抢到的数据,
    而pts/4上显示的数据就是cat命令抢到的数据[luther.gliethttp]

    比如我们仍然在pts/4上执行cat命令,然后我们在pts/5上执行echo命令
    luther@gliethttp:~$ who am i
    luther   pts/5        2009-07-03 09:08 (:0.0)
    luther@gliethttp:~$ echo 'luther.gliethttp' >/dev/pts/3
    这时pts/3对应的terminal将完全显示'luther.gliethttp'字符串,因为没有人和ptm争抢数据[luther.gliethttp].
4.5 在pts/3自己所在terminal中执行cat回是什么现象呢,我么继续看看
    luther@gliethttp:~$ cat /dev/pts/3
    ls
    ls
    pwd
    pwd
    可以看到,输入ls回车之后,显示了2个ls,其中1个ls数据是cat命令自己回显出来的,
    另外一个ls就来自/dev/pts/3文件,那这是怎么回事呢,原因是这样的,
    cat和terminal都能获得键盘数据,cat将键盘数据直接回显到terminal上,
    而terminal捕获的数据将通过ptm发送到pts/3,而cat自己又在等待pts/3的数据,所以
    cat将从pts/3上再次读取到ptm发送过来的数据,再一次显示到terminal上,
    那同样是cat pts/3,为什么就不一样呢,通过strace发现,如果在
    terminal中直接调用库函数execve()执行另外一个shell命令,那么sh自身将停止对stdin进行数据读取,
    只是等待shell命令退出,数据读取操作权完全交给被执行的shell命令(cat),
    所以cat这时0,1,2都是对应pts/3,因为cat /dev/pts/3命令需要打开文件,
    所以fd = open('/dev/pts/3',...)之后,fd数值将等于3,这样cat /dev/pts/3他的
    0,1,2和3这4个句柄都对应pts/3节点[0为stdin,1为stdout,2为stderr]
    所以读取pts/3的进程只有了一个,没有人和他争数据了,当然cat能够完全获得数据了,呵呵[luther.gliethttp]
阅读(4656) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~