2015-11-17 3 views
1

Я пишу программу на основе проклятий. Чтобы упростить для меня поиск ошибок в этой программе, я хотел бы создать вывод отладки. Из-за того, что программа уже отображает пользовательский интерфейс на терминале, я не могу разместить там отладочный вывод.Как зарезервировать дескриптор файла?

Вместо этого я планирую написать вывод отладки в дескриптор файла 3 безоговорочно. Вы можете вызвать программу как program 3>/dev/ttyX, а /dev/ttyX - это другой телетайп, чтобы увидеть выход отладки. Когда дескриптор 3 файла не открывается, сбой записи вызовов с EBADF, который я игнорирую, как и все ошибки при записи вывода отладки.

Проблема возникает, когда я открываю другой файл, и никакой запрос отладки не запрашивался (т. Е. Дескриптор файла 3 не был открыт). В этом случае вновь открытый файл может получить файловый дескриптор 3, в результате чего вывод отладки приведет к случайному повреждению файла, который я только что открыл. Это плохо. Как я могу избежать этого? Есть ли способ переносить дескриптор файла как «зарезервированный» или такой?

Вот несколько идей, которые я имел и свои проблемы:

  • Я мог бы открыть /dev/null или временный файл в файл с дескриптором 3 (например, с помощью dup2()), прежде чем открыть любой другой файл. Это работает, но я не уверен, могу ли я считать это всегда успешным, так как открытие /dev/null может не получиться.
  • Я мог проверить, открыт ли дескриптор файла 3 и не выводить вывод отладки, если он не является. Это проблематично, когда я пытаюсь перезапустить программу, вызвав exec, поскольку другой дескриптор файла, возможно, был открыт (и не закрыт) до вызова exec. Я мог бы намеренно закрыть дескриптор файла 3 перед вызовом exec, когда он не был открыт для отладки, но это очень опасно.
+5

Почему это должны быть Fd 3? Почему он должен быть вызван с явным дескриптором из оболочки? Обычным решением является обращение с этим в вашем приложении. Передайте дополнительный вариант, например, --debug-file = FILE в ваше приложение, откройте этот файл и сохраните дескриптор, возвращенный функцией open() внутри, для использования в качестве вывода отладки. – Juliano

+0

@Juliano Потому что это проще, чем реализация дополнительной опции командной строки, а также более простой в использовании (дескриптор файла должен указываться только один раз, а не дважды). – fuz

+1

@FUZxxl * Потому что это проще, чем реализация дополнительной опции командной строки, а также более простой в использовании. * Это, конечно, не так просто. Что произойдет, если что-то еще закончится с 3 в качестве дескриптора файла? –

ответ

1

Вы все еще не уверены, что можете успешно открыть /dev/null, что немного странно, но давайте работать с ним. Вы должны иметь возможность использовать socketpair(), чтобы получить пару FD. Затем вы можете установить конец записи пары без блокировки и dup2. Вы утверждаете, что уже игнорируете ошибки при записи в этот FD, поэтому данные, поступающие в бит-ведро, не будут вас беспокоить. Вы можете, конечно, закрыть другой конец сокета.

+1

Это подход, который мог бы работать. Я беспокоюсь о '/ dev/null', поскольку приложение также должно работать, когда система находится в нерабочем состоянии (например,'/dev' еще не установлен). – fuz

+1

@FUZxxl, На самом деле, играя с системами загрузки, я хорошо помню, что произошли плохие вещи, когда приложения не могли получить доступ к '/ dev/null' (я думаю, это была проблема разрешения), поэтому я сочувствую. – abligh

+1

@FUZxxl Если '/ dev' еще не установлен, как вы планируете использовать оболочку для перенаправления вывода отладки на'/dev/ttyX'? Вы должны уточнить, что точные обстоятельства вашего приложения должны работать в этом вопросе, а не добавлять больше комментариев, но это не будет работать, потому что ... »после каждого ответа. – biziclop

2

В самом начале программы, откройте /dev/null, а затем назначить его в файл с дескриптором 3:

int fd = open ("/dev/null", O_WRONLY); 
dup2(fd, 3); 

Таким образом, дескриптор файла 3 не будут приняты.

Затем, при необходимости, повторно используйте dup2(), чтобы назначить файловый дескриптор 3 на ваш вывод отладки.

+0

Я знаю о 'dup2'. Как это решает мою проблему? – fuz

+0

@FUZxxl Если FD 3 не открыт при запуске, откройте «/ dev/null» и используйте dup2, чтобы убедиться, что он использует FD 3 (если открытый вызов еще не вернулся FD 3) –

+0

@JonasWielicki Это один из подходов I обсудите в вопросе. Это проблематично, потому что я не уверен, могу ли я предположить, что я могу успешно открыть '/ dev/null'. – fuz

1

Не сосредотачивайтесь на конкретном значении дескриптора файла - вы все равно не можете управлять им в переносном режиме. Если вы можете контролировать его вообще. Но вы можете использовать переменные окружения для управления выводом отладки в файл:

int debugFD = getDebugFD(); 

... 

int getDebugFD() 
{ 
    const char *debugFile = getenv("DEBUG_FILE"); 
    if (NULL == debugFile) 
    { 
     return(-1); 
    } 

    int fd = open(debugFile, O_CREAT | O_APPEND | O_WRONLY, 0644); 
    // error checking can be here 

    return(fd); 
} 

Теперь вы можете написать свой вывод отладки debugFD. Я предполагаю, что вы знаете достаточно, чтобы убедиться, что debugFD видно, где вам это нужно, а также как убедиться, что он инициализирован, прежде чем пытаться его использовать.

Если вы не передадите envval DEBUG_FILE, вы получите недопустимый файловый дескриптор, а ваши отладочные вызовы не пройдут - предположительно, молча.

+0

Конечно, вы можете управлять конкретным значением дескриптора файла портативно: просто выполните программу с соответствующим перенаправлением оболочки, например '3> debug_file'. Совершенно портативный, простой и тонкий. – fuz

+0

@FUZxxl * Конечно, вы можете управлять конкретным значением дескриптора файла портативно: просто выполните программу с соответствующим перенаправлением оболочки, например 3> debug_file. Совершенно портативный, простой и тонкий. * Тогда почему вы спросили? Потому что вы не можете контролировать его - если вы не настроите его таким образом, что-то еще будет использовать его - следовательно, вы не ** имеете контроль над файловым дескриптором 3 - вы можете * использовать * его. Вы ** не можете ** * контролировать * это. –

+0

Опять же: если файловый дескриптор 3 открывается, когда запускается моя программа, я хочу написать ему вывод отладки. Если нет, я не хочу этого делать. Семантика, которую я хочу, просты и понятны. – fuz

3

Зачем использовать fd 3? Почему бы не использовать fd 2 (stderr)? У него уже есть четко определенные «Я записываю какие-то виды», то есть всегда (не верно, но достаточно верно ...), и вы можете перенаправить его перед запуском своего двоичного файла, чтобы получить журналы, где вы хотите.

Другим вариантом является регистрация сообщений в syslog с использованием уровня LOG_DEBUG. Это влечет за собой вызов syslog() вместо обычной функции записи, но это просто делает запись более явной.

Простой способ проверки, если STDERR был перенаправлен или по-прежнему указывает на терминале с помощью isatty функции (пример кода ниже):

#include <stdio.h> 
#include <unistd.h> 

int main(void) { 
    if (isatty(2)) { 
    printf("stderr is not redirected.\n"); 
    } else { 
    printf("stderr seems to be redirected.\n"); 
    } 
} 
+0

Прочтите, как работает проклятие. Подсказка: он использует файловый дескриптор 2 для материала TUI, если он доступен. Да, я мог бы бороться с тем, как проклинают работу, но это помешает основной функции и заставит мою программу внезапно вести себя иначе, чем все другие проклятия, что плохо. 'syslog()' является глобальным и не очень подходит для моей ситуации. – fuz

+0

@FUZxxl Последние запрограммированные приложения ncurses, fd2 был совершенно свободен для использования, это было, однако, почти 15 лет назад. Если бы вы включили * почему * вы не смогли использовать fd2, я бы не предложил его.Мое предложение для syslog все еще стоит. – Vatine

+0

Я писал, почему я не мог использовать fd2: он по умолчанию идет в терминал, поэтому мне нужно предоставить флаг для включения отладочного вывода (чего я не хочу), или мне нужно найти другой способ узнать, stderror был перенаправлен или нет (что может быть нелегко сделать правильно). – fuz

Смежные вопросы