Чому я не можу `tail -f / proc / $ pid / fd / 1`?


10

Я написав простий сценарій, який echo-є його PID:

#/bin/bash

while true; do
    echo $$;
    sleep 0.5;
done

Я запускаю вказаний сценарій (він повторюється 3844знову) в одному терміналі і намагаюся tailдескриптор файлу в іншому:

$ tail -f /proc/3844/fd/1

Він нічого не друкує на екран і зависає доти ^c. Чому?

Крім того, всі дескриптори файлів STD (IN / OUT / ERR) посилаються на одні і ті ж бали:

$ ls -l /proc/3844/fd/
total 0
lrwx------ 1 mg mg 64 sie 29 13:42 0 -> /dev/pts/14
lrwx------ 1 mg mg 64 sie 29 13:42 1 -> /dev/pts/14
lrwx------ 1 mg mg 64 sie 29 13:42 2 -> /dev/pts/14
lr-x------ 1 mg mg 64 sie 29 13:42 254 -> /home/user/test.sh
lrwx------ 1 mg mg 64 sie 29 13:42 255 -> /dev/pts/14

Це нормально?

Запуск Ubuntu GNOME 14.04.

Якщо ви думаєте, що це питання належить до SO або SU замість UL, скажіть.


Відповіді:


13

Зробити straceз tail -f, це все пояснює. Цікава частина:

13791 fstat(3, {st_mode=S_IFREG|0644, st_size=139, ...}) = 0
13791 fstatfs(3, {...}) = 0
13791 inotify_init()                    = 4
13791 inotify_add_watch(4, "/path/to/file", IN_MODIFY|IN_ATTRIB|IN_DELETE_SELF|IN_MOVE_SELF) = 1
13791 fstat(3, {st_mode=S_IFREG|0644, st_size=139, ...}) = 0
13791 read(4, 0xd981c0, 26)             = -1 EINTR (Interrupted system call)

Що це робить? Він встановлює inotifyобробник файлу, а потім чекає, поки з цим файлом щось не відбудеться. Якщо ядро ​​скаже tailчерез цей обробник inotify, що файл змінився (як правило, додався), то tail1) шукає 2) читає зміни 3) записує їх на екран.

/proc/3844/fd/1у вашій системі є символічне посилання на /dev/pts/14, що є пристроєм символів. Немає такого, як деякі, як "карта пам'яті", до якої можна було б отримати доступ. Таким чином, немає нічого, чиї зміни можна було б підписати до інотифікованого, оскільки немає диска чи області пам'яті, до якої можна було б отримати доступ.

Цей символьний пристрій - це віртуальний термінал, який практично працює так, ніби це мережевий сокет. Програми, що працюють на цьому віртуальному терміналі, підключаються до цього пристрою (так само, як ніби ви telnet-тед в порт tcp) і записуєте те, що хочуть записати. Є і складніші речі, наприклад, блокування екрана, послідовності управління терміналом і таке, як правило, обробляються ioctl()дзвінками.

Я думаю, ви хочете якось дивитись віртуальний термінал. Це можна зробити на Linux, але це не так просто, йому потрібен певний функціонал мережевого проксі, і трохи хитро використання цих ioctl()дзвінків. Але є інструменти, які можуть це зробити.

В даний час я не можу пригадати, який пакет debian має інструмент для досягнення цієї мети, але з невеликим поглинанням ви могли це знайти, мабуть, легко.

Розширення: як згадується тут @Jajesh (дайте йому +1, якщо ви дали мені), інструмент названий watch.

Розширення №2: Згаданий @kelnos, досить cat /dev/pts/14було також простого . Я спробував це, і так, це спрацювало, але не правильно. Я з цим не експериментував багато, але мені здається, що вихід, що надходить у цей віртуальний термінал, перейшов або до catкоманди, або до його вихідного місця, і ніколи до обох. Але це не точно.


відповідь peterh про tailправильну (інотифікований біт годинника), але він невірний, оскільки робити насправді дуже просто: просто використовувати catзамість цього tail.
kelnos

@kelnos Дякую, я спробую це і продовжую свою відповідь результатами.
петерх

@kelnos catтакож не працює для мене, він висить так само, як і хвіст, і все, що я можу зробити ctrl+c.
cprn

1
Я досі не розумію. Я змінив echo $$до echo $$ >> fooтак що тепер є файл і процес відкриває його і додає до нього кожні 0,5 секунди. Я досі не можу отримати доступ до нього через дескриптор файлу та всі дескриптори файлів у /proc/$pid/fd/(але 254, на які посилається test.shсам сценарій) /dev/pts/14. Як доступ до bash, до якого fooвін пише?
cprn

1
Як не дивно, це, здається, працює лише в деяких ситуаціях. використовуючи сценарій у питанні, він не працює. але якщо я виконую "ехо $$" в оболонці, а потім переношу FD 1 на цей під в іншій оболонці, все, що я набираю в першій оболонці, відлунюється у другій.
kelnos

4

Файли в /dev/ptsне є звичайними файлами, вони є ручками для віртуальних терміналів. ptsПоведінка для читання і письма не є симетричним (тобто, що написано там пізніше можна прочитати з нього, як звичайний файл або ФІФО / труби), але опосередковано процесом , який створив віртуальний термінал: деякі поширені з них xterm або ssh або agetty або екран. Процес контролю зазвичай відправляє натискання клавіш на процеси, які читають ptsфайл, і виводить на екран те, що вони записують на pts.

Таким чином, tail -f /dev/pts/14буде друкувати ключі ви натиснете на термінал , з якого ви почали свій сценарій, і якщо ви повідомлення з'явиться в терміналі.echo meh > /dev/pts/14meh


Ви маєте рацію сказати, що я можу писати на pts-пристрій, але я не можу прочитати з нього. Як і в: tail -f /dev/pts/14не друкує ключі, які я натискаю на цьому терміналі. Хоча це цікава відповідь. Дякую.
cprn

0

Деякий час назад я знайшов свого роду тимчасове рішення , що іноді відповідає на необхідність перевірки того , що час виводиться на STDOUT, припускаючи , що у вас є pidпроцесу , і ви можете оголити очі недружні результати:

sudo strace -p $pid 2>&1 | grep write\(

-2

Я думаю, для цього, а не хвостів, те, що вам потрібно зробити, це перегляд результатів.

$ watch -n2 ls -l /proc/3844/fd/

Сподіваюсь, що це те, що вам потрібно.


3
Ця команда показуватиме список відкритих FDS кожні 2 секунди, а не вміст, що виводиться на stdout.
Ángel

Анхель, правда. Він міг би використовувати годинник з кішкою, щоб побачити результат, за яким дескриптором він хоче стежити. Гадаю, @ peter-horvath дав ідеальне пояснення для питання.
Jayesh

Я знаю watch. Що я намагаюся зробити, це зазирнути на результат уже запущеного процесу, тому watchне допомагає.
cprn
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.