打印
[信息]

Linux下的consolen(控制台)和terminal(终端)

[复制链接]
884|3
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
classroom|  楼主 | 2021-5-12 11:18 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

  console和terminal是很容易让人迷惑的两个概念。要本质上区别这两个名词背后的差异,应该从它的使用角度去区分。

    terminal(终端),其实是处于用户使用系统的角度来说的,相对于提供服务的系统终端是用户使用这个系统的入口,这个名词的使用范围比console要广。而console(控制台)则强调是控制系统(几乎就是大机了)的地方,其使用者主要是管理员。由于历史的的原因,在计算机世界里terminal和console常常指同一样东西,因为这种东西能完成两种角色,后来便发展到一些技术人员已经不严格称呼这样东西了。但在著述文档时,应该要根据上下文环境决定使用哪个名词。

    举个例子:对于摄像系统,摆在监控室里面的那些就是terminal而不是console.手机也是一个terminal,它是用户使用通信系统的入口,而手机这个小系统也是可以通过console来管理的。




使用特权

评论回复
沙发
classroom|  楼主 | 2021-5-12 11:18 | 只看该作者

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)。



使用特权

评论回复
板凳
classroom|  楼主 | 2021-5-12 11:20 | 只看该作者

下面是另外一个概念——伪终端(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)。我们可以通过一个实例看一下如何使用:


[cpp] view plain copy


  • #include <stdio.h>
  • #include <sys/stat.h>
  • #include <fcntl.h>
  • #include <sys/ioctl.h>
  • #include <termios.h>
  • #include <unistd.h>
  • #include <pthread.h>
  • #include <sched.h>
  • #include <stropts.h>
  • char    *mptname = "/dev/ptmx";         /* master pseudo-tty device */
  • //...
  • int master,slave;
  • struct termios sbuf;
  • struct  winsize size;
  • void getmaster()
  • {
  •     struct stat stb;
  •     if ((master = open(mptname, O_RDWR))>= 0) {  /* a pseudo-tty is free */
  •         (void) ioctl(0, TCGETS, (char *)&sbuf);
  •         (void) ioctl(0, TIOCGWINSZ, (char *)&size);     /// TIOCGWINSZ 获得终端设备的窗口大小 get
  •         return;
  •     } else {                    /* out of pseudo-tty's */
  •         perror(mptname);
  •         fprintf(stderr, gettext("Out of pseudo-tty's\n"));
  •         exit(1);
  •     }
  • }
  • void getslave()
  • {
  •     char *slavename;    /* name of slave pseudo-tty */
  •     grantpt(master);        /* change permissions of slave */
  •     unlockpt(master);          /* unlock slave */
  •     slavename = ptsname(master);        /* get name of slave */
  •     slave = open(slavename, O_RDWR);    /* open slave */
  •     if (slave <0) {       /* error on opening slave */
  •         perror(slavename);
  •         exit(1);
  •     }
  •     ioctl(slave, I_PUSH, "ptem");   /* push pt hw emulation module */
  •     ioctl(slave, I_PUSH, "ldterm");  /* push line discipline */
  •     (void) ioctl(slave, TCSETSF, (char *)&sbuf);
  •     (void) ioctl(slave, TIOCSWINSZ, (char *)&size);
  • }



使用特权

评论回复
地板
classroom|  楼主 | 2021-5-12 11:21 | 只看该作者
然后我们再来看一下glibc中对ptsname(3)的实现:
(源文件sysdeps/unix/sysv/linux/ptsname.c)


[cpp] view plain copy


  • /** Return the pathname of the pseudo terminal slave associated with
  •    the master FD is open on, or NULL on errors.
  •    The returned storage is good until the next call to this function.  */
  • char * ptsname (int fd)
  • {
  •   static char peername[1024];  /** XXX */
  •   error_t err;
  •   err = __ptsname_r (fd, peername, sizeof (peername));
  •   if (err)
  •     __set_errno (err);
  •   return err ? NULL : peername;
  • }
  • /** Store at most BUFLEN characters of the pathname of the slave pseudo
  •    terminal associated with the master FD is open on in BUF.
  •    Return 0 on success, otherwise an error number.  */
  • int
  • __ptsname_r (int fd, char *buf, size_t buflen)
  • {
  •   char peername[1024];  /** XXX */
  •   size_t len;
  •   error_t err;
  •   peername[0] = '\0';
  •   if (err = HURD_DPORT_USE (fd, __term_get_peername (port, peername)))
  •     return _hurd_fd_error (fd, err);
  •   len = strlen (peername) + 1;
  •   if (len > buflen)
  •     return ERANGE;
  •   memcpy (buf, peername, len);
  •   return 0;
  • }



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


[plain] view plain copy


  • sina@ubuntu:~/work/seriol$ mount | grep devpts
  • devpts on /dev/pts type devpts (rw,noexec,nosuid,gid=5,mode=0620)


实际上经过代码测试发现当打开/dev/ptmx 在主设备号成功返回的时候在 /dev/pts目录下也创建一个从设备。
嵌入式学习交流群:561213221

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

414

主题

1993

帖子

1

粉丝