3

В течение долгого времени мое понимание термина в unix-подобных системах заключалось в том, что он запускает процесс оболочки и предоставляет ему интерфейс пользователя, связываясь с ним через его stdin, stdout & stderr.

Однако недавно, глядя на проблему с запуском консольного приложения Windows через терминал Cygwin, я понимаю, что это может быть не так просто.

На http://cygwin.com/1.5/cygwin-ug-net/using-effectively.html я вижу,

Другая проблема заключается в получении выходных данных или предоставлении данных для консольных программ Windows. К сожалению, взаимодействие с консольными приложениями Windows - это не просто вопрос использования утилиты перевода. Консольные приложения Windows предназначены для запуска под command.com или cmd.exe, а некоторые не работают в других ситуациях. Cygwin может получать консольный ввод только в том случае, если он также работает в консоли (в окне DOS), поскольку Windows не предоставляет никакого способа подключения к серверной части консольного устройства. Другой традиционный метод ввода / вывода Unix, ptys (псевдо-терминалы), поддерживается Cygwin, но не полностью Windows. Основная проблема заключается в том, что Cygwin pty представляет собой канал, а некоторым приложениям Windows не нравится, когда их ввод или вывод перенаправляются на каналы.

Я написал небольшую программу на C, которую я скомпилировал под windows, используя VC++cl.exe -

#include <stdio.h>

int main(int argc, char *argv[]) {
    #define BUFFER_LEN 1024
    char buffer[BUFFER_LEN];

    printf("echo server started\n"); 
    while (fgets(buffer, BUFFER_LEN, stdin) != NULL) {
        printf("%s", buffer);
    }

    return 0;
}

Когда я запускаю терминал Cygwin (mintty.exe) и запускаю эту программу, я не могу с ней взаимодействовать -

[puneet@freestyle ~]$ /cygdrive/c/echo1.exe
Hello

^ - нет ответа

Но когда я помещаю это в трубу, это работает -

[puneet@freestyle ~]$ echo  -e "1\n2\n3" | /cygdrive/c/echo1.exe | while read line; do echo $line; done
1
2
3
[puneet@freestyle ~]$

По сути, он не взаимодействует с терминалом mintty.exe . Однако при запуске bash.exe непосредственно из консоли Windows, он может корректно взаимодействовать с -

[puneet@freestyle ~]$ /cygdrive/c/echo1.exe
Hello
Hello
^Z
[puneet@freestyle ~]$

Затем я подумал, что если я зайду в ssh и запустлю эту программу как команду, она будет работать, так как тогда терминал не будет напрямую взаимодействовать с ней, а сервер SSH будет. Однако это тоже не работает -

[puneet@freestyle ~]$ ssh freestyle /cygdrive/c/echo1.exe
Hello

^ - нет ответа

Но положить это в трубу снова работает! -

[puneet@freestyle ~]$ echo  -e "1\n2\n3" | ssh freestyle /cygdrive/c/echo1.exe | while read line; do echo $line; done
1
2
3
[puneet@freestyle ~]$

Может ли кто-нибудь объяснить теорию, стоящую за всеми этими наблюдениями?

Является ли взаимодействие между терминалом и оболочкой чем-то большим, чем просто использование оболочки stdin, stdout и stderr?

Чем отличается консоль Windows? Почему консольные программы Windows, кажется, работают нормально, когда находятся в конвейере с программами cygwin?

1 ответ1

2

Если добавить fflush(stdout) в свое while цикла (после printf то ваша программа будет работать , как вы ожидаете, даже внутри mintty. Вы также должны иметь возможность вызывать setbuf(stdout, NULL) в качестве первой операции, которую вы выполняете на stdout, и выполняйте эту работу.

Вы также можете запустить c:\cygwin\bin\bash в окне консоли Windows, и ваша оригинальная программа будет работать так же, как и вы. Я не пробовал, но вы также должны иметь возможность запускать bash в окне conemu или console2, и ваша оригинальная программа будет работать так, как вы ожидаете.

То есть это о мяте.

Вот (часть), что происходит:

Консоль Windows очень особенная. В Windows есть служба, называемая клиент / серверная подсистема выполнения. Когда вы запускаете командную строку в Windows, вы фактически подключаетесь к клиентской / серверной подсистеме времени выполнения, и она создает окно для вас.

Mintty, с другой стороны, является "обычным" окном (в нем, как правило, работает эмулятор терминала).

Процедуры ввода-вывода библиотеки C на самом деле проверяют, работают ли они под окном, созданным подсистемой исполнения клиент / сервер, и изменяют свое поведение, если они есть. Одно из изменений, которое они делают, - отключение полной буферизации для stdout.

Когда вы запускаете с помощью mintty, процедуры получения / размещения в Window думают, что они не запускаются в командной строке, поэтому они выполняют полную буферизацию.

Вот еще один трюк: в bash под mintty вы можете запустить cat | echo1.exe . Затем напечатайте что-нибудь, и когда вы нажмете ^D все эти буферизованные выходные данные stdout появятся сразу.

Причина в том, что кот | Трюк cat | echo1.exe работает в том, что cat - это программа cygwin. Cygwin действительно является библиотекой эмуляции posix. Таким образом, stdin cat эмулируется библиотекой Cygwin, а библиотека Cygwin обрабатывает ^D иначе, чем "настоящий" Windows stdin.

Но если вы просто запускаете echo1.exe в bash под mintty то ваши нажатия клавиш попадают в неэмулируемый файловый поток (т. Е. Ваш стандартный ввод - это стандартный ввод Windows, а не тот, который эмулируется библиотекой Cygwin), поэтому ^D не отправляет ВФ. Вместо этого ^Z будет отправлять eof, но ^Z означает что-то особенное для bash, поэтому оно не отправляется. Скорее вам нужно нажать ^C что немедленно завершает echo1.exe (и не очищает буферы, что правильно).

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .