linux 终端下敲ctrl-c时,到底发生了什么?(转)
通过telnet登录到单板,然后按ctrl-c会发生什么情况,流程是怎么样的?
在分析之前,先介绍tty的相关知识。
我们可以认为,所有跟输入输出相关的操作,最终都由tty来接管。
举例来说,当我们敲 ls /dev时得到
- ls /dev/ -l
- total 0
- crw------- 1 root root 10, 235 8月 16 13:08 autofs
- drwxr-xr-x 2 root root 720 8月 16 13:08 block
- drwxr-xr-x 2 root root 80 8月 16 13:08 bsg
- crw-rw---- 1 root disk 10, 234 8月 16 13:08 btrfs-control
- drwxr-xr-x 3 root root 60 8月 16 13:08 bus
- lrwxrwxrwx 1 root root 3 8月 16 13:08 cdrom -> sr0
- drwxr-xr-x 2 root root 3760 8月 16 13:08 char
- crw------- 1 root root 5, 1 8月 16 13:09 console
- lrwxrwxrwx 1 root root 11 8月 16 13:08 core -> /proc/kcore
- drwxr-xr-x 2 root root 60 8月 16 13:08 cpu
- crw------- 1 root root 10, 60 8月 16 13:08 cpu_dma_latency
- crw------- 1 root root 10, 203 8月 16 13:08 cuse
- drwxr-xr-x 5 root root 100 8月 16 13:08 disk
- drwxr-xr-x 2 root root 100 8月 16 13:08 dri
- lrwxrwxrwx 1 root root 3 8月 16 13:08 dvd -> sr0
- crw------- 1 root root 10, 61 8月 16 13:08 ecryptfs
- crw-rw---- 1 root video 29, 0 8月 16 13:08 fb0
- lrwxrwxrwx 1 root root 13 8月 16 13:08 fd -> /proc/self/fd
- crw-rw-rw- 1 root root 1, 7 8月 16 13:08 full
- crw-rw-rw- 1 root root 10, 229 8月 16 13:08 fuse
- crw------- 1 root root 249, 0 8月 16 13:08 hidraw0
- crw------- 1 root root 249, 1 8月 16 13:08 hidraw1
- crw------- 1 root root 249, 2 8月 16 13:08 hidraw2
- crw------- 1 root root 10, 228 8月 16 13:08 hpet
- drwxr-xr-x 2 root root 0 8月 16 13:08 hugepages
- crw------- 1 root root 10, 183 8月 16 13:08 hwrng
- lrwxrwxrwx 1 root root 25 8月 16 13:08 initctl -> /run/systemd/initctl/fifo
- drwxr-xr-x 4 root root 320 8月 16 13:08 input
- crw-r--r-- 1 root root 1, 11 8月 16 13:08 kmsg
- lrwxrwxrwx 1 root root 28 8月 16 13:08 log -> /run/systemd/journal/dev-log
- brw-rw---- 1 root disk 7, 0 8月 16 13:08 loop0
- brw-rw---- 1 root disk 7, 1 8月 16 13:08 loop1
- brw-rw---- 1 root disk 7, 2 8月 16 13:08 loop2
- brw-rw---- 1 root disk 7, 3 8月 16 13:08 loop3
- brw-rw---- 1 root disk 7, 4 8月 16 13:08 loop4
- brw-rw---- 1 root disk 7, 5 8月 16 13:08 loop5
- brw-rw---- 1 root disk 7, 6 8月 16 13:08 loop6
- brw-rw---- 1 root disk 7, 7 8月 16 13:08 loop7
- crw-rw---- 1 root disk 10, 237 8月 16 13:08 loop-control
- drwxr-xr-x 2 root root 60 8月 16 13:08 mapper
- crw------- 1 root root 10, 227 8月 16 13:08 mcelog
- crw------- 1 root root 250, 0 8月 16 13:08 mei0
- crw-r----- 1 root kmem 1, 1 8月 16 13:08 mem
- crw------- 1 root root 10, 57 8月 16 13:08 memory_bandwidth
- drwxrwxrwt 2 root root 40 8月 16 13:08 mqueue
- drwxr-xr-x 2 root root 60 8月 16 13:08 net
- crw------- 1 root root 10, 59 8月 16 13:08 network_latency
- crw------- 1 root root 10, 58 8月 16 13:08 network_throughput
- crw-rw-rw- 1 root root 1, 3 8月 16 13:08 null
- crw-r----- 1 root kmem 1, 4 8月 16 13:08 port
- crw------- 1 root root 108, 0 8月 16 13:08 ppp
- crw------- 1 root root 10, 1 8月 16 13:08 psaux
- crw-rw-rw- 1 root tty 5, 2 8月 16 13:44 ptmx
- drwxr-xr-x 2 root root 0 8月 16 13:08 pts
- brw-rw---- 1 root disk 1, 0 8月 16 13:08 ram0
- brw-rw---- 1 root disk 1, 1 8月 16 13:08 ram1
- brw-rw---- 1 root disk 1, 10 8月 16 13:08 ram10
- brw-rw---- 1 root disk 1, 11 8月 16 13:08 ram11
- brw-rw---- 1 root disk 1, 12 8月 16 13:08 ram12
- brw-rw---- 1 root disk 1, 13 8月 16 13:08 ram13
- brw-rw---- 1 root disk 1, 14 8月 16 13:08 ram14
- brw-rw---- 1 root disk 1, 15 8月 16 13:08 ram15
- brw-rw---- 1 root disk 1, 2 8月 16 13:08 ram2
- brw-rw---- 1 root disk 1, 3 8月 16 13:08 ram3
- brw-rw---- 1 root disk 1, 4 8月 16 13:08 ram4
- brw-rw---- 1 root disk 1, 5 8月 16 13:08 ram5
- brw-rw---- 1 root disk 1, 6 8月 16 13:08 ram6
- brw-rw---- 1 root disk 1, 7 8月 16 13:08 ram7
- brw-rw---- 1 root disk 1, 8 8月 16 13:08 ram8
- brw-rw---- 1 root disk 1, 9 8月 16 13:08 ram9
- crw-rw-rw- 1 root root 1, 8 8月 16 13:08 random
- crw-rw-r--+ 1 root root 10, 62 8月 16 13:08 rfkill
- lrwxrwxrwx 1 root root 4 8月 16 13:08 rtc -> rtc0
- crw------- 1 root root 253, 0 8月 16 13:08 rtc0
- brw-rw---- 1 root disk 8, 0 8月 16 13:08 sda
- brw-rw---- 1 root disk 8, 1 8月 16 13:08 sda1
- brw-rw---- 1 root disk 8, 2 8月 16 13:08 sda2
- brw-rw---- 1 root disk 8, 3 8月 16 13:08 sda3
- brw-rw---- 1 root disk 8, 4 8月 16 13:08 sda4
- brw-rw---- 1 root disk 8, 5 8月 16 13:08 sda5
- brw-rw---- 1 root disk 8, 6 8月 16 13:08 sda6
- brw-rw---- 1 root disk 8, 7 8月 16 13:08 sda7
- brw-rw---- 1 root disk 8, 8 8月 16 13:08 sda8
- crw-rw---- 1 root disk 21, 0 8月 16 13:08 sg0
- crw-rw----+ 1 root cdrom 21, 1 8月 16 13:08 sg1
- drwxrwxrwt 2 root root 120 8月 16 13:08 shm
- crw------- 1 root root 10, 231 8月 16 13:08 snapshot
- drwxr-xr-x 3 root root 200 8月 16 13:08 snd
- brw-rw----+ 1 root cdrom 11, 0 8月 16 13:08 sr0
- lrwxrwxrwx 1 root root 15 8月 16 13:08 stderr -> /proc/self/fd/2
- lrwxrwxrwx 1 root root 15 8月 16 13:08 stdin -> /proc/self/fd/0
- lrwxrwxrwx 1 root root 15 8月 16 13:08 stdout -> /proc/self/fd/1
- crw-rw-rw- 1 root tty 5, 0 8月 16 13:08 tty
- crw--w---- 1 root tty 4, 0 8月 16 13:08 tty0
- crw--w---- 1 root tty 4, 1 8月 16 13:08 tty1
- crw--w---- 1 root tty 4, 10 8月 16 13:08 tty10
- crw--w---- 1 root tty 4, 11 8月 16 13:08 tty11
- crw--w---- 1 root tty 4, 12 8月 16 13:08 tty12
- crw--w---- 1 root tty 4, 13 8月 16 13:08 tty13
- crw--w---- 1 root tty 4, 14 8月 16 13:08 tty14
- crw--w---- 1 root tty 4, 15 8月 16 13:08 tty15
- crw--w---- 1 root tty 4, 16 8月 16 13:08 tty16
- crw--w---- 1 root tty 4, 17 8月 16 13:08 tty17
- crw--w---- 1 root tty 4, 18 8月 16 13:08 tty18
- crw--w---- 1 root tty 4, 19 8月 16 13:08 tty19
- crw--w---- 1 root tty 4, 2 8月 16 13:08 tty2
- crw--w---- 1 root tty 4, 20 8月 16 13:08 tty20
- crw--w---- 1 root tty 4, 21 8月 16 13:08 tty21
- crw--w---- 1 root tty 4, 22 8月 16 13:08 tty22
- crw--w---- 1 root tty 4, 23 8月 16 13:08 tty23
- crw--w---- 1 root tty 4, 24 8月 16 13:08 tty24
- crw--w---- 1 root tty 4, 25 8月 16 13:08 tty25
- crw--w---- 1 root tty 4, 26 8月 16 13:08 tty26
- crw--w---- 1 root tty 4, 27 8月 16 13:08 tty27
- crw--w---- 1 root tty 4, 28 8月 16 13:08 tty28
- crw--w---- 1 root tty 4, 29 8月 16 13:08 tty29
- crw--w---- 1 root tty 4, 3 8月 16 13:08 tty3
- crw--w---- 1 root tty 4, 30 8月 16 13:08 tty30
- crw--w---- 1 root tty 4, 31 8月 16 13:08 tty31
- crw--w---- 1 root tty 4, 32 8月 16 13:08 tty32
- crw--w---- 1 root tty 4, 33 8月 16 13:08 tty33
- crw--w---- 1 root tty 4, 34 8月 16 13:08 tty34
- crw--w---- 1 root tty 4, 35 8月 16 13:08 tty35
- crw--w---- 1 root tty 4, 36 8月 16 13:08 tty36
- crw--w---- 1 root tty 4, 37 8月 16 13:08 tty37
- crw--w---- 1 root tty 4, 38 8月 16 13:08 tty38
- crw--w---- 1 root tty 4, 39 8月 16 13:08 tty39
- crw--w---- 1 root tty 4, 4 8月 16 13:08 tty4
- crw--w---- 1 root tty 4, 40 8月 16 13:08 tty40
- crw--w---- 1 root tty 4, 41 8月 16 13:08 tty41
- crw--w---- 1 root tty 4, 42 8月 16 13:08 tty42
- crw--w---- 1 root tty 4, 43 8月 16 13:08 tty43
- crw--w---- 1 root tty 4, 44 8月 16 13:08 tty44
- crw--w---- 1 root tty 4, 45 8月 16 13:08 tty45
- crw--w---- 1 root tty 4, 46 8月 16 13:08 tty46
- crw--w---- 1 root tty 4, 47 8月 16 13:08 tty47
- crw--w---- 1 root tty 4, 48 8月 16 13:08 tty48
- crw--w---- 1 root tty 4, 49 8月 16 13:08 tty49
- crw--w---- 1 root tty 4, 5 8月 16 13:08 tty5
- crw--w---- 1 root tty 4, 50 8月 16 13:08 tty50
- crw--w---- 1 root tty 4, 51 8月 16 13:08 tty51
- crw--w---- 1 root tty 4, 52 8月 16 13:08 tty52
- crw--w---- 1 root tty 4, 53 8月 16 13:08 tty53
- crw--w---- 1 root tty 4, 54 8月 16 13:08 tty54
- crw--w---- 1 root tty 4, 55 8月 16 13:08 tty55
- crw--w---- 1 root tty 4, 56 8月 16 13:08 tty56
- crw--w---- 1 root tty 4, 57 8月 16 13:08 tty57
- crw--w---- 1 root tty 4, 58 8月 16 13:08 tty58
- crw--w---- 1 root tty 4, 59 8月 16 13:08 tty59
- crw--w---- 1 root tty 4, 6 8月 16 13:08 tty6
- crw--w---- 1 root tty 4, 60 8月 16 13:08 tty60
- crw--w---- 1 root tty 4, 61 8月 16 13:08 tty61
- crw--w---- 1 root tty 4, 62 8月 16 13:08 tty62
- crw--w---- 1 root tty 4, 63 8月 16 13:08 tty63
- crw--w---- 1 root tty 4, 7 8月 16 13:08 tty7
- crw--w---- 1 root tty 4, 8 8月 16 13:08 tty8
- crw--w---- 1 root tty 4, 9 8月 16 13:08 tty9
- crw------- 1 root root 5, 3 8月 16 13:08 ttyprintk
- crw-rw---- 1 root dialout 4, 64 8月 16 13:08 ttyS0
- crw-rw---- 1 root dialout 4, 65 8月 16 13:08 ttyS1
- crw-rw---- 1 root dialout 4, 74 8月 16 13:08 ttyS10
- crw-rw---- 1 root dialout 4, 75 8月 16 13:08 ttyS11
- crw-rw---- 1 root dialout 4, 76 8月 16 13:08 ttyS12
- crw-rw---- 1 root dialout 4, 77 8月 16 13:08 ttyS13
- crw-rw---- 1 root dialout 4, 78 8月 16 13:08 ttyS14
- crw-rw---- 1 root dialout 4, 79 8月 16 13:08 ttyS15
- crw-rw---- 1 root dialout 4, 80 8月 16 13:08 ttyS16
- crw-rw---- 1 root dialout 4, 81 8月 16 13:08 ttyS17
- crw-rw---- 1 root dialout 4, 82 8月 16 13:08 ttyS18
- crw-rw---- 1 root dialout 4, 83 8月 16 13:08 ttyS19
- crw-rw---- 1 root dialout 4, 66 8月 16 13:08 ttyS2
- crw-rw---- 1 root dialout 4, 84 8月 16 13:08 ttyS20
- crw-rw---- 1 root dialout 4, 85 8月 16 13:08 ttyS21
- crw-rw---- 1 root dialout 4, 86 8月 16 13:08 ttyS22
- crw-rw---- 1 root dialout 4, 87 8月 16 13:08 ttyS23
- crw-rw---- 1 root dialout 4, 88 8月 16 13:08 ttyS24
- crw-rw---- 1 root dialout 4, 89 8月 16 13:08 ttyS25
- crw-rw---- 1 root dialout 4, 90 8月 16 13:08 ttyS26
- crw-rw---- 1 root dialout 4, 91 8月 16 13:08 ttyS27
- crw-rw---- 1 root dialout 4, 92 8月 16 13:08 ttyS28
- crw-rw---- 1 root dialout 4, 93 8月 16 13:08 ttyS29
- crw-rw---- 1 root dialout 4, 67 8月 16 13:08 ttyS3
- crw-rw---- 1 root dialout 4, 94 8月 16 13:08 ttyS30
- crw-rw---- 1 root dialout 4, 95 8月 16 13:08 ttyS31
- crw-rw---- 1 root dialout 4, 68 8月 16 13:08 ttyS4
- crw-rw---- 1 root dialout 4, 69 8月 16 13:08 ttyS5
- crw-rw---- 1 root dialout 4, 70 8月 16 13:08 ttyS6
- crw-rw---- 1 root dialout 4, 71 8月 16 13:08 ttyS7
- crw-rw---- 1 root dialout 4, 72 8月 16 13:08 ttyS8
- crw-rw---- 1 root dialout 4, 73 8月 16 13:08 ttyS9
- crw------- 1 root root 10, 239 8月 16 13:08 uhid
- crw------- 1 root root 10, 223 8月 16 13:08 uinput
- crw-rw-rw- 1 root root 1, 9 8月 16 13:08 urandom
- crw-rw---- 1 root tty 7, 0 8月 16 13:08 vcs
- crw-rw---- 1 root tty 7, 1 8月 16 13:08 vcs1
- crw-rw---- 1 root tty 7, 2 8月 16 13:08 vcs2
- crw-rw---- 1 root tty 7, 3 8月 16 13:08 vcs3
- crw-rw---- 1 root tty 7, 4 8月 16 13:08 vcs4
- crw-rw---- 1 root tty 7, 5 8月 16 13:08 vcs5
- crw-rw---- 1 root tty 7, 6 8月 16 13:08 vcs6
- crw-rw---- 1 root tty 7, 7 8月 16 13:08 vcs7
- crw-rw---- 1 root tty 7, 128 8月 16 13:08 vcsa
- crw-rw---- 1 root tty 7, 129 8月 16 13:08 vcsa1
- crw-rw---- 1 root tty 7, 130 8月 16 13:08 vcsa2
- crw-rw---- 1 root tty 7, 131 8月 16 13:08 vcsa3
- crw-rw---- 1 root tty 7, 132 8月 16 13:08 vcsa4
- crw-rw---- 1 root tty 7, 133 8月 16 13:08 vcsa5
- crw-rw---- 1 root tty 7, 134 8月 16 13:08 vcsa6
- crw-rw---- 1 root tty 7, 135 8月 16 13:08 vcsa7
- drwxr-xr-x 2 root root 60 8月 16 13:08 vfio
- crw------- 1 root root 10, 63 8月 16 13:08 vga_arbiter
- crw------- 1 root root 10, 137 8月 16 13:08 vhci
- crw------- 1 root root 10, 238 8月 16 13:08 vhost-net
- prw-r----- 1 root adm 0 8月 16 13:08 xconsole
- crw-rw-rw- 1 root root 1, 5 8月 16 13:08 zero
另外还可以通过 /proc/tty/drivers得到tty相关驱动信息
- 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
- ttyprintk /dev/ttyprintk 5 3 console
- serial /dev/ttyS 4 64-111 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
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可以查看系统所有进程的控制终端,如:
- root 781 0.0 0.0 20052 2088 tty1 Ss+ 13:08 0:00 /sbin/agetty --noclear tty1 linux
ptmx 伪终端主
pts 伪终端从
tty0-tty63 虚拟终端,也叫虚拟控制台终端,virtual console ternimal 通常情况下是6个,这里我们查看
的服务器有63个
tty0可以理解为指针
ttyS0-ttyS3 串口终端
刚才说到,/proc/pid/stat的第7个字段是进程控制终端的设备号,可以根据此设备号得到具体的设备名。
- tty_nr = new_encode_dev(tty_devnum(sig->tty));
- seq_put_decimal_ll(m, ' ', tty_nr);
例如:
- cat /proc/781/stat
- 81 (agetty) S 1 781 781 1025 781 4194560 180 35 0 0 0 0 0 0 20 0 1 0 2778 20533248 522 18446744073709551615 4194304 4227700 140728752939200 140728752928472 139843305755776 0 0 6 0 18446744071579591276 0 0 17 2 0 0 0 0 0 6327824 6329216 20836352 140728752942807 140728752942841 140728752942841 140728752943083 0
即1025,十六进为0x401 ,主设备号为高4字节4,次设备号为低字节1,
对照/proc/tty/drivers得到
- unknown /dev/tty 4 1-63 console
即tty1。再根据ps -ax | grep 781得到
- /# ps -ax | grep 781
- 781 tty1 Ss+ 0:00 /sbin/agetty --noclear tty1 linux
确实是吻合的。
扯远了,下面来看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流程图如下:
说完了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);
即向进程组发送INT信号。
有了上面的知识铺垫,我们来看一个实际的问题。
我们先说明,这是一个busybox 1.13版本的bug,后来已经在高版本修复。
故障现象是这样的:
mips32&64 busybox 1.13 kernel 2.6.32
在串口终端执行如下脚本:
./tftp -r file_256M -g 192.168.1.1
'echo 1'
然后在tftp下载过程中,按ctrl-c
那么tftp进程将变成Z
故障的场景:
1)# ./test.sh之后 ash fork生成一个sh, 这个sh尝试按行解析test.sh,每读取一行就fork一个新进程并exec去执行。 然后sh通过waitpid等待子进程的完成。 正常情况下,两条命令顺序执行完,一切都结束的很好。
2)如果sh在waitpid过程中,即等待tftp完成的过程中,正好收到了SIGINT信号,那么sh就被人从阻塞状态里唤醒。
do_wait继续进入轮询检查到tftp不是为Z(因为这个时候tftp还没得到调度),所以不会回收tftp,然后退出系统调用返回用户态
时检查到有pending的SIGINT,于是get_signal_to_deliever,
由于sh注册了SIGINT信号的处理函数onsig,因此不会do_group_exit
3)onsig执行完之后,再解析下一条命令`echo 1` , 由于sh发现这条命令是引号包起来的, 说明是一个子命令,因此sh再次fork一个进程sh_2,去执行echo 1,并且通过管道pipe_wait等待 sh_2的echo 1的结果
4)sh_2在解析echo 1的过程中,需要获取tty串口的信息,由于这个时候控制终端tty串口已经被sh占有,所以sh_2 就会一直阻塞在tty_read,造成与sh的互锁。
因此问题出在,第3步sh如果收到了SIGINT信号,就不应该继续执行下一条echo 1指令了,而是整个进程退出 后面的一系列问题都是错误的流程导致的错误的结果。
附出问题时的阻塞现场
941 root 3840 S -/bin/ash
955 root 3840 S /bin/sh
969 root 3792 S /bin/sh ./test.sh //即上文的sh
970 root 0 Z [tftp]
971 root 3792 S /bin/sh ./test.sh //sh收到ctrl-c后不做处理,继续fork,尝试解析echo 1字符
972 root 3856 R ps (gdb) attach 969
The target endianness is set automatically (currently big endian)
[New Thread 969]
0x0000000120013370 in read ()
(gdb) bt
#0 0x0000000120013370 in read ()
#1 0x00000001200fe9d4 in safe_read (fd=3, buf=0xffffa53bf8, count=128)
at libbb/read.c:27
#2 0x00000001200fea68 in nonblock_safe_read (fd=3, buf=0xffffa53bf8,
count=128) at libbb/read.c:75
#3 0x000000012018fd3c in expbackq (cmd=0x120341b48, quoted=0, quotes=0)
at shell/ash.c:5557
#4 0x0000000120190580 in argstr (p=0x120341b6b "", flag=68, var_str_list=0x0)
at shell/ash.c:5783
#5 0x0000000120193574 in expandarg (arg=0x120341b70, arglist=0xffffa53e40,
flag=4) at shell/ash.c:6829
#6 0x0000000120198304 in evalcommand (cmd=0x120341b90, flags=0)
at shell/ash.c:8813
#7 0x0000000120196474 in evaltree (n=0x120341b90, flags=0)
at shell/ash.c:8005
#8 0x000000012019fd6c in cmdloop (top=1) at shell/ash.c:11739
#9 0x00000001201a46e8 in ash_main (argc=2, argv=0xffffa54408)
at shell/ash.c:13743
#10 0x00000001200fa098 in run_applet_no_and_exit (applet_no=219,
argv=0xffffa54408) at libbb/appletlib.c:747
#11 0x00000001200fa10c in run_applet_and_exit (name=0xffffa57f61 "sh",
argv=0xffffa54408) at libbb/appletlib.c:754
#12 0x00000001200fa24c in main (argc=2, argv=0xffffa54408)
at libbb/appletlib.c:791
(gdb) detach
Ending debugging.
(gdb) attach 971
[New Thread 971]
0x0000000120013370 in read ()
(gdb) bt
#0 0x0000000120013370 in read ()
#1 0x00000001200fe9d4 in safe_read (fd=0, buf=0x12031cd08, count=8191)
at libbb/read.c:27
#2 0x00000001200fea68 in nonblock_safe_read (fd=0, buf=0x12031cd08,
count=8191) at libbb/read.c:75
#3 0x0000000120199148 in preadfd () at shell/ash.c:9139
#4 0x00000001201994d8 in preadbuffer () at shell/ash.c:9258
#5 0x000000012019972c in pgetc () at shell/ash.c:9314
#6 0x000000012019f078 in xxreadtoken () at shell/ash.c:11365
#7 0x000000012019f3a8 in readtoken () at shell/ash.c:11499
#8 0x000000012019f64c in parsecmd (interact=0) at shell/ash.c:11576
#9 0x000000012019fc80 in cmdloop (top=1) at shell/ash.c:11724
#10 0x00000001201a46e8 in ash_main (argc=2, argv=0xffffa54408)
at shell/ash.c:13743
#11 0x00000001200fa098 in run_applet_no_and_exit (applet_no=219,
argv=0xffffa54408) at libbb/appletlib.c:747
#12 0x00000001200fa10c in run_applet_and_exit (name=0xffffa57f61 "sh",
argv=0xffffa54408) at libbb/appletlib.c:754
#13 0x00000001200fa24c in main (argc=2, argv=0xffffa54408)
at libbb/appletlib.c:791
(gdb)
http://blog.csdn.net/chenyu105/article/details/7738388
linux 终端下敲ctrl-c时,到底发生了什么?(转)的更多相关文章
- 如何记录linux终端下的操作日志
如何记录linux终端下的操作日志 在linux终端下,为方便检查操作中可能出现的错误,以及避免屏幕滚屏的限制,我们可以把操作日志记录下来.常用的工具有 screen,script,以及tee等,通过 ...
- linux终端下一些“风骚”的按键操作及Linux终端命令
linux终端下一些"风骚"的按键操作 <backspace> 删除 <ctrl-l> 清空屏幕, 相当于clear tab ...
- Linux终端下安装jdk
linux 终端下安装jdk(rpm方法) 1.下载jdk对应版本rpm文件(以下称为jdk.rpm) 放在对应文件夹下 2.使用命令给定权限 #chmod +x jdk.rpm 3.解压rpm文件 ...
- linux终端下 编译c语言程序
linux终端下,编译C语言程序步骤为: 采用vi进行源代码编写,编写完成后,:wq存盘退出,如: vi test.c 在命令行下,运行gcc编译程序,生成执行码,如: gcc -o test te ...
- Linux 终端下的颜色
Linux 终端下颜色的输出 在命令行下也能产生五颜六色的字体和图案,只需要加上一些颜色代码,例如 echo -e "\033[41;36m 红底绿字\033[0m" 其中41的位 ...
- 24小时学通Linux内核之电源开和关时都发生了什么
说实话感觉自己快写不下去了,其一是有些勉强跟不上来,其二是感觉自己越写越差,刚开始可能是新鲜感以及很多读者的鼓励,现在就是想快点完成自己制定的任务,不过总有几个读者给自己鼓励,很欣慰的事情,不多感慨了 ...
- 这些孩子在 Ubuntu 的 Linux 终端下玩耍
导读 我发现了一个孩子们在他们的计算机教室里玩得很开心的视频.我不知道他们在哪里,但我猜测是在印度尼西亚或者马来西亚.视频请自行搭梯子: https://youtu.be/z8taQPomp0Y 在L ...
- 在Linux终端下使用代理访问网络(转)
最近,需要在linux环境下使用脚本进行一些网络访问(主要是HTTP请求与文件下载),于是查阅了一些关于代理的资料. 以下是尝试的几种代理设置方法,以供参考: 一.使用wget命令进行代理访问 wge ...
- linux终端下文件不同颜色的含义
偶然注意到在终端下花花绿绿的目录显示效果,开始以为只是些特效,后来研究了一下,原来其中有些规律性的东西,总结如下: 蓝色表示目录:
随机推荐
- Ubuntu下使用虚拟机安装Windows XP(sunvirtualbox)
看完这个教程之后,网银?淘宝?CS?魔兽?---一个都不能少!好了,教程开始! 首先下载虚拟机,下载地址:http://download.virtualbox.org/virtualbox/3.1.0 ...
- wcf例子01
一.概述 Windows Communication Foundation(WCF)是由微软发展的一组数据通信的应用程序开发接口,可以翻译为Windows通讯接口,它是.NET框架的一部分.由 .NE ...
- Android Volley 之自定义Request
转载标明出处:http://blog.csdn.net/lmj623565791/article/details/24589837 今天群里一哥们需要自定义Volley的Request的例子,于是产生 ...
- linux下安装oracle11g 64位最简客户端(转)
安装环境 Linux服务器:SuSe11 sp1 64位 Oracle客户端:Oracle11gR2 64位(最简客户端) 部署流程 1.准备工作,首先在oracle官网下载最新的 ...
- 邮箱password复位图
blog宗旨:用图说话 辅助文字说明: 长处:用户系统完毕接口部分.界面留给业务自行设计. 缺点:邮箱发送邮件定制模板,没有完毕松耦合. 改进点:邮箱的准备发送邮件模板. 版权声明:本文博客原创文章, ...
- group by和order by的错误
select u.Col_Name from hs_user u left join ( select tuid,count(*) as 'col_sumtopic' from BBS_Topic ...
- 隐藏快捷方式扩展名(.lnk)
自从装了Windows 7操作系统以后,有一个问题一直困扰着我.当我去掉“目录选项中”的“隐藏已知文件类型的扩展名”选项的“√”之后,桌面上的快捷方式的扩展名也出来了,看上去非常不爽.记得在XP.Vi ...
- Arcgis for Js之Graphiclayer扩展具体解释
在前两节,讲到了两种不同方式的聚类,一种是基于距离的,一种是基于区域范围的,两种不同的聚类都是通过扩展esri/layers/GraphicsLayer方法来实现的.在本节,就具体的讲讲esri/la ...
- JAVA环境配置---------jdk安装路径中存在空格的问题
我把jdk 安装在C:\Program Files下 而ide启动脚本 startup.bat 例如以下: if "%OS%"=="Windows_NT" se ...
- CodeForces 484A Bits
意甲冠军: 10000询价 每次查询输入L和R(10^18) 在区间的二进制输出指示1大多数数字 1个数同样输出最小的 思路: YY一下 认为后几位全是1的时候能保证1的个数多 那么怎样构造 ...