현재 저는 주로 재미있는 프로젝트를 수행 중이며 간단한 터미널 에뮬레이터를 코딩하려고합니다. 지금까지 내가하고 싶은 것은 단지 터미널 명령을 전달하는 "프록시 프로세스"를 갖추는 것입니다. 이것은 지금까지, 내가 아래의 코드로/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;
}
}
}
}
어떤 도움을 주시면 감사하겠습니다. 어떤 일이 벌어 질지 잘 모릅니다.
감사합니다. Forkpty는 whola 문제를 훨씬 쉽게 만듭니다. 보통 나는 분기를 좋아하지 않지만,이 경우에는 기본적으로 최선의 대안이라고 생각합니다. – schoppenhauer