2013-12-16 4 views
3

Недавно я читаю CSAPP. В разделе 10.9, он отметил, что стандарт ввода/вывода не должны использоваться с гнездом по причинам, следующим образом:Ограничение стандартного ввода-вывода C и почему мы не можем использовать стандартный ввод-вывод C с разъемами

(1) Ограничения стандартного ввода/вывода

Ограничение 1: Input функции после выходных функций. Функция ввода не может следовать за выходной функцией без промежуточного вызова для fflush, fseek, fsetpos или перемотки назад. Функция fflush освобождает буфер , связанный с потоком. В последних трех функциях функция Unix I/O lseek используется для сброса текущей позиции файла.

Ограничение 2: Функции выхода следующих функций ввода. Функция вывода не может следовать за входной функцией без промежуточного вызова в fseek, fsetpos или перемотке, если только функция ввода не встречает конец файла.

(2) Нельзя использовать функцию lseek на сокете.

Вопрос 1: Что произойдет, если я нарушу ограничение? Я написал фрагмент кода, и он отлично работает.

Вопрос 2: Для того, чтобы ходить по ограничению 2, один подход заключается в следующем:

File *fpin, *fpout; 

fpin = fdopen(sockfd, "r"); 
fpout = fdopen(sockfd, "w"); 

/* Some Work Here */ 

fclose(fpin); 
fclose(fpout); 

В тексте книги, он сказал,

Закрытие уже закрытого дескриптора в резьбовую программа - это рецепт для катастрофы .

Почему?

ответ

4

Ваше обходное решение не работает так, как написано, из-за ошибки двойного замыкания, которую вы указали. Double-close безвреден для однопоточных программ, если нет промежуточных операций, которые могли бы открыть новые файловые дескрипторы (второе закрытие будет просто безобидно с EBADF), но они являются критическими ошибками в многопоточных программах. Рассмотрим этот сценарий:

  • Речь A вызывает close(n).
  • Thread B вызывает open и возвращает n, который хранится как int fd1.
  • Тема A вызывает close(n) еще раз.
  • Thread B вызывает open и снова возвращает n, который хранится как fd2.
  • Thread B теперь пытается записать в fd1 и фактически записывает в файл, открытый вторым вызовом, open вместо первого открытого.

Это может привести к массовому повреждению файла, утечки информации (представьте себе писать пароль для сокета вместо локального файла) и т.д.

Однако проблема легко исправить. Вместо того, чтобы звонить fdopen дважды с тем же файловым дескриптором, просто используйте dup, чтобы скопировать его и передать копию fdopen. Благодаря этому простому исправлению, stdio отлично подходит для сокетов. Это не подходит для использования асинхронного цикла событий, но если вы используете потоки для ввода-вывода, он отлично работает.

Edit: Я думаю, что я пропустил, отвечая на ваш вопрос 1. Что произойдет, если вы нарушаете правила о том, как переключаться между входом и выходом на STDIO потоке неопределенное поведение. Это означает, что тестирование и наблюдение, что оно «работает» не имеет смысла; это может означать либо:

  1. Реализация C вы используете обеспечивает определение (как часть документации) для того, что происходит в этом случае, и это соответствует поведению вы хотели. В этом случае вы можете использовать его, но ваш код не будет переносимым для других реализаций. По этой причине это считается очень плохой практикой. Или,

  2. Вы получили результат, которого вы ожидали случайно, обычно как побочный эффект того, как соответствующие функции реализованы внутри реализации, которую вы используете. В этом случае нет гарантии, что у него нет угловых случаев, которые не будут вести себя так, как вы ожидали, или что они будут продолжать работать одинаково в будущих выпусках и т. Д.

+0

Благодарим вас за подробный объяснение. Но я не совсем понимаю «асинхронное использование цикла событий». – feirainy

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

  • Нет связанных вопросов^_^