Linux下的console和terminal


2023年12月21日发(作者:飞机实时动态查询)

Linux下的console和terminal

console和terminal是很容易让人迷惑的两个概念。根据wikipedia上的定义,小型计算机的console应该就是键盘加显示器;而terminal则是输入数据进去,和显示数据来源的设备,通常是一个计算机系统。

Linux下的console除了真实的硬件设备外,还有virtual console,也就是你按alt+Fn或者alt+ctrl+Fn切换到的东西。所谓虚拟就是这些console共享同一个真实的设备,只有一个活动的console才显示在前面。这些console对应的设备是:/dev/ttyN,其中1 ≤ N ≤ 63。而/dev/tty0则是指向当前的terminal;/dev/console是指向当前console,但它现在并_不是_对/dev/tty0的符号链接。更多可参考console(4)。

/dev/tty是另一个特殊设备,它指向控制终端(controlling terminal)。如果某个进程的控制终端是/dev/tty3,那么/dev/tty就指向/dev/tty3了。控制终端是什么概念?它是一个进程的某个属性,是依附带该进程上的终端。比如我们在某个终端下输入ctrl+C,那么它控制的前台进程就会收到SIGINT,而后台进程会收到SIGTTIN或SIGTTOU ,如果它们读写该终端的话。被同一个终端控制的所有进程被称为一个会话(session),会话的领导就是创建改会话的进程,其子进程也会被该终端控制。所以,1) 需要交互的命令行程序通常会从/dev/tty这个设备进行读写;2) Unix后台进程都需要在fork之后调用setsid(2),3) 需要加O_NOCTTY,当你open一个可能是终端的文件时。

另外,想要确定/dev/tty究竟是指向哪个设备,可以调用TIOCCONS ioctl。参考tty(4)。

下面是另外一个概念——伪终端(pseudo-terminal),根据pty(7)的介绍,伪终端一对虚拟设备,提供端到端双向通信的通路,一端称为master,另一端称为slave。在slave那端看到的和在真实终端看到的效果一样。所以伪终端一般被ssh等网络登录程序使用。历史上,有两套伪终端接口,一个是Unix 98伪终端,另一个是BSD伪终端。

BSD提供的接口很简单:/dev/pty[p-za-e][0-9a-f] 是master;

/dev/tty[p-za-e][0-9a-f] 是slave,它们都是配好对的。这样看起来很简单,但对程序员来说不容易,要到一个合适的终端需要一个个从头尝试。所以这种方式已经被遗弃。而Unix 98伪终端则完全不同,它始终使用/dev/ptmx作为master复制设备,然后在每次打开它的时候才得到一个master设备的fd,同时在/dev/pts/目录下得到一个slave设备。这样编程就相对容易了,根据pts(4)介绍,需要三个新的API: ptsname(3),grantpt(3)和unlockpt(3)。我们可以通过一个实例看一下如何使用:

(以下代码摘自netvirt)

PLAIN TEXT

C:

1. char *mptname = "/dev/ptmx"; /* master pseudo-tty device */

2. //...

3. void

4. getmaster()

5. {

6. struct stat stb;

7.

8. if ((master = open(mptname, O_RDWR))>= 0) { /* a pseudo-tty is free

*/

9. (void) ioctl(0, TCGETS, (char *)&b);

10. (void) ioctl(0, TIOCGWINSZ, (char *)&size);

11. return;

12. } else { /* out of pseudo-tty's */

13. perror(mptname);

14. fprintf(stderr, gettext("Out of pseudo-tty'sn"));

15. fail();

16. }

17. }

18.

19. void

20. getslave()

21. {

22. char *slavename; /* name of slave pseudo-tty */

23.

24. grantpt(master); /* change permissions of slave */

25. unlockpt(master); /* unlock slave */

26. slavename = ptsname(master); /* get name of slave */

27. slave = open(slavename, O_RDWR); /* open slave */

28. if (slave <0) { /* error on opening slave */

29. perror(slavename);

30. fail();

31. }

32. ioctl(slave, I_PUSH, "ptem"); /* push pt hw emulation module */

33. ioctl(slave, I_PUSH, "ldterm"); /* push line discipline */

34.

35. (void) ioctl(slave, TCSETSF, (char *)&b);

36. (void) ioctl(slave, TIOCSWINSZ, (char *)&size);

37. }

然后我们再来看一下glibc中对ptsname(3)的实现:

(源文件sysdeps/unix/sysv/linux/ptsname.c)

PLAIN TEXT

C:

1. #define _PATH_DEVPTS "/dev/pts/"

2.

3. char *

4. ptsname (int fd)

5. {

6. return __ptsname_r (fd, buffer, sizeof (buffer)) != 0 ? NULL : buffer;

7. }

8.

9. int

10. __ptsname_r (int fd, char *buf, size_t buflen)

11. {

12.

13. ...

14.

15. if (__ioctl (fd, TIOCGPTN, &ptyno) == 0)

16. ...

17. numbuf[sizeof (numbuf) - 1] = '0';

18. p = _itoa_word (ptyno, &numbuf[sizeof (numbuf) - 1], 10, 0);

19. ...

20. memcpy (__stpcpy (buf, devpts), p, &numbuf[sizeof (numbuf)] - p);

我们可以看出,实际上是调用ioctl TIOCGPTN,通过内核,而Linux内核又是通过devpts这种文件系统实现了这一切:

$ mount | grep devpts

devpts on /dev/pts type devpts (rw,gid=5,mode=620)

这样我们终于把一切搞清楚了。:-)

If you're new here, you may want to subscribe to my RSS feed. Thanks for

visiting!

This entry was posted on Monday, November 10th, 2008 at 7:01 am and is filed under Linux

Application. You can follow any responses to this entry through the RSS 2.0 feed. You

can leave a response, or trackback from your own site.


本文发布于:2024-09-23 02:31:54,感谢您对本站的认可!

本文链接:https://www.17tex.com/fanyi/19915.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:终端   设备   进程
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议