2009-01-24 48 views
23

我想用C语言在Linux系统上编写一个简单,笨拙的X终端仿真器。* nix伪终端如何工作?主/从通道是什么?

起初,我只是认为我必须打开一个shell并显示其输出。 我检查了xterm和rxvt代码,它看起来有点复杂。

首先,我必须用openpty打开一个伪终端。所以我看看手册页,看到openpty会填充2个文件描述符,即主服务器和从服务器。 由于这些特殊文件的系统依赖性,xterm和rxvt代码都是混乱的。

我明白termios的东西:它只是一堆关于终端转义码的信息。 我真的没有得到的是:我想用主/从文件描述符做什么?

一个打开终端,登录并在shell上执行“ls”的示例程序非常棒。

(英语不是我的母语,原谅我最后的错误)

编辑: 这是我想出了示例代码:

#include <stdlib.h> 
#include <stdio.h> 
#include <unistd.h> 
#include <string.h> 
#include <pty.h> 
#include <utmp.h> 
#include <ctype.h> 

void 
safe_print (char* s) 
{ 
    while(*s) { 
     if(*s == '\n') 
      putchar("\n"); 
     else if(iscntrl(*s)) 
      printf("\\e(%d)", *s); 
     else 
      putchar(*s); 
     s++; 
    } 
} 


int 
main (int argc, char** argv) 
{ 
    char buf[BUFSIZ] = {0}; 
    int master; 
    int ret = forkpty(&master, NULL, NULL, NULL); 

    if(ret == -1) 
     puts("no fork"), exit(0); 

    if(!ret) { 
     execl("/bin/sh", "sh", NULL); 
     exit(0); 
    } 

    sleep(1); /* let the shell run */ 


    if(argc >= 2) { 
     write(master, argv[1], strlen(argv[1])); 
     write(master, "\n", 1); 
    } else { 
     write(master, "date\n", sizeof "date\n"); 
    } 


    while(1) { 
     switch(ret = read(master, buf, BUFSIZ)) { 
     case -1: 
      puts("error!"); 
      exit(1); 
      break; 
     case 0: 
      puts("nothing.."), sleep(1); 
      break; 
     default: 
      buf[ret] = '\0'; 
      safe_print(buf); 

     } 
    } 

    close(master); 

    return 0; 
}  
+0

我认为,名为“屏幕”的命令行程序使用这个。这样,您就可以在主机上拥有一个登录控制台,并且如果您被抛出,您可以重新登录并重新连接该会话,然后继续。这是pty的本质。它有一个与主机系统相互作用的通道,以及你在外部告诉它要做什么(并看到结果)的“后向通道”。我也没有任何实施经验,我在“Linux应用程序开发”中阅读了他们。在X下,我想还有更多的橱窗装饰,但其基本原则应该是 – gbarry 2009-01-24 17:49:47

回答

19

对于主/从部分您的问题,从pty(4)手册页(从我的系统上的openpty(3)手册页引用):

伪终端是一对 字符设备,主设备和 从设备。从设备 向处理提供与tty(4)中所述的相同的接口 。 然而,尽管所有其他设备 ,其提供在TTY描述 接口(4)具有的 硬件设备在他们后面的某种,从属 装置具有,相反,另一过程 通过主 一半的操纵它伪终端。 也就是, 写在主设备 上的任何东西都作为输入 给予从设备,并且在从设备 上书写的任何东西都作为主设备上的输入呈现。

手册页是你的朋友。

1

我刚刚尝试了this tutorial上找到的例子,它们对我来说工作得很好,我认为它们对于这个问题是一个有趣的起点。编辑: 本教程简要说明了伪终端功能。解释是一步一步完成的,后面是例子。

下面的示例示出了如何在两个部分上的伪终端的侧创建新的伪终端,和的过程中,一个书写,另从从属侧边读出的伪终端。

#define _XOPEN_SOURCE 600 
#include <stdlib.h> 
#include <fcntl.h> 
#include <errno.h> 
#include <unistd.h> 
#include <stdio.h> 
#define __USE_BSD 
#include <termios.h> 


int main(void) 
{ 
int fdm, fds, rc; 
char input[150]; 

fdm = posix_openpt(O_RDWR); 
if (fdm < 0) 
{ 
fprintf(stderr, "Error %d on posix_openpt()\n", errno); 
return 1; 
} 

rc = grantpt(fdm); 
if (rc != 0) 
{ 
fprintf(stderr, "Error %d on grantpt()\n", errno); 
return 1; 
} 

rc = unlockpt(fdm); 
if (rc != 0) 
{ 
fprintf(stderr, "Error %d on unlockpt()\n", errno); 
return 1; 
} 

// Open the slave PTY 
fds = open(ptsname(fdm), O_RDWR); 
printf("Virtual interface configured\n"); 
printf("The master side is named : %s\n", ptsname(fdm)); 

// Creation of a child process 
if (fork()) 
{ 
    // Father 

    // Close the slave side of the PTY 
    close(fds); 
    while (1) 
    { 
    // Operator's entry (standard input = terminal) 
    write(1, "Input : ", sizeof("Input : ")); 
    rc = read(0, input, sizeof(input)); 
    if (rc > 0) 
    { 
     // Send the input to the child process through the PTY 
     write(fdm, input, rc); 

     // Get the child's answer through the PTY 
     rc = read(fdm, input, sizeof(input) - 1); 
     if (rc > 0) 
     { 
     // Make the answer NUL terminated to display it as a string 
     input[rc] = '\0'; 

     fprintf(stderr, "%s", input); 
     } 
     else 
     { 
     break; 
     } 
    } 
    else 
    { 
     break; 
    } 
    } // End while 
} 
else 
{ 
struct termios slave_orig_term_settings; // Saved terminal settings 
struct termios new_term_settings; // Current terminal settings 

    // Child 

    // Close the master side of the PTY 
    close(fdm); 

    // Save the default parameters of the slave side of the PTY 
    rc = tcgetattr(fds, &slave_orig_term_settings); 

    // Set raw mode on the slave side of the PTY 
    new_term_settings = slave_orig_term_settings; 
    cfmakeraw (&new_term_settings); 
    tcsetattr (fds, TCSANOW, &new_term_settings); 

    // The slave side of the PTY becomes the standard input and outputs of the child process 
    close(0); // Close standard input (current terminal) 
    close(1); // Close standard output (current terminal) 
    close(2); // Close standard error (current terminal) 

    dup(fds); // PTY becomes standard input (0) 
    dup(fds); // PTY becomes standard output (1) 
    dup(fds); // PTY becomes standard error (2) 

    while (1) 
    { 
    rc = read(fds, input, sizeof(input) - 1); 

    if (rc > 0) 
    { 
     // Replace the terminating \n by a NUL to display it as a string 
     input[rc - 1] = '\0'; 

     printf("Child received : '%s'\n", input); 
    } 
    else 
    { 
     break; 
    } 
    } // End while 
} 

return 0; 
} // main