2009-01-24 16 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