source

*nix 의사단말기는 어떻게 작동합니까?마스터/슬레이브 채널은 무엇입니까?

nicesource 2023. 10. 16. 21:55
반응형

*nix 의사단말기는 어떻게 작동합니까?마스터/슬레이브 채널은 무엇입니까?

저는 리눅스 시스템의 C에서 단순하고 바보 같은 X 터미널 에뮬레이터를 쓰고 싶습니다.

처음에는 껍데기를 열고 출력물을 보여줘야 할 거라고 생각했습니다.xterm과 rxvt 코드를 확인해보니 조금 더 복잡해 보입니다.

우선 오픈프티가 있는 의사단말기를 열어야 합니다.그래서 man 페이지를 보고 openpty가 마스터와 슬레이브라는 2개의 파일 설명자를 채우는 것을 봅니다.xterm 코드와 rxvt 코드 모두 특수 파일의 시스템 의존성 때문에 지저분합니다.

단말기 탈출 코드에 대한 수많은 정보를 이해합니다.제가 정말 이해할 수 없는 것은 마스터/슬레이브 파일 설명자를 어떻게 해야 합니까?

단말기를 열고, 로그인하고, 셸에서 "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;
}    

질문의 마스터/슬레이브 부분과 관련하여 pty(4) man 페이지(내 시스템에서 열린 pty(3) man 페이지에서 참조):

의사 단말(pseudo terminal)은 캐릭터 장치, 마스터 장치 및 슬레이브 장치의 한 쌍입니다.슬레이브 디바이스는 tty(4)에 기재된 것과 동일한 인터페이스를 프로세스에 제공합니다.그러나, tty(4)에 기재된 인터페이스를 제공하는 다른 모든 디바이스들은 그들 뒤에 일종의 하드웨어 디바이스를 갖는 반면, 슬레이브 디바이스는 대신에 의사 단말의 마스터 절반을 통해 그것을 조작하는 다른 프로세스를 갖습니다.즉, 마스터 디바이스에 기록된 모든 것은 슬레이브 디바이스에 입력으로 제공되고 슬레이브 디바이스에 기록된 모든 것은 마스터 디바이스에 입력으로 제공됩니다.

맨 페이지는 당신의 친구입니다.

저는 방금 이 튜토리얼에서 찾은 예시들을 시도해 보았는데, 그것들은 저에게 매우 잘 작동하고 저는 그것들이 그 문제의 흥미로운 출발점이라고 생각합니다.

EDIT: 튜토리얼에서는 의사 단자 기능에 대해 간단히 설명합니다.설명은 단계적으로 진행되며, 이어서 예문이 나옵니다.

다음 예제에서는 새 의사 터미널을 생성하는 방법을 보여 줍니다. 프로세스를 두 부분으로 분할합니다. 하나는 의사 터미널의 마스터 측에서, 다른 하나는 의사 터미널의 슬레이브 측에서 읽습니다.

#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

언급URL : https://stackoverflow.com/questions/476354/how-do-nix-pseudo-terminals-work-whats-the-master-slave-channel

반응형