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

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

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

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


    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)。我们可以通过一个实例看一下如何使用:

  1. #include <stdio.h>  
  2. #include <sys/stat.h>  
  3. #include <fcntl.h>  
  4. #include <sys/ioctl.h>  
  5. #include <termios.h>  
  6.   
  7. #include <unistd.h>  
  8. #include <pthread.h>     
  9. #include <sched.h>    
  10.   
  11. #include <stropts.h>  
  12.   
  13.   
  14. char    *mptname = "/dev/ptmx";         /* master pseudo-tty device */  
  15. //...  
  16. int master,slave;  
  17.   
  18. struct termios sbuf;  
  19. struct  winsize size;  
  20.   
  21. void getmaster()  
  22. {  
  23.     struct stat stb;  
  24.     if ((master = open(mptname, O_RDWR))>= 0) {  /* a pseudo-tty is free */  
  25.         (void) ioctl(0, TCGETS, (char *)&sbuf);  
  26.   
  27.         (void) ioctl(0, TIOCGWINSZ, (char *)&size);     /// TIOCGWINSZ 获得终端设备的窗口大小 get  
  28.         return;  
  29.     } else {                    /* out of pseudo-tty's */  
  30.         perror(mptname);  
  31.         fprintf(stderr, gettext("Out of pseudo-tty's\n"));  
  32.         exit(1);  
  33.     }  
  34. }  
  35.   
  36. void getslave()  
  37. {  
  38.     char *slavename;    /* name of slave pseudo-tty */  
  39.     grantpt(master);        /* change permissions of slave */  
  40.     unlockpt(master);          /* unlock slave */  
  41.     slavename = ptsname(master);        /* get name of slave */  
  42.     slave = open(slavename, O_RDWR);    /* open slave */  
  43.     if (slave <0) {       /* error on opening slave */  
  44.         perror(slavename);  
  45.         exit(1);  
  46.     }  
  47.   
  48.     ioctl(slave, I_PUSH, "ptem");   /* push pt hw emulation module */  
  49.     ioctl(slave, I_PUSH, "ldterm");  /* push line discipline */  
  50.   
  51.     (void) ioctl(slave, TCSETSF, (char *)&sbuf);  
  52.     (void) ioctl(slave, TIOCSWINSZ, (char *)&size);  
  53. }  
然后我们再来看一下glibc中对ptsname(3)的实现:
(源文件sysdeps/unix/sysv/linux/ptsname.c)

  1. /** Return the pathname of the pseudo terminal slave associated with 
  2.    the master FD is open on, or NULL on errors. 
  3.    The returned storage is good until the next call to this function.  */  
  4. char * ptsname (int fd)  
  5. {  
  6.   static char peername[1024];  /** XXX */  
  7.   error_t err;  
  8.   
  9.   err = __ptsname_r (fd, peername, sizeof (peername));  
  10.   if (err)  
  11.     __set_errno (err);  
  12.   
  13.   return err ? NULL : peername;  
  14. }  
  15.   
  16.   
  17. /** Store at most BUFLEN characters of the pathname of the slave pseudo 
  18.    terminal associated with the master FD is open on in BUF. 
  19.    Return 0 on success, otherwise an error number.  */  
  20. int  
  21. __ptsname_r (int fd, char *buf, size_t buflen)  
  22. {  
  23.   char peername[1024];  /** XXX */  
  24.   size_t len;  
  25.   error_t err;  
  26.   
  27.   peername[0] = '\0';  
  28.   if (err = HURD_DPORT_USE (fd, __term_get_peername (port, peername)))  
  29.     return _hurd_fd_error (fd, err);  
  30.   
  31.   len = strlen (peername) + 1;  
  32.   if (len > buflen)  
  33.     return ERANGE;  
  34.   
  35.   memcpy (buf, peername, len);  
  36.   return 0;  
  37. }  


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

[plain]  view plain copy print ?
  1. sina@ubuntu:~/work/seriol$ mount | grep devpts  
  2. devpts on /dev/pts type devpts (rw,noexec,nosuid,gid=5,mode=0620)  

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


关于伪终端的使用可以详见《unix环境高级编程》第19章(父子进程之间)


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值