faqs.org.ru

 Главная > Программирование > Программирование в Unix >

FAQ по программированию в Unix

From: "Valentin Nechayev" <netch@segfault.kiev.ua>
Date: Tue, 6 Apr 2004 07:02:39 +0000 (UTC)

RU.UNIX.PROG FAQ
$Id: FAQ.p1,v 1.14 2004/03/25 09:11:45 netch Exp $

================================================================
>Q: А что бы почитать по эхотагу:
A:

1. В электронном виде:
- http://www.faqs.org/faqs/by-newsgroup/comp/comp.unix.programmer.html
- http://unixfaq.ru
- http://opennet.ru
- http://docs.freebsd.org/44doc/psd/20.ipctut/paper.html
- http://docs.freebsd.org/44doc/psd/21.ipc/paper.html
- http://www.ecst.csuchico.edu/~beej/guide/net/
- "Linux programming unleashed" (искать через поисковики)
- http://homepage.mac.com/dbutenhof/Threads/source.html (примеры к книге)
  http://home.earthlink.net/~anneart/family/Threads/source.html (оно же)
- http://www.kegel.com
- "The C10K problem" http://www.kegel.com/c10k.html  (few notes on how to
  configure operating systems and write code to support thousands of clients)
- comp.programming.threads FAQ http://www.lambdacs.com/cpt/FAQ.html
- STL Programmer's Guide http://www.sgi.com/tech/stl/
- И естественно ;-) http://www.gnu.org/software/gcc/onlinedocs/

Есть и масса других источников, все не перечислить;)
Не забывайте про поисковики - они могут найти то что хорошо спрятано;)
Непосредственно в системе есть:
- многочисленные маны. можно читать все подряд;) на многих платформах в
  каждой секции есть intro, имеет смысл начинать с него.
- при наличии GPL софта - документацию в texinfo. Скажи `info' и увидишь
  общий каталог.
- /usr/share/doc

2. В бумажном виде:
- Richard W. Stevens, все книги;)
  В частности, "Advanced programming in UNIX environment",
  "Network programming" (в двух томах), есть русские переводы.
- David Butenhof, "Programming with POSIX threads"
- Робачевский "Операционная система UNIX" (на обложке три слона на черепахе)
- Керниган, Пайк "UNIX - универсальная среда программирования"
  (есть в электронном виде)
- Scott Meyers, Effective STL, Effective C++ & More Effective C++.
  Первая есть в переводе на русский "Эффективное использование STL",
  остальные две бегают в электронном виде где-то в РУНете.


================================================================
>Q: А какая еще есть литература?

A: Можете спросить в RU.BOOKS.COMPUTING


================================================================
>Q: Где стандарты?
A:

1. http://www.unix-systems.org/.
Single Unix Specification версии 3 одновременно является Posix.1-2001 (с
некоторыми отличиями - например, XSI extension обязателен для SUS, но не для
Posix).
Можно читать с сайта (зарегистрировавшись), можно скачать;)

2. http://www.ieee.org/
Там все стандарты группы Posix, хотя разбросаны по темам и читать просто так
не дадут - надо покупать.

Есть еще много групп стандартов: XPG, SVID, стандарты ISO C, ISO C++
и так далее. В большинстве они недоступны бесплатно.

================================================================
>Q: Какие есть IDE (integrated development environments) под Unix?
>   Ну чтобы компилятор, среда редактирования, отладчик и прочее - были все
>   вместе?

A: (Alexey Mahotkin)

UNIX сам по себе является Integrated Development Environment.

В "обычных" IDE есть бинарник-интегратор, который вызывает в лучшем случае
внешние утилиты, а в худшем случае -- свою реализацию каждой функцию из DLL
или прямо зашитую в бинарник.

В UNIX таким бинарником-интегратором является shell (Emacs считается
shell'ом в данном случае).  Для выполнения каждой функции вызываются
специально написанные динамически выполняемые модули, такие как make, cc,
ld, и т. д.

Преимущество в этом такое же, как преимущество математических функций
высшего порядка перед "обычными" функциями.

Например, функция "отслеживать зависимости" чаще всего реализуется с
помощью make, но можно также легко использовать, скажем, cook, или же
переключаться между GNU Make и BSD Make по вкусу.  Точно такая ситуация с
используемыми редактором, компилятором, etc.  Более того, сам по себе shell
является "функцией высшего порядка", и легко может быть заменен.

Кроме того, так как пространство функций практически неограниченно, то IDE
"Unix" обеспечивает также заранее не предусмотренные функции высшего
порядка, например, различную автогенерацию кода, поддержку тестирования и
т. п.

A: (Valentin Nechayev, скомпилировав ответы разных участников)

В принципе, это не Unix way ;) На все варианты IDE все равно не наклепаешь,
поэтому есть более другие средства, из которых можно собрать себе аналог
специфического IDE. Надо понимать, что IDE как таковой не даст, например,
возможность собрать программу на другой платформе - для этого нужны make,
autotools и тому подобные средства, а продукция разных IDE обычно
несовместима с ними.

Краткое перечисление существующих IDE:

vim + ctags + скрипты с vim.sf.net :)
[x]emacs :)
KDevelop (разработка под иксы и Qt)
CodeWarrior
CodeForge
Eclipse
fte :)
Anjuta

Там, где стоят смайлики - это не IDE в привычном виндовом понимании,
а рабочие среды с функциями редактирования текстов, вызова компиляторов,
парсинга ошибок компиляции с выделением проблемных частей кода,
полезных вспомогательных средств (code browsers) и прочая и прочая.
vim и emacs являются первыми двумя широко используемыми и имеющими обширную
community.

================================================================
>Q: а у меня зомби плодятся, в списке процессов <defunct>

A: http://www.faqs.org/faqs/unix-faq/programmer/faq/, и не говорите, что Вас
не предупреждали.
Пример кода там (был раньше) не совсем правилен - см. следующий вопрос.

================================================================
>Q: Дочерние процессы становятся зомби, несмотря на то, что есть
>   обработчик сигнала SIGCHLD, вызывающий wait/waitpid/wait3/wait4.
>   Что не так?

A:

Сигнал SIGCHLD, как и все остальные сигналы основной (не realtime) группы,
может накапливаться так, что обработчик вызывается только один раз
независимо от того, сколько за это время сигнал был послан. В ядре в этом
случае просто ставится флажок, что надо вызвать обработчик; этот флажок
снимается при вызове обработчика. Поэтому на каждый вызов SIGCHLD надо
предполагать, что успело завершиться несколько процессов. Поэтому код
обработчика должен содержать цикл "пока еще есть завершившиеся процессы",
например:

	for(;;) {
		pid = waitpid( -1, &istatus, WNOHANG );
		if( pid == -1 && errno == EINTR )  continue;
		if( pid <= 0 )  break;
		<<учесть этот pid>>
	}

В примере в Unix Programming FAQ до начала 2003 г. был записан одиночный
if, это неправильно.

Ну и проверьте, что обработчик SIGCHLD не заблокирован и действительно
вызывается, а то мало ли чего вы там накрутили в sigaction и sigprocmask ;-|

================================================================
>Q: Как переносимо обеспечить сборку разделяемой библиотеки?

A: Для большинства установок достаточно того, что умеет libtool.
Имеет смысл рассмотреть использование полного комплекта GNU autotools
(см. например "Building a Shared Library" в info automake),
хотя libtool может работать и самостоятельно.


================================================================
>Q: Чем плохо и чем хорошо использовать threads?

A:

Вопрос почти религиозный. У тредов есть и преимущества, и недостатки.
Стивенс рассматривает ряд моделей работы множества процессов и тредов,
рассмотрение можно начать с изучения сделанного ним анализа.

См. также приложение "как писать сервера"

================================================================
>Q: Как писать сервера?

Ответ в приложении.


================================================================
>Q: Пишу демона, компилирую, запускаю - на запускается с диагностикой
>   bind: Can't assign requested address ...
A: (Snar) А вы структуру sockaddr_in перед заполнением нулями забили ? :)
A: (Netch) Вообще-то лучше все структуры заливать нулями перед их заполнением.
    Иногда тако-о-ое вылазит, когда не ждешь...
    Еще пример, кроме sockaddr_in, где обязательна заливка нулями - DBT
    для Berkeley DB версий 2 и выше.


================================================================
>Q: Пишу демона, если демон перезапускается - не может сделать bind()
A: Смотреть в сторону setsockopt с опциями SO_REUSEADDR, SO_REUSEPORT.
SO_REUSEPORT есть не везде.


================================================================
>Q: Хочу ждать в треде одновременно mutex, event и поступление данных в пайп.
A:

Штатных средств нет. Лучше всего сделать свою прослойку, которая будет
для каждого треда держать сигнальный пайп и по, например, освобождению
mutex'а, кидать байтик в передающий конец пайпа той ветки, которой отдается
мьютекс. Есть несколько готовых прослоек такого рода.
Ну а несколько файловых объектов уже штатным образом ожидаются через
select/poll/kqueue.

================================================================
>Q: Чем ловить утечки памяти в программе на C:

A:

http://www.cs.colorado.edu/homes/zorn/public_html/MallocDebug.html (внести
поправку на завышенную оценку dmalloc)

valgrind:
очень мощное средство, только для Linux/x86.

dmalloc:
Ловит только ошибки записи только при проверке heap, обычно
каждая n-я операция malloc/free, но можно вызвать проверку явно.
При включении проверок на каждую операцию изрядно тормозит.
Имеет смысл использовать только для отлова memory-leaks.
Неочевидно конфигурится. Использование без чтения доки (каждый раз заново:))
проблематично..
Работает на всём.

ccmalloc:
Выпадает при неверном обращении к памяти, ищет leak-и.
Функциональность почти как у ElectricFence + dmalloc.
Достаточно удобен. Чтение мануала практически не требуется.
Congif удобен, хорошо откомментирован.
Заявлено что работает в Linux и Solaris, я пробовал только в Linux. (Eugene
Exarevsky)
Появился относительно недавно.

LeakTracer

ElectricFence:
Не умеен ловить утечки памяти.
Фактически всё что умеет - выпасть в кору при обращении к неверной странице.
Не требует конфигурирования.
Достаточно быстро работает.
Практически на всём (я лично пробовал в Sinix, Irix, Linux - Eugene Exarevsky)

memprof

BoundsChecker

mprof

Insure (платный)

dbx (на Sparc)

YAMD:
- Он маленький и в случае чего нетрудно будет разобраться
  где и что не так.
- Нет необходимости в перекмпиляции/линковки программ для вылавливания
  багов при работе с памятью по крайней мере на платформах, где есть
  LD_PRELOAD (ELF).
(Продолжение рассказа про YAMD отдельно)

njamd
довольно нормально работает с тредами. (Andrey Melnikov)


================================================================
>Q: Надо ли проверять код возврата close()? А если вернется -1 и EINTR?

A:

Надо. Проблемы возникают редко, но возникают.
Например, может прерваться происходящий по close() сброс буферов на NFS.
Для fclose() это еще более характерно.
Следует помнить, что не специфицируется, стал ли дескриптор открытым или
остался закрытым после неудачного выполнения close(). В нескольких
широко используемых системах дескриптор вначале отрывается от
процесса и только после этого вызываются операции реального закрытия.
Закрылся ли дескриптор, можно проверить несколькими методами, лучше
всего fcntl(,F_GETFD) на дескриптор (и надеяться, что за это время
другой тред не успел открыть новый объект, который получил этот дескриптор).
Если в файл что-то писалось, и требуется, чтобы факт успешной записи был
подтверждён, используйте fsync() для сброса буферов.

================================================================
>Q: А как бы узнать, нажали ли кнопочку на клавиатуре?

A:

man curses; man curs_getch

Если curses неприменима - man termios


================================================================
>Q: Хочу таймеры. Man что?

A:

sleep
usleep
nanosleep
select
poll
(даже с пустыми наборами дескрипторов или только с нотификаторами для
выхода из ожидания)
alarm
setitimer
eventlib

sleep, alarm - с точностью до секунды, остальные - точнее.

При необходимости точности более нескольких миллисекунд - бросать штатные
средства и искать realtime.


================================================================
>Q: Как писать оконные интерфейсы переносимо между Windows и Unix?
>Какие toolkits для этого можно применять?

A: (Victor Wagner, выдрано из письма на немного другую тему)

Информации о кроссплатформной переносимости тулкитов (возможности работы в
Windows и MacOS) я не привожу специально, так как считаю что писать
кроссплатформные GUI в большинстве случаев вредно. GUI должен быть удобен
пользователю. Пользователям Unix и оболочек дешевых удобно разное. Исключения
бывают, но редко (вертикальные рынки).

A: (с эхи по нитке)

fox-toolkit, FLTK, wxWidgets, Qt, gtk (gtk+)...
http://freshmeat.net/articles/view/928/ - неплохой обзор.

Tk - не уродец ;) (Часто известен как Tcl/Tk, хотя Tcl - язык, а не тулкит)

================================================================
>Q: Включает ли -Wall в gcc ключ -W?

A: Не включает. В документации к 3.2 это хорошо видно, в 2.95 - вскользь.
BTW, начиная с gcc 3.4 -W будет называться -Wextra.

RU.UNIX.PROG FAQ - приложение 1

$Id: FAQ.a1,v 1.6 2003/05/12 19:35:59 netch Exp $

>Q: Как писать сервера?

A: (Lev Walkin, Dmitri Lenev, множественные дополнения, особенно от
    Igor Sysoev и Igor Khasilev)

Возможны следующие варианты:

1. Сервер может использовать однопросессную FSM (Finite State Machine)
архитектуру, используя те или иные методы получения данных о состоянии
сокетов (select, poll, etc).
  Плюсы:
    + Очень эффективный метод с точки зрения CPU.
  Минусы:
    - Не масштабируется с ростом числа процессоров.
    - Серверные FSM, как правило, достаточно сложны и
      требуют тщательного подхода при проектировании.
    - в случае если обработка данных пришедших по соединению
      требует долгой (в частности блокирующей) операции, то на
      время выполнения этой операции невозможно обрабатывать
      другие соединения. Соответственно возникают проблемы с
       задержкой обработки запросов...
      Проблема в том что:
      а) Например в случае ввода вывода на диск, неблокирующий ввод-вывод
         по select/poll не всегда поддерживается...
      б) даже если мы пользуемся другим механизмом не обладающим данным
         недостатком, например kqueue, или aio, то нам все равно может быть
         не доступна напрямую работа с файлом. Ну например есть библиотека
         для работы с СУБД и нет возможности залезть в ее внутренности
         чтобы получить файловые дескрипторы соответствующие соединениям с
         сервером СУБД.
      в) даже если мы имеем полный контроль над вводом выводом то может
         возникать потребность в долгих вычислениях (то есть затык в
         занятости процессора)... Ну можно конечно вручную пытаться
         квантовать работу но это не всегда удобно...

      В принципе все три проблемы можно решить используя для выполнения
      длительных или блокирующих операций slave процессы или нити
      делая их тем самым не блокирующими. В принципе про данный подход
      можно посмотреть здесь: http://www.cs.princeton.edu/~vivek/flash99/

(Dmitri Lenev)
+ По собственному опыту могу сказать что имея скажем проработанную
библиотеку классов писать сервера на FSM достаточно легко...

Примеры реализации:
- innd
- squid (с ufs хранилищем)
- named (с поправкой на протокол UDP для большинства передач)

Пример реализации со slave процессами для блокирующих операций:
- squid с diskd

Пример реализации со slave тредами для блокирующих операций:
- squid с aufs

2. Сервер может использовать несколько процессов, каждый из которых
обслуживает собственного клиента:
  Плюсы:
    + Простая модель данных
    + Скалируется с ростом числа процессоров.
    + Ошибки в одном процессе не приводят к отказу в
    обслуживании остальных клиентов.
  Минусы:
    - Процесс - это достаточно тяжелый объект OS, поэтому
      метод неприменим при большом количестве одновременно
      работающих клиентов (больше нескольких десятков или
      сотен).
    - Несмотря на масштабируемость, модель очень тяжела
      и в среднем гораздо менее эффективна, чем предыдущая.

Примеры реализации:
- Большинство MTA (sendmail, postfix, exim, qmail)
- Традиционные попперы (qpopper, cucipop, popa3d)
- И другие традиционные unix'овые сервисы (telnetd, rlogind, nnrpd,...)
У всех перечисленных выше время жизни процесса - время обслуживания клиента.
- apache 1.*
У apache - процессы форкаются заранее, процесс может жить
неограниченное время.

3. Сервер может использовать небольшое число процессов, каждый из
которых имплементирует FSM (a la пункт 1).
  Плюсы:
    + Если уже имеется система по типу #1, то перевод ее
      на рельсы системы #3 как правило, достаточно простой.
      Это дает возможность сделать систему скалируемой за
      счет очень небольших усилий.
  Минусы:
    - Все равно придется делать полную FSM.

4. Сервер - процесс, использующий нити (threads) для каждого клиента
(сокета).
  Плюсы:
    + Небольшая сложность разработки, похожа на #2.
      Требуется проработка механизмов защиты общих данных.
    + В зависимости от OS, модель может быть и масштабируемой,
      и эффективной (Solaris, HP-UX).
  Минусы:
    - В зависимости от OS, модель может быть как неэффективной (Linux,
      так как нить "весит" почти столько же, сколько и процесс), так и
      не масштабируемой с ростом числа процессоров (FreeBSD
      с user-space threads).
    - (Igor Khasilev) Если планируется обслуживать одновременно большое число
      подключенных клиентов (от тысячи и выше в зависимости от ОС) эта модель
      может оказаться нерабочей по причинам: расход адресного пространства
      на стек для каждой нити, большая нагрузка на планировщик и
      ограничение на общее число нитей в системе (особенно в случае
      1:1 модели). Иногда может спасти экстенсивный путь - переход на
      64-битные платформы.
    - Существенно затрудняется отладка.

Примеры:
- Oops! 1.*
- apache 2.*
- CommunigatePro (исходный код недоступен, но в Usenet можно найти много
  деталей устройства с авторским описанием)

5. Сервер - процесс, использующий небольшое количество нитей, каждая
из которых обслуживает некоторое количество сокетов одновременно.
  Плюсы:
    + На архитектурах с kernel-threads (Linux, Solaris)
      обладает масштабируемостью и очень эффективна.
  Минусы:
    - Требуется разработка FSM по типу #1, плюс разработка
      разграничения доступа к общим данным (#4).
    - Не приносит масштабируемости на некоторых имплементациях
      потоков (FreeBSD), поэтому в целом несколько менее
      эффективна, чем #1.

6. Несколько процессов, каждый из которых поддерживает нескольких
клиентов путем выделения по потоку на клиента или методом #5.
  Плюсы:
    + Система защищена от неустранимых сбоев при
      обработке одного клиента, так как остаются работать
      остальные процессы.
    + Система масштабируется с ростом числа процессоров.

  Минусы:
    - Очевидно, складывается сложность всех перечисленных
      выше методов.
    - Не предоставляет преимуществ перед #3 на одном
      процессоре.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Некоторые методы получения состояния (активности) сокета
(файлового дескриптора):

Плюсы select():
  + Широкая портабельность.
  + Очень эффективен при относительно небольшом числе одновременно
    активных сокетов (передача в ядро и назад по три бита на сокет).

Минусы select():
  - На многих платформах максимальное ограничение на 1024 (иногда
    другое) файловых дескрипторах не обходится без
    перекомпилирования приложения или даже ядра системы (для
    FreeBSD не нужно перекомпилировать ядро, только приложение).
  - При большом количестве неактивных клиентов передача в ядро и
    назад пустого состояния сокета представляет собой сплошные
    накладные расходы.

Плюсы poll():
  + Не содержит имманентного ограничения на максимальное
    количество обслуживаемых сокетов.
  + Достаточно широкая портабельность.

Минусы poll():
  - Менее эффективен, чем select() (так как передает в ядро
    и назад по восемь байт на сокет) (Реализация  в Linux
    использует особенно тормозной алгоритм обхода данных в poll())

Плюсы /dev/poll (Последние Solaris, патчи для Linux):
  + Не имеет ограничения на максимальное количество обслуживаемых
    сокетов.
  + Из-за модели передачи event'ов вместо модели передачи
    состояний, очень эффективен при обслуживании большого количества
    клиентов, только часть из которых очень активна (типичная
    ситуация для web- и другого вида серверов). Состояния неактивных
    сокетов не вызывают расхода ресурсов.

Минусы /dev/poll:
  - Не портабелен.
  - Неадекватно реагирует на закрытие дескриптора, занесённого в список
    контроля и не вынесенного оттуда перед закрытием. Дескриптор остаётся
    в списке (числясь своим номером). Поэтому перед закрытием надо
    обязательно выносить дескриптор из списка.

Ещё про /dev/poll см. http://soldc.sun.com/articles/polling_efficient.html

Плюсы kqueue/kevent (FreeBSD, OpenBSD):
  + Не имеет ограничения на максимальное количество обслуживаемых
    сокетов.
  + Имеет "вкусные фичи", которые позволяют использовать его
    более эффективным образом не только для сокетов, но и для
    объектов другого типа (файлов, процессов).
  + Из-за модели передачи event'ов вместо модели передачи
    состояний, очень эффективен при обслуживании большого количества
    клиентов, только часть из которых очень активна (типичная
    ситуация для web- и другого вида серверов). Состояния неактивных
    сокетов не вызывают расхода ресурсов.
  + (Igor Sysoev) kqueue/kevent эффективнее, чем /dev/poll.
Минусы:
  - Не портабелен.
  - Линус Торвальдс его не любит. (См.
    http://www.uwsg.iu.edu/hypermail/linux/kernel/0010.3/0013.html; впрочем,
    epoll повторяет тот же "silly" триплет)

Плюсы realtime signals (sigtimedwait,
http://www.kegel.com/c10k.html#nb.sigio, Linux 2.4+):
  + Не имеет ограничения на максимальное количество обслуживаемых
    дескрипторов. (Однако, количество сигналов ограничено, и если дескрипторы
    группировать по сигналам, внутри группы придется опрашивать все
    дескрипторы.)

Минусы realtime signals:
  - Есть в слишком малом количестве систем.
  - Очередь сигналов может переполняться. (Linux в этом случае даёт SIGIO,
    что означает необходимость итерирования всех дескрипторов. Но это лучше,
    чем замалчивание переполнения очереди.)
  - Хуже kqueue/kevent - только один сигнал обрабатывается за раз; kevent()
    может принять и передать несколько событий за один вызов.

Плюсы epoll (Linux 2.5.44+):
  + Не имеет ограничения на максимальное количество обслуживаемых
    сокетов.
  + Из-за модели передачи event'ов вместо модели передачи
    состояний, очень эффективен при обслуживании большого количества
    клиентов, только часть из которых очень активна (типичная
    ситуация для web- и другого вида серверов). Состояния неактивных
    сокетов не вызывают расхода ресурсов.
Вообще, epoll похож на kevent. 4-я версия научилась level triggering
в дополнение к edge triggering (что уже умела kqueue/kevent).

Минусы epoll:
  - Не портабелен - только Linux и только после 2.5.44.
  - Слабее по возможностям чем kqueue/kevent (нет наблюдения за процессами,
    файлами, таймерами, завершениями AIO запросов; только одно событие
    на входе и выходе системного вызова).

Еще по epoll см. http://www.uwsg.iu.edu/hypermail/linux/kernel/0210.3/1164.html

Поднятые здесь вопросы также обсуждаются в документе по адресу:
http://www.kegel.com/c10k.html

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Еще стоит посмотреть в сторону D.C. Schmidt'овкого ACE и JAWS,
если не в сторону первого так в сторону последнего как теоретически -
экспериментального исследования...

Вернуться в раздел "Программирование в Unix" - Обсудить эту статью на Форуме
Главная - Поиск по сайту - О проекте - Форум - Обратная связь

© faqs.org.ru