2013-02-10 2 views
0

현재 저는 주로 재미있는 프로젝트를 수행 중이며 간단한 터미널 에뮬레이터를 코딩하려고합니다. 지금까지 내가하고 싶은 것은 단지 터미널 명령을 전달하는 "프록시 프로세스"를 갖추는 것입니다. 이것은 지금까지, 내가 아래의 코드로/usr/bin/nano를 호출했을 때 작동하는 것으로 보인다.이맥스에 쓸 때 프로세스가 때때로 중지됩니다.

그러나 이맥스에서 동일한 작업을 시도 할 때 heisenbug로 실행됩니다. 때로는 잘 작동하지만 대부분 크기 조정은 작동하지 않지만 입력 할 때마다 프로세스가 중단됩니다. Ctrl + z (물론 나는하지 않았다)를 눌렀다.

이맥스의

의 strace가의 순서입니다 : 정지시에 내 프로세스의

ioctl(6, FIONREAD, [0])     = 0 
poll([{fd=7, events=POLLIN}], 1, 0)  = 0 (Timeout) 
read(7, 0x7fff078de1d0, 16)    = -1 EAGAIN (Resource temporarily unavailable) 
select(8, [6 7], NULL, NULL, {100000, 0}) = ? ERESTARTNOHAND (To be restarted) 
--- SIGIO (I/O possible) @ 0 (0) --- 
rt_sigreturn(0x1d)      = -1 EINTR (Interrupted system call) 

의 strace입니다 :

:

rt_sigaction(SIGTSTP, NULL, {SIG_DFL, [], 0}, 8) = 0 
rt_sigaction(SIGTSTP, {0x7f8964468b50, [], SA_RESTORER|SA_RESTART, 0x7f8963ea54a0}, NULL, 8) = 0 
rt_sigaction(SIGINT, NULL, {SIG_DFL, [], 0}, 8) = 0 
rt_sigaction(SIGINT, {0x7f8964468a80, [], SA_RESTORER|SA_RESTART, 0x7f8963ea54a0}, NULL, 8) = 0 
rt_sigaction(SIGTERM, NULL, {SIG_DFL, [], 0}, 8) = 0 
rt_sigaction(SIGTERM, {0x7f8964468a80, [], SA_RESTORER|SA_RESTART, 0x7f8963ea54a0}, NULL, 8) = 0 
rt_sigaction(SIGWINCH, NULL, {0x401088, [WINCH], SA_RESTORER|SA_RESTART, 0x7f8963ea54a0}, 8) = 0 
ioctl(1, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig -icanon -echo ...}) = 0 
ioctl(1, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig -icanon -echo ...}) = 0 
ioctl(1, SNDCTL_TMR_STOP or TCSETSW, {B38400 opost -isig -icanon -echo ...}) = 0 
ioctl(1, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost -isig -icanon -echo ...}) = 0 
select(4, [0 3], [], NULL, NULL)  = 1 (in [0]) 
read(0, 0x7fffafd46890, 4096)   = ? ERESTARTSYS (To be restarted) 
--- SIGTTIN (Stopped (tty input)) @ 0 (0) --- 
--- SIGTTIN (Stopped (tty input)) @ 0 (0) --- 
read(0, 0x7fffafd46890, 4096)   = ? ERESTARTSYS (To be restarted) 
--- SIGTTIN (Stopped (tty input)) @ 0 (0) --- 
--- SIGTTIN (Stopped (tty input)) @ 0 (0) --- 
read(0, 0x7fffafd46890, 4096)   = ? ERESTARTSYS (To be restarted) 
--- SIGTTIN (Stopped (tty input)) @ 0 (0) --- 
--- SIGTTIN (Stopped (tty input)) @ 0 (0) --- 

내 소스 코드, -lutil -lncurses 컴파일

#include <assert.h> 
#include <string.h> 
#include <errno.h> 
#include <stdlib.h> 
#include <stdbool.h> 
#include <spawn.h> 
#include <fcntl.h> 
#include <sys/select.h> 
#include <stdarg.h> 
#include <unistd.h> 
#include <stdio.h> 
#include <malloc.h> 
#include <curses.h> 
#include <sys/ioctl.h> 
#include <signal.h> 


#ifdef __GNUC__ 
# define likely(x)  __builtin_expect((x),1) 
# define unlikely(x)  __builtin_expect((x),0) 
#else 
# define likely(x)  (x) 
# define unlikely(x)  (x) 
#endif 

int mpt; /* master pty */ 
pid_t pid = 0; /* child pid */ 
bool childexit = false; 

int max (int ini, ...) { 
    /* the maximum of positive integers, terminated with -1 */ 
    va_list ap; 
    va_start(ap, ini); 
    int ret = -1; 
    while (ini != -1) { 
ret = (ret < ini) ? ini : ret; 
ini = va_arg(ap, int); 
    } 
    va_end(ap); 
    return ret; 
} 
/* atexit */ 
void close_child (void) { 
    if (! childexit) { 
kill (pid, SIGTERM); 
    } 
} 
void reset_term (void) { 
    endwin(); 
} 
/* signal handlers */ 
void sigwinch (int sig) { 
    (void) sig; 
    struct winsize w; 
    if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1) { 
ioctl(mpt, TIOCSWINSZ, &w); 
    } 
} 
void sigchld (int sig) { 
    (void) sig; 
    //childexit = true; 
    exit(EXIT_SUCCESS); 
} 
void sigusr1 (int sig) { 
    (void) sig; 
    struct winsize w; 
    ioctl(STDIN_FILENO, TIOCGWINSZ, &w); 
    w.ws_row = 25; 
    w.ws_col = 25; 
} 
int main (void) { 
    atexit(close_child); 
    atexit(reset_term); 
    char* spawnedArgs[] = { "/usr/bin/emacs", "-nw", NULL }; 
    char* spawnedEnv[] = { "TERM=xterm", NULL }; 
    size_t slave_len = 128 * sizeof(char); 
    char* slave = malloc (slave_len); 
    if (slave == NULL) { 
fprintf(stderr, "Cannot allocate slave var"); 
exit(EXIT_FAILURE); 
    } 
    mpt = posix_openpt(O_RDWR | O_NOCTTY); 
    if (mpt < 0) { 
perror("Cannot open Master"); 
exit(EXIT_FAILURE); 
    } 
    if (grantpt(mpt) < 0) { 
perror("Cannot grant Terminal"); 
exit(EXIT_FAILURE); 
    } 
    if (unlockpt(mpt) < 0) { 
perror("Cannot unlock Terminal"); 
exit(EXIT_FAILURE); 
    } 
    while ((ptsname_r(mpt, slave, slave_len)) != 0) { 
int e = errno; 
if (e == ERANGE) { 
    slave_len *= 2; 
    slave = realloc(slave, slave_len); 
    if (slave == NULL) { 
    fprintf(stderr, "Cannot reallocate slave var\n"); 
    exit(EXIT_FAILURE); 
    } 
} else { 
    fprintf(stderr, "Cannot get name of terminal: %s\n", strerror(e)); 
    exit(EXIT_FAILURE); 
} 
    } 
    int slavefd = open(slave, O_RDWR | O_NOCTTY); 
    if (slavefd < 0) { 
perror("Cannot open Slave"); 
exit(EXIT_FAILURE); 
    } 
    posix_spawn_file_actions_t action; 
    posix_spawnattr_t attrs; 
    sigset_t set; 
    sigemptyset(&set); 
    posix_spawnattr_setsigmask(&attrs, &set); 
    posix_spawnattr_setflags(&attrs, POSIX_SPAWN_SETSIGMASK); 
    posix_spawn_file_actions_init(&action); 
    posix_spawn_file_actions_adddup2(&action, slavefd, STDOUT_FILENO); 
    posix_spawn_file_actions_adddup2(&action, slavefd, STDIN_FILENO); 
    posix_spawnp(&pid, spawnedArgs[0], &action, &attrs, spawnedArgs, spawnedEnv); 

    fd_set rfds, wfds; 
    int sel, nfds; 

    char mystdin[4096], yourstdout[4096]; 
    int mystdindef = 0, yourstdoutdef = 0; 

    signal(SIGWINCH, sigwinch); 
    signal(SIGCHLD, sigchld); 
    signal(SIGUSR1, sigusr1); 

    initscr(); raw(); noecho(); 

    while (1) { 
    begin_select_loop: 

nfds = 0; 

FD_ZERO(&rfds); 
FD_ZERO(&wfds); 

/* read if buffers are empty, write if buffers are filled */ 

if (mystdindef == 0) { 
    FD_SET(STDIN_FILENO, &rfds); 
    nfds = max(nfds, STDIN_FILENO, -1); 
} else { 
    FD_SET(mpt, &wfds); 
    nfds = max(nfds, mpt, -1); 
} 

if (yourstdoutdef == 0) { 
    FD_SET(mpt, &rfds); 
    nfds = max(nfds, mpt, -1); 
} else { 
    FD_SET(STDOUT_FILENO, &wfds); 
    nfds = max(nfds, STDOUT_FILENO, -1); 
} 

sel = select(1 + nfds, &rfds, &wfds, NULL, NULL); 
if (sel < 0) { 
    int e = errno; 
    if (unlikely(e == EINTR)) { 
    /* A signal has interrupted us. Well, I guess this might be 
     one of the few legit uses of goto, since C99 has no good 
     mechanism for continuations yet. */ 
    goto begin_select_loop; 
    } else { 
    fprintf(stderr, "Error calling select: %s\n", strerror(e)); 
    exit(EXIT_FAILURE); 
    } 
} 

if (FD_ISSET(STDIN_FILENO, &rfds)) { 
    mystdindef = read (STDIN_FILENO, mystdin, sizeof(mystdin)); 
    if (unlikely(mystdindef == -1)) { 
    perror("Reading from stdin"); 
    exit(EXIT_FAILURE); 
    } else if (unlikely(mystdindef == 0)) { 
    /* EOF */ 
    //exit(EXIT_SUCCESS); 
    } 
} 

if (FD_ISSET(mpt, &rfds)) { 
    yourstdoutdef = read (mpt, yourstdout, sizeof(mystdin)); 
    if (unlikely(yourstdoutdef == -1)) { 
    perror("Reading from master"); 
    exit(EXIT_FAILURE); 
    } else if (unlikely(yourstdoutdef == 0)) { 
    /* EOF */ 
    //exit(EXIT_SUCCESS); 
    } 
} 

if (FD_ISSET(STDOUT_FILENO, &wfds)) { 
    int written = write(STDOUT_FILENO, yourstdout, yourstdoutdef); 
    if (unlikely(written == -1)) { 
    perror("Writing to stdout"); 
    exit(EXIT_FAILURE); 
    } else if (unlikely(written < yourstdoutdef)) { 
    memmove(yourstdout, yourstdout + written, yourstdoutdef -= written); 
    } else { 
    yourstdoutdef = 0; 
    } 
} 

if (FD_ISSET(mpt, &wfds)) { 
    int written = write(mpt, mystdin, mystdindef); 
    if (unlikely(written == -1)) { 
    perror("Writing to stdout"); 
    exit(EXIT_FAILURE); 
    } else if (unlikely(written < mystdindef)) { 
    memmove(mystdin, mystdin + written, mystdindef -= written); 
    } else { 
    mystdindef = 0; 
    } 
} 
    } 
} 

어떤 도움을 주시면 감사하겠습니다. 어떤 일이 벌어 질지 잘 모릅니다.

답변

1

을 통해 posix_spawnattr_t 개체를 초기화하지 않으므로 (예상치 못한 특성이 사용 될 수 있음) posix_spawnattr_t 또는 posix_spawn_file_actions_t 개체를 파괴하지 마십시오.

하위 프로세스의 의사 터미널이 표준 오류 (fd 2)가 아니며 제어 터미널도 아닙니다. 따라서, 원래의 tty에 첨부 된 것처럼 부분적으로 동작하고 새로운 tty에는 동작하지 않습니다.

전자는 다른 dup2를 통해 쉽게 해결할 수 있습니다. 후자는 자식 프로세스에서 tcsetsid() 또는 TIOCSCTTY ioctl과 같은 비표준 함수를 호출하여 setsid()을 호출해야합니다. 따라서 posix_spawnp()에서 fork()execve()으로 이동해야합니다.

비표준 함수를 더 이상 사용하지 않으려면 login_tty() 또는 forkpty()을 사용하여 코드를 단순화 할 수 있습니다.

+0

감사합니다. Forkpty는 whola 문제를 훨씬 쉽게 만듭니다. 보통 나는 분기를 좋아하지 않지만,이 경우에는 기본적으로 최선의 대안이라고 생각합니다. – schoppenhauer

관련 문제