首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 操作系统 > UNIXLINUX >

linux 终端上敲ctrl-c时,到底发生了什么

2012-08-15 
linux 终端下敲ctrl-c时,到底发生了什么?通过telnet登录到单板,然后按ctrl-c会发生什么情况,流程是怎么样

linux 终端下敲ctrl-c时,到底发生了什么?
通过telnet登录到单板,然后按ctrl-c会发生什么情况,流程是怎么样的?

在分析之前,先介绍tty的相关知识。
我们可以认为,所有跟输入输出相关的操作,最终都由tty来接管。
举例来说,当我们敲 ls /dev时得到
[root@ dev]# ls /dev
autofs           fb     loop0  lp3                 oldmem  sda1        snapshot  tty12  tty22  
tty32  tty42  tty52  tty62    usbmon1
block            fb0    loop1  MAKEDEV             port    sda2        snd       tty13  tty23  
tty33  tty43  tty53  tty63    usbmon2
bsg              fd     loop2  mapper              ppp     sda3        stderr    tty14  tty24  
tty34  tty44  tty54  tty7     usbmon3
bus              full   loop3  mcelog              ptmx    sda4        stdin     tty15  tty25  
tty35  tty45  tty55  tty8     usbmon4
char             fuse   loop4  megadev0            pts     sda5        stdout    tty16  tty26  
tty36  tty46  tty56  tty9     usbmon5
console          hpet   loop5  mem                 random  sda6        systty    tty17  tty27  
tty37  tty47  tty57  ttyS0    vcs
core             hvc0   loop6  net                 rfkill  sequencer   tty       tty18  tty28  
tty38  tty48  tty58  ttyS1    vcs1
cpu              input  loop7  network_latency     root    sequencer2  tty0      tty19  tty29  
tty39  tty49  tty59  ttyS2    vcsa
cpu_dma_latency  kmem   lp0    network_throughput  rtc     sg0         tty1      tty2   tty3   
tty4   tty5   tty6   ttyS3    vcsa1
disk             kmsg   lp1    null                rtc0    sg1         tty10     tty20  tty30  
tty40  tty50  tty60  urandom  vga_arbiter
dri              log    lp2    nvram               sda     shm         tty11     tty21  tty31  
tty41  tty51  tty61  usbmon0  zero
[root@ dev]#


另外还可以通过 /proc/tty/drivers得到tty相关驱动信息
[root@ dev]# cat /proc/tty/drivers 
/dev/tty             /dev/tty        5       0 system:/dev/tty
/dev/console         /dev/console    5       1 system:console
/dev/ptmx            /dev/ptmx       5       2 system
/dev/vc/0            /dev/vc/0       4       0 system:vtmaster
serial               /dev/ttyS       4 64-95 serial
pty_slave            /dev/pts      136 0-1048575 pty:slave
pty_master           /dev/ptm      128 0-1048575 pty:master
unknown              /dev/tty        4 1-63 console
[root@ dev]# 

tty  控制终端,control terminal 这个是跟进程相关的,可以理解为一个指针,指向具体的tty终端设备。
     进程可以通过open /dev/tty来获取当前进程使用的tty终端具体是哪个,例如是ptmx,还是pts/2,还是

tty2,或者是ttyS0
     linux shell下tty命令的可以查看当前进程的控制终端。例如:
     [root@ dev]# tty
     /dev/pts/10
     具体的实现是靠ttyname(0)
     即返回当前进程的标准输入,对应的tty终端设备是哪个。
     另外通过ps -ax可以查看系统所有进程的控制终端,如:
     18448 ?        Ss     0:06 sshd: root@pts/9 
     18451 pts/9    Ss+    0:00 -bash
     3424 tty2     Ss+    0:00 /sbin/mingetty /dev/tty2
     这是通过进程的/proc/pid/stat的第7个字段,当前控制终端的设备号得到的。

ptmx 伪终端主
pts  伪终端从
tty0-tty63  虚拟终端,也叫虚拟控制台终端,virtual console ternimal 通常情况下是6个,这里我们查看
的服务器有63个
tty0可以理解为指针
ttyS0-ttyS3 串口终端 

刚才说到,/proc/pid/stat的第7个字段是进程控制终端的设备号,可以根据此设备号得到具体的设备名。

例如:

[root@ dev]# cat /proc/1191/stat
1191 (bash) S 1185 1191 1191 34849 1191 4202752 3550 51329644 0 1551 5 6 157769 26478 20 0 1 0 
36325973 111104000 471 18446744073709551615 4194304 5064252 140735122662272 140735122657512 
263321371088 0 0 3686404 1266761467 18446744071582166303 0 0 17 0 0 0 0 0 0
[root@ dev]# 
34849即0x8821,主设备号为高4字节0x88即136,次设备号为低4字节0x21即33
对照/proc/tty/drivers得到/dev/pts/33
再根据ps -ax | grep 1191得到
 1191 pts/33   Ss     0:00 -bash
确实是吻合的。


扯远了,下面来看telnet的流程

26912 ?        Ss     0:00 busybox telnetd
当用户通过telnet客户端连接telnetd后
26912 ?        Ss     0:00 busybox telnetd
27030 ?        Ss     0:00 login -- chenyu
27038 pts/6    Ss+    0:00 -bash

可以看出telnetd先fork出gettty并执行login,登录成功后则再fork出实际的bash,

这个bash用的是伪终端pts/6,

以下数据的PPid字段可以证明:
Name:   busybox
PPid:   1

Name:   login
Pid:    27030
PPid:   26912

Name:   bash
Pid:    27038
PPid:   27030

bash并不知道这个pts/6具体是个什么设备,只知道往里面读写。
每次在telnet客户端,敲一个字符,就会把数据发给telnetd,telnetd再操作ptmx通过
tty displine将数据格式化传递给pts/6,然后bash从pts/6收到数据进行处理,再写回pts/6,
传给ptmx,由telnetd将数据再传递回telnet客户端。 可以通过每次在telnet客户端
敲一个字符,telnetd的调度次数就增加2,来大体验证这个流程(telnetd收到客户端数据被唤醒,bash通过
伪终端把数据写回唤醒telnetd,一共两次唤醒)

具体的telnet流程图如下:

linux 终端上敲ctrl-c时,到底发生了什么



说完了telnetd的原理,再来看看在telnet客户端敲ctrl-c时,系统会发生什么。
首先可以明确的是,ctrl-c传递到telnetd后,会通过写伪终端主设备ptmx的方式传递给从设备进而传给bash。
那么,到底是telnetd,还是bash端会处理这个ctrl-c?

实际上我们可以这么理解,在直接登录shell时,没有telnetd这一层,

应该是shell进程本身被ctrl-c键盘中断打断,

然后再驱动里通过tty收到了此特殊字符,然后在接收流程里,决定是否给当前进程shell发SIG信号。
那么我们就认为telnetd不过是一个中转站,具体的数据处理还是要靠bash来完成。

因此当bash从pts/6收到ctrl-c特殊字符后,会进行特殊处理。来看这段代码:
首先,不管是伪终端,还是串口,亦或是控制台,接收到字符后都要推送给tty displine 的接收函数
n_tty_receive_buf做进一步处理。n_tty_receive_buf的数据来源是tty->buf。
这个tty->buf的来源,要么是伪终端主设备pty_write写入的数据,要么是中断往里面写入的数据。

如果是伪终端pty_write
static int pty_write(struct tty_struct *tty, const unsigned char *buf, int c)
{
     //得到从设备
struct tty_struct *to = tty->link;


if (tty->stopped)
return 0;


if (c > 0) {
/* Stuff the data into the input queue of the other end */
     //让从设备强行收数据,即将buf数据传递给从设备的tty->buf
c = tty_insert_flip_string(to, buf, c);
/* And shovel */
if (c) {
            //将从设备的tty->buf临时缓冲数据提交给displine处理,即提交给tty->read_buf
            //供用户空间使用
             //这个一般是通过schedule_work来完成
tty_flip_buffer_push(to);
tty_wakeup(tty);
}
}
return c;
}

如果是串口中断,则是
serial8250_interrupt->serial8250_handle_port->receive_chars->
uart_insert_char->tty_insert_flip_char 将串口数据拷贝到tty->buf中之后,
再tty_flip_buffer_push(tty);将数据推送给displine处理

因此可以看出,串口和伪终端的不同之处在于,数据来源不同,一个是tty_write写入的,
一个是串口中断接受到的。
所以,我们在串口,或者是在telnet下,敲ctrl-c,实际上都会走到tty_flip_buffer_push,
进行数据的分析和接受。具体的特殊字符解析,应该也是在tty_flip_buffer_push之后的步骤完成。

那下面我们来看tty_flip_buffer_push
* This routine is called out of the software interrupt to flush data
* from the buffer chain to the line discipline.


最终调到到receive_buf->n_tty_receive_buf->n_tty_receive_break->
isig(SIGINT, tty, 1);->
if (tty->pgrp)
kill_pgrp(tty->pgrp, sig, 1);


还剩下tty的pgrp字段和kill_pgrp分析
待续











热点排行