diff options
Diffstat (limited to 'documentation/content/ru/books/developers-handbook/tools/_index.adoc')
-rw-r--r-- | documentation/content/ru/books/developers-handbook/tools/_index.adoc | 1428 |
1 files changed, 1428 insertions, 0 deletions
diff --git a/documentation/content/ru/books/developers-handbook/tools/_index.adoc b/documentation/content/ru/books/developers-handbook/tools/_index.adoc new file mode 100644 index 0000000000..83dbc7784c --- /dev/null +++ b/documentation/content/ru/books/developers-handbook/tools/_index.adoc @@ -0,0 +1,1428 @@ +--- +authors: + - + author: 'James Raynard' + - + author: 'Murray Stokely' +description: 'Инструменты разработки' +next: books/developers-handbook/secure +params: + path: /books/developers-handbook/tools/ +prev: books/developers-handbook/introduction +showBookMenu: true +tags: ["tools", "Interpreters", "Compilers", "cc", "make", "Debugging", "lldb", "gdb", "clang", "Emacs"] +title: 'Глава 2. Инструменты разработки' +weight: 3 +--- + +[[tools]] += Инструменты разработки +:doctype: book +:toc: macro +:toclevels: 1 +:icons: font +:sectnums: +:sectnumlevels: 6 +:sectnumoffset: 2 +:partnums: +:source-highlighter: rouge +:experimental: +:c-plus-plus-command: c++ +:clang-plus-plus-command: clang++ +:images-path: books/developers-handbook/ + +ifdef::env-beastie[] +ifdef::backend-html5[] +:imagesdir: ../../../../images/{images-path} +endif::[] +ifndef::book[] +include::shared/authors.adoc[] +include::shared/mirrors.adoc[] +include::shared/releases.adoc[] +include::shared/attributes/attributes-{{% lang %}}.adoc[] +include::shared/{{% lang %}}/teams.adoc[] +include::shared/{{% lang %}}/mailing-lists.adoc[] +include::shared/{{% lang %}}/urls.adoc[] +toc::[] +endif::[] +ifdef::backend-pdf,backend-epub3[] +include::../../../../../shared/asciidoctor.adoc[] +endif::[] +endif::[] + +ifndef::env-beastie[] +toc::[] +include::../../../../../shared/asciidoctor.adoc[] +endif::[] + +[[tools-synopsis]] +== Обзор + +В этой главе представлено введение в использование некоторых инструментов для программирования, поставляемых с FreeBSD, хотя многое из описанного применимо и к другим версиям UNIX(R). Она _не_ претендует на детальное описание процесса написания кода. Большая часть главы предполагает наличие минимальных или отсутствие знаний в программировании, хотя предполагается, что даже опытные программисты найдут в ней что-то полезное. + +[[tools-intro]] +== Введение + +FreeBSD предоставляет отличную среду разработки. Компиляторы для C и C++, а также ассемблер входят в базовую систему, не говоря уже о классических инструментах UNIX(R), таких как `sed` и `awk`. Если этого недостаточно, в коллекции Ports доступно множество других компиляторов и интерпретаторов. В следующем разделе, crossref:tools[tools-programming,Введение в программирование], перечислены некоторые из доступных вариантов. FreeBSD обладает высокой совместимостью со стандартами, такими как POSIX(R) и ANSI C, а также с собственным наследием BSD, что позволяет создавать приложения, которые будут компилироваться и запускаться с минимальными изменениями или без них на широком спектре платформ. + +Однако вся эта мощь может поначалу ошеломить, если вы никогда раньше не писали программы на платформе UNIX(R). Этот документ призван помочь вам начать работу, не углубляясь слишком сильно в более сложные темы. Цель заключается в том, чтобы дать вам достаточно базовых знаний для понимания документации. + +Большая часть документа не требует или почти не требует знаний программирования, хотя предполагает базовые навыки работы с UNIX(R) и готовность учиться! + +[[tools-programming]] +== Введение в программирование + +Программа — это набор инструкций, которые указывают компьютеру выполнять различные действия; иногда выполняемая инструкция зависит от результата предыдущей. В этом разделе представлен обзор двух основных способов передачи таких инструкций, или, как их обычно называют, «команд». Один способ использует _интерпретатор_, другой — _компилятор_. Поскольку человеческие языки слишком сложны для однозначного понимания компьютером, команды обычно записываются на одном из специально разработанных для этого языков. + +=== Интерпретаторы + +С интерпретатором язык поставляется как среда, в которой вы вводите команды в приглашении, и среда выполняет их для вас. Для более сложных программ вы можете ввести команды в файл и заставить интерпретатор загрузить файл и выполнить команды в нём. Если что-то пойдёт не так, многие интерпретаторы переведут вас в отладчик, чтобы помочь найти проблему. + +Преимущество этого подхода в том, что вы сразу видите результаты выполнения команд, а ошибки можно легко исправить. Самый большой недостаток проявляется, когда вы хотите поделиться своими программами с кем-то. У них должен быть такой же интерпретатор, или у вас должен быть способ предоставить его, и они должны понимать, как им пользоваться. Кроме того, пользователям может не понравиться, если они попадут в отладчик при нажатии не той клавиши! С точки зрения производительности интерпретаторы могут потреблять много памяти и обычно генерируют код менее эффективно, чем компиляторы. + +По моему мнению, интерпретируемые языки — это лучший способ начать, если вы раньше не занимались программированием. Такая среда обычно встречается в языках вроде Lisp, Smalltalk, Perl и Basic. Можно также утверждать, что UNIX(R) shell (`sh`, `csh`) сам по себе является интерпретатором, и многие часто пишут shell-«скрипты» для помощи в различных «хозяйственных» задачах на своих машинах. Действительно, часть оригинальной философии UNIX(R) заключалась в предоставлении множества небольших утилит, которые можно было связывать вместе в shell-скриптах для выполнения полезных задач. + +=== Доступные интерпретаторы в FreeBSD + +Вот список интерпретаторов, доступных в Коллекции портов FreeBSD, с кратким обзором некоторых наиболее популярных интерпретируемых языков. + +Инструкции по получению и установке приложений из Коллекции портов можно найти в extref:{handbook}[разделе Порты, ports-using] руководства. + +BASIC:: +Сокращение от Beginner's All-purpose Symbolic Instruction Code. Разработан в 1950-х годах для обучения студентов университетов программированию и поставлялся с каждым уважающим себя персональным компьютером в 1980-х. BASIC — первый язык программирования для многих программистов. Он также является основой для Visual Basic. ++ +Интерпретатор Bywater Basic можно найти в Коллекции портов как package:lang/bwbasic[], а интерпретатор Phil Cockroft's Basic (ранее известный как Rabbit Basic) доступен как package:lang/pbasic[]. + +Lisp:: +Язык, разработанный в конце 1950-х годов как альтернатива популярным в то время языкам для «численных расчётов». В отличие от них, Lisp основан на списках; фактически, название является сокращением от «List Processing» (обработка списков). Он очень популярен в кругах, связанных с ИИ (искусственным интеллектом). ++ +Lisp — это чрезвычайно мощный и сложный язык, но он может показаться довольно большим и громоздким. ++ +В Коллекции портов FreeBSD доступны различные реализации Lisp, которые могут работать в системах UNIX(R). CLISP от Bruno Haible и Michael Stoll доступен как package:lang/clisp[]. Более простая реализация Lisp, SLisp, доступна как package:lang/slisp[]. + +Perl:: +Очень популярен среди системных администраторов для написания скриптов; также часто используется на веб-серверах для написания CGI-скриптов. ++ +Perl доступен в Коллекции портов как package:lang/perl5.36[] для всех выпусков FreeBSD. + +Scheme:: +Диалект Lisp, который более компактен и чист по сравнению с Common Lisp. Популярен в университетах, так как достаточно прост для обучения студентов в качестве первого языка, и при этом обладает достаточным уровнем абстракции для использования в исследовательской работе. ++ +Схема доступна из Коллекции Портов как package:lang/elk[] для Интерпретатора Elk Scheme. Интерпретатор MIT Scheme можно найти в package:lang/mit-scheme[], а Интерпретатор SCM Scheme — в package:lang/scm[]. + +Lua:: +Lua — это легковесный встраиваемый язык сценариев. Он обладает высокой переносимостью и относительно прост. Lua доступен в коллекции портов в пакете package:lang/lua54[]. Он также включен в базовую систему как [.filename]#/usr/libexec/flua# для использования компонентами базовой системы. Стороннее программное обеспечение не должно зависеть от [.filename]#flua#. + +Python:: +Python — это объектно-ориентированный интерпретируемый язык. Его сторонники утверждают, что это один из лучших языков для начала программирования, поскольку он относительно прост в освоении, но не уступает другим популярным интерпретируемым языкам, используемым для разработки крупных и сложных приложений (Perl и Tcl — два других языка, популярных для таких задач). ++ +Последняя версия Python доступна в Коллекции портов в пакете package:lang/python[]. + +Ruby:: +Ruby — это интерпретируемый, чисто объектно-ориентированный язык программирования. Он получил широкую популярность благодаря простому для понимания синтаксису, гибкости при написании кода и возможности легко разрабатывать и поддерживать большие, сложные программы. ++ +Ruby доступен в Коллекции портов как package:lang/ruby32[]. + +Tcl и Tk:: +Tcl — это встраиваемый интерпретируемый язык, который получил широкое распространение и популярность в основном благодаря своей переносимости на множество платформ. Он может использоваться как для быстрого написания небольших прототипов приложений, так и (в сочетании с Tk, набором инструментов для графического интерфейса) полноценных программ с богатым функционалом. ++ +Различные версии Tcl доступны в качестве портов для FreeBSD. Последняя версия, Tcl 8.7, находится в пакете package:lang/tcl87[]. + +=== Компиляторы + +Компиляторы довольно сильно различаются. Прежде всего, вы пишете свой код в файле (или файлах) с помощью редактора. Затем вы запускаете компилятор и проверяете, принимает ли он вашу программу. Если программа не скомпилировалась, стисните зубы и вернитесь к редактору; если же компиляция прошла успешно и программа была создана, вы можете запустить её либо в командной строке оболочки, либо в отладчике, чтобы проверить её работу.footnote:[Если вы запустите её в оболочке, может произойти дамп памяти.] + +Очевидно, это требует больше усилий по сравнению с использованием интерпретатора. Однако это позволяет делать множество вещей, которые очень сложны или даже невозможны с интерпретатором, например, писать код, тесно взаимодействующий с операционной системой — или даже создавать собственную операционную систему! Это также полезно, если требуется написать очень эффективный код, так как компилятор может не спешить и оптимизировать код, что было бы неприемлемо в интерпретаторе. Более того, распространение программы, написанной для компилятора, обычно проще, чем для интерпретатора — можно просто предоставить копию исполняемого файла, предполагая, что у пользователя та же операционная система, что и у вас. + +Поскольку цикл редактирования-компиляции-запуска-отладки довольно утомителен при использовании отдельных программ, многие производители коммерческих компиляторов создали интегрированные среды разработки (сокращённо IDE). FreeBSD не включает IDE в базовую систему, но в Коллекции портов доступен package:devel/kdevelop[], и многие используют для этой цели Emacs. Использование Emacs в качестве IDE обсуждается в crossref:tools[emacs, Использование Emacs как среды разработки]. + +[[tools-compiling]] +== Компиляция с помощью `cc` + +Этот раздел посвящён компилятору clang для языков C и C++, так как он устанавливается вместе с базовой системой FreeBSD. Clang устанавливается как `cc`; пакет GNU-компилятора package:lang/gcc[gcc] доступен в Коллекции портов. Детали создания программы с интерпретатором значительно различаются в зависимости от интерпретатора и обычно хорошо описаны в документации и онлайн-справке интерпретатора. + +Как только вы напишете свой шедевр, следующий шаг — преобразовать его во что-то, что (надеюсь!) будет работать на FreeBSD. Обычно это включает несколько шагов, каждый из которых выполняется отдельной программой. + +[.procedure] +. Обработь исходный код, чтобы удалить комментарии и выполнить другие действия, такие как раскрытие макросов в C. +. Проверить синтаксис вашего кода, чтобы убедиться, что вы соблюдаете правила языка. Если нет, он пожалуется! +. Преобразовать исходный код в ассемблерный язык — это очень близко к машинному коду, но всё ещё понятно человеку. Как утверждается. +. Преобразовать язык ассемблера в машинный код — да, здесь речь идет о битах и байтах, единицах и нулях. +. Проверить, что вы использовали такие элементы, как функции и глобальные переменные, правильно и последовательно. Например, если вы вызвали несуществующую функцию, это будет отмечено. +. Если вы пытаетесь создать исполняемый файл из нескольких исходных файлов, определить, как объединить их все вместе. +. Определить, как создать что-то, что загрузчик времени выполнения системы сможет загрузить в память и запустить. +. Наконец, записать исполняемый файл в файловую систему. + +Слово _компиляция_ часто относится только к шагам с 1 по 4, а остальные шаги называются _линковкой_. Иногда шаг 1 называют _препроцессированием_, а шаги 3-4 — _ассемблированием_. + +К счастью, почти все эти детали скрыты от вас, так как `cc` — это интерфейс, который управляет вызовом всех этих программ с правильными аргументами за вас; достаточно просто набрать + +[source, bash] +.... +% cc foobar.c +.... + +и это вызовет компиляцию файла [.filename]#foobar.c# всеми перечисленными выше шагами. Если у вас несколько файлов для компиляции, просто сделайте что-то вроде + +[source, bash] +.... +% cc foo.c bar.c +.... + +Обратите внимание, что проверка синтаксиса — это всего лишь проверка синтаксиса. Она не выявит логических ошибок, которые вы могли допустить, например, создание бесконечного цикла или использование пузырьковой сортировки вместо бинарной.footnote:[На случай, если вы не знали: бинарная сортировка — это эффективный способ упорядочивания элементов, в отличие от пузырьковой.] + +Существует множество опций для `cc`, все они описаны в руководстве. Вот несколько наиболее важных из них с примерами использования. + +`-o _filename_`:: +Имя выходного файла. Если вы не используете эту опцию, `cc` создаст исполняемый файл с именем [.filename]#a.out#.footnote:[Причины этого кроются в глубинах истории.] ++ +[source, bash] +.... +% cc foobar.c executable is a.out +% cc -o foobar foobar.c executable is foobar +.... + +`-c`:: +Просто скомпилирует файл, не связывая его. Полезно для небольших программ, где нужно только проверить синтаксис, или если вы используете [.filename]#Makefile#. ++ +[source, bash] +.... +% cc -c foobar.c +.... ++ +Это создаст _объектный файл_ (не исполняемый) с именем [.filename]#foobar.o#. Его можно скомпоновать с другими объектными файлами в исполняемый файл. + +`-g`:: +Создать отладочную версию исполняемого файла. Это заставляет компилятор записывать в исполняемый файл информацию о том, какая строка какого исходного файла соответствует какому вызову функции. Отладчик может использовать эту информацию для отображения исходного кода при пошаговом выполнении программы, что _очень_ полезно; недостатком является то, что вся эта дополнительная информация значительно увеличивает размер программы. Обычно вы компилируете с `-g` во время разработки программы, а затем компилируете "релизную версию" без `-g`, когда убедитесь, что она работает правильно. ++ + +[source, bash] +.... +% cc -g foobar.c +.... ++ +Это создаст отладочную версию программы. footnote:[Примечание: мы не использовали флаг -o для указания имени исполняемого файла, поэтому получим исполняемый файл с именем a.out. Создание отладочной версии с именем foobar остается упражнением для читателя!] + +`-O`:: +Создает оптимизированную версию исполняемого файла. Компилятор применяет различные хитрые приёмы, чтобы попытаться создать исполняемый файл, который работает быстрее обычного. Вы можете добавить число после `-O`, чтобы указать более высокий уровень оптимизации, но это часто выявляет ошибки в оптимизаторе компилятора. ++ +[source, bash] +.... +% cc -O -o foobar foobar.c +.... ++ +Это создаст оптимизированную версию [.filename]#foobar#. + +Следующие три флага заставят `cc` проверять, что ваш код соответствует соответствующему международному стандарту, часто называемому стандартом ANSI, хотя строго говоря, это стандарт ISO. + +`-Wall`:: +Включить все предупреждения, которые разработчики `cc` считают полезными. Несмотря на название, это не включит все предупреждения, которые `cc` способен выдавать. + +`-ansi`:: +Отключит большинство, но не все, не-ANSI C функции, предоставляемые `cc`. Несмотря на название, это не гарантирует строгого соответствия вашего кода стандарту. + +`-pedantic`:: +Отключит _все_ не-ANSI C возможности ``cc``. + +Без этих флагов `cc` позволит вам использовать некоторые из своих нестандартных расширений стандарта. Некоторые из них очень полезны, но не будут работать с другими компиляторами — фактически, одна из основных целей стандарта заключается в том, чтобы позволить людям писать код, который будет работать с любым компилятором на любой системе. Это известно как _переносимый код_. + +Обычно следует стремиться к тому, чтобы ваш код был как можно более переносимым, иначе позже вам, возможно, придётся полностью переписать программу для её работы в другом месте — а кто знает, что вы будете использовать через несколько лет? + +[source, bash] +.... +% cc -Wall -ansi -pedantic -o foobar foobar.c +.... + +В результате будет создан исполняемый файл [.filename]#foobar# после проверки [.filename]#foobar.c# на соответствие стандартам. + +`-l__library__`:: +Укажите библиотеку функций, которая будет использоваться во время компоновки. ++ +Наиболее распространённый пример этого — компиляция программы, использующей некоторые математические функции в C. В отличие от большинства других платформ, они находятся в отдельной библиотеке, отличной от стандартной библиотеки C, и необходимо указать компилятору добавить её. ++ +Правило заключается в том, что если библиотека называется [.filename]#libsomething.a#, то вы передаёте `cc` аргумент `-l__something__`. Например, математическая библиотека называется [.filename]#libm.a#, поэтому вы передаёте `cc` аргумент `-lm`. Типичный подводный камень с математической библиотекой заключается в том, что она должна быть последней библиотекой в командной строке. ++ +[source, bash] +.... +% cc -o foobar foobar.c -lm +.... ++ +Это приведёт к подключению функций математической библиотеки в [.filename]#foobar#. ++ +Если вы компилируете код на C++, используйте {c-plus-plus-command}. {c-plus-plus-command} также может быть вызван как {clang-plus-plus-command} в FreeBSD. ++ +[source, bash] +.... +% c++ -o foobar foobar.cc +.... ++ +Это создаст исполняемый файл [.filename]#foobar# из исходного файла на C++ +[.filename]#foobar.cc#. + +=== Распространённые вопросы и проблемы `cc` + +==== Я скомпилировал файл с именем foobar.c и не могу найти исполняемый файл с именем foobar. Куда он пропал? + +Помните, что `cc` вызовет исполняемый файл [.filename]#a.out#, если вы не укажете иное. Используйте опцию `-o _имя_файла_`: + +[source, bash] +.... +% cc -o foobar foobar.c +.... + +==== Хорошо, у меня есть исполняемый файл с именем foobar, я вижу его при выполнении команды ls, но когда я ввожу foobar в командной строке, система сообщает, что такого файла нет. Почему он не может его найти? + +В отличие от MS-DOS(R), UNIX(R) не ищет в текущем каталоге, когда пытается определить, какую программу нужно запустить, если вы явно не укажете это. Введите `./foobar`, что означает "запустить файл с именем [.filename]#foobar# в текущем каталоге." + +=== Я назвал свой исполняемый файл test, но при запуске ничего не происходит. В чем дело? + +Большинство UNIX(R) систем имеют программу под названием `test` в [.filename]#/usr/bin#, и оболочка выбирает её, прежде чем проверить текущий каталог. Введите следующее: + +[source, bash] +.... +% ./test +.... + +или выберите более подходящее название для вашей программы! + +==== Я скомпилировал свою программу, и сначала она работала нормально, но потом произошла ошибка, и было сообщение о core dumped. Что это значит? + +Название _core dump_ восходит к самым ранним дням UNIX(R), когда машины использовали ферритовую память для хранения данных. По сути, если программа завершалась сбоем при определённых условиях, система записывала содержимое ферритовой памяти на диск в файл с именем [.filename]#core#, который программист затем мог изучить, чтобы выяснить причину ошибки. + +==== Увлекательный материал, но что мне теперь делать? + +Используйте отладчик для анализа образа памяти (см. crossref:tools[debugging, Отладка]). + +==== Когда моя программа сбросила core, она сообщила что-то о segmentation fault. Что это? + +Это означает, что ваша программа попыталась выполнить какую-то недопустимую операцию с памятью; UNIX(R) разработана для защиты операционной системы и других программ от некорректно работающих программ. + +Распространенные причины этого: + +* Попытка записи в NULL-указатель, например: ++ +[.programlisting] +.... +char *foo = NULL; +strcpy(foo, "bang!"); +.... + +* Использование неинициализированного указателя, например: ++ +[.programlisting] +.... +char *foo; +strcpy(foo, "bang!"); +.... ++ +Указатель будет иметь случайное значение, которое, возможно, укажет на область памяти, недоступную вашей программе, и ядро завершит вашу программу до того, как она сможет нанести какой-либо ущерб. Если вам не повезет, он укажет внутрь вашей собственной программы и повредит одну из структур данных, что приведет к загадочному сбою программы. +* Попытка доступа за пределы массива, например ++ +[.programlisting] +.... +int bar[20]; +bar[27] = 6; +.... + +* Попытка сохранить что-то в память только для чтения, например ++ +[.programlisting] +.... +char *foo = "My string"; +strcpy(foo, "bang!"); +.... ++ +Версии UNIX(R) компиляторы часто помещают строковые литералы, такие как `"Моя строка"`, в области памяти только для чтения. +* Выполнение нежелательных действий с `malloc()` и `free()`, например ++ +[.programlisting] +.... +char bar[80]; +free(bar); +.... ++ +или ++ +[.programlisting] +.... +char *foo = malloc(27); +free(foo); +free(foo); +.... + +Совершение одной из этих ошибок не всегда приведет к сбою, но это всегда плохая практика. Некоторые системы и компиляторы более терпимы, чем другие, поэтому программы, которые хорошо работают на одной системе, могут аварийно завершаться при попытке запустить их на другой. + +==== Иногда при получении дампа памяти я вижу сообщение ошибки шины (bus error). В моей книге по UNIX(R) сказано, что это означает аппаратную проблему, но компьютер продолжает работать. Это правда? + +Нет, к счастью, нет (если, конечно, у вас действительно нет аппаратной проблемы...). Обычно это означает, что вы обратились к памяти способом, который не следует использовать. + +==== Этот процесс создания дампа памяти звучит довольно полезно, если я могу запускать его по своему желанию. Могу ли я это сделать, или нужно ждать возникновения ошибки? + +Можете. Просто перейдите на другую консоль или xterm, выполните + +[source, bash] +.... +% ps +.... + +чтобы узнать идентификатор процесса вашей программы и выполните + +[source, bash] +.... +% kill -ABRT pid +.... + +где `_pid_` — идентификатор процесса, который вы нашли. + +Это полезно, если ваша программа зависла в бесконечном цикле, например. Если ваша программа перехватывает SIGABRT, есть несколько других сигналов, которые оказывают аналогичный эффект. + +В качестве альтернативы, вы можете создать дамп памяти изнутри вашей программы, вызвав функцию `abort()`. Дополнительную информацию можно найти на man:abort[3]. + +Если вы хотите создать дамп памяти извне вашей программы, но не хотите завершать процесс, вы можете использовать программу `gcore`. Подробнее см. на странице руководства man:gcore[1]. + +[[tools-make]] +== Make + +=== Что такое `make`? + +Когда вы работаете над простой программой с одним или двумя исходными файлами, вводя + +[source, bash] +.... +% cc file1.c file2.c +.... + +это не слишком плохо, но быстро становится очень утомительным, когда есть несколько файлов — и компиляция тоже может занять время. + +Один из способов обойти это — использовать объектные файлы и перекомпилировать исходный файл только в случае изменения исходного кода. Таким образом, у нас может получиться что-то вроде: + +[source, bash] +.... +% cc file1.o file2.o … file37.c … +.... + +если бы мы изменили файл [.filename]#file37.c#, но не трогали остальные с момента последней компиляции. Это может значительно ускорить компиляцию, но не решает проблему с вводом. + +Или мы могли бы написать shell-скрипт для решения проблемы с вводом, но тогда пришлось бы перекомпилировать всё, что сделало бы его очень неэффективным для крупного проекта. + +Что произойдет, если у нас есть сотни исходных файлов? Что, если мы работаем в команде с другими людьми, которые забывают сообщить нам, когда они изменили один из своих исходных файлов, которые мы используем? + +Возможно, мы могли бы объединить два решения и написать что-то вроде shell-скрипта, который содержал бы какое-то волшебное правило, указывающее, когда исходный файл нужно компилировать. Теперь нам осталось только найти программу, которая сможет понимать эти правила, так как для shell это немного слишком сложно. + +Эта программа называется `make`. Она читает файл, называемый _makefile_, который указывает, как различные файлы зависят друг от друга, и определяет, какие файлы нужно перекомпилировать, а какие нет. Например, правило может звучать так: «если [.filename]#fromboz.o# старше, чем [.filename]#fromboz.c#, значит, кто-то изменил [.filename]#fromboz.c#, и его нужно перекомпилировать». В makefile также содержатся правила, указывающие make, _как_ именно перекомпилировать исходный файл, что делает эту программу гораздо более мощным инструментом. + +Файлы Makefile обычно хранятся в том же каталоге, что и исходный код, к которому они применяются, и могут называться [.filename]#makefile#, [.filename]#Makefile# или [.filename]#MAKEFILE#. Большинство программистов используют имя [.filename]#Makefile#, так как это помещает его в начало списка файлов в каталоге, где его легко заметить.footnote:[Они не используют форму MAKEFILE, так как заглавные буквы часто применяются для файлов документации, таких как README.] + +=== Пример использования `make` + +Вот очень простой файл для make: + +[.programlisting] +.... +foo: foo.c + cc -o foo foo.c +.... + +Он состоит из двух строк: строки зависимости и строки создания. + +Строка зависимости здесь состоит из имени программы (известного как _цель_), за которым следует двоеточие, пробел и имя исходного файла. Когда `make` читает эту строку, он проверяет, существует ли файл [.filename]#foo#; если он существует, программа сравнивает время последнего изменения файла [.filename]#foo# с временем последнего изменения файла [.filename]#foo.c#. Если файл [.filename]#foo# не существует или старше файла [.filename]#foo.c#, программа смотрит на строку создания, чтобы выяснить, что делать. Другими словами, это правило для определения, когда файл [.filename]#foo.c# нужно перекомпилировать. + +Строка создания начинается с табуляции (нажмите kbd:[tab]), а затем следует команда, которую вы бы ввели для создания [.filename]#foo#, если бы делали это в командной строке. Если [.filename]#foo# устарел или не существует, `make` выполняет эту команду для его создания. Другими словами, это правило, которое сообщает make, как перекомпилировать [.filename]#foo.c#. + +Таким образом, при вводе команды `make` система обеспечит актуальность файла [.filename]#foo# относительно последних изменений в [.filename]#foo.c#. Этот принцип можно распространить на [.filename]#Makefile#, содержащие сотни целей — фактически, в FreeBSD можно собрать всю операционную систему, просто введя `make buildworld buildkernel` в корневом каталоге дерева исходных кодов (src). + +Еще одно полезное свойство makefile заключается в том, что цели не обязательно должны быть программами. Например, у нас может быть makefile, который выглядит так: + +[.programlisting] +.... +foo: foo.c + cc -o foo foo.c + +install: + cp foo /home/me +.... + +Мы можем указать make, какую цель мы хотим собрать, набрав: + +[source, bash] +.... +% make target +.... + +`make` будет рассматривать только указанную цель и игнорировать все остальные. Например, если мы введём `make foo` с указанным выше makefile, make проигнорирует цель `install`. + +Если мы просто введем `make` без параметров, make всегда будет обращаться к первой цели и затем остановится, не рассматривая остальные. Поэтому если мы введем `make` здесь, он просто перейдет к цели `foo`, перекомпилирует [.filename]#foo# при необходимости и затем остановится, не переходя к цели `install`. + +Обратите внимание, что цель `install` не зависит ни от чего! Это означает, что команда в следующей строке всегда выполняется при попытке создать эту цель с помощью команды `make install`. В данном случае она скопирует [.filename]#foo# в домашний каталог пользователя. Это часто используется в makefile приложений, чтобы приложение можно было установить в правильный каталог после успешной компиляции. + +Это немного запутанная тема для объяснения. Если вы не до конца понимаете, как работает `make`, лучше всего написать простую программу, например, "hello world", и make-файл, как указано выше, и поэкспериментировать. Затем можно перейти к использованию нескольких исходных файлов или добавлению заголовочного файла в исходный код. В этом случае очень полезен `touch` — он изменяет дату файла без необходимости его редактирования. + +=== make и include-файлы + +Код на C часто начинается со списка подключаемых файлов, например stdio.h. Некоторые из этих файлов являются системными, а некоторые принадлежат текущему проекту: + +[.programlisting] +.... +#include <stdio.h> +#include "foo.h" + +int main(.... +.... + +Чтобы убедиться, что этот файл перекомпилируется при изменении [.filename]#foo.h#, необходимо добавить его в [.filename]#Makefile#: + +[.programlisting] +.... +foo: foo.c foo.h +.... + +В момент, когда ваш проект становится больше и у вас появляется все больше собственных включаемых файлов для поддержки, отслеживание всех включаемых файлов и файлов, которые от них зависят, становится проблемой. Если вы измените включаемый файл, но забудете перекомпилировать все файлы, которые от него зависят, последствия будут катастрофическими. У `clang` есть опция для анализа ваших файлов и создания списка включаемых файлов и их зависимостей: `-MM`. + +Если вы добавите это в ваш Makefile: + +[.programlisting] +.... +depend: + cc -E -MM *.c > .depend +.... + +и выполните `make depend`, появится файл [.filename]#.depend# со списком объектных файлов, C-файлов и включаемых файлов: + +[.programlisting] +.... +foo.o: foo.c foo.h +.... + +Если вы измените файл [.filename]#foo.h#, при следующем запуске `make` все файлы, зависящие от [.filename]#foo.h#, будут перекомпилированы. + +Не забудьте выполнить `make depend` каждый раз, когда вы добавляете include-файл в один из своих файлов. + +=== Файлы Makefile системы FreeBSD + +Makefile-ы могут быть довольно сложными для написания. К счастью, в BSD-системах, таких как FreeBSD, есть очень мощные Makefile-ы, поставляемые в составе системы. Отличным примером этого является система портов FreeBSD. Вот основная часть типичного [.filename]#Makefile# для портов: + +[.programlisting] +.... +MASTER_SITES= ftp://freefall.cdrom.com/pub/FreeBSD/LOCAL_PORTS/ +DISTFILES= scheme-microcode+dist-7.3-freebsd.tgz + +.include <bsd.port.mk> +.... + +Теперь, если мы перейдем в каталог этого порта и наберем `make`, произойдет следующее: + +[.procedure] +. Проверяется, есть ли исходный код этого порта уже в системе. +. Если это не так, устанавливается FTP-соединение с URL в MASTER_SITES для загрузки исходного кода. +. Контрольная сумма исходного кода вычисляется и сравнивается с контрольной суммой известной и хорошей копии исходного кода. Это делается для того, чтобы убедиться, что исходный код не был поврежден во время передачи. +. Все необходимые изменения для адаптации исходного кода к работе в FreeBSD применяются — это называется применением _патча_. +. Любая необходимая специальная настройка для исходного кода выполнена. (Многие дистрибутивы программ UNIX(R) пытаются определить, на какой версии UNIX(R) они компилируются и какие дополнительные функции UNIX(R) доступны — именно здесь они получают эту информацию в сценарии портов FreeBSD). +. Компилируется исходный код программы. По сути, мы переходим в каталог, куда были распакованы исходные файлы, и выполняем `make` — собственный make-файл программы содержит необходимую информацию для сборки программы. +. Теперь у нас есть скомпилированная версия программы. При желании мы можем протестировать её сейчас; когда мы уверены в программе, можно ввести `make install`. Это приведёт к копированию программы и всех необходимых вспомогательных файлов в нужные места, а также к добавлению записи в `базу данных пакетов`, чтобы позже можно было легко удалить порт, если мы передумаем. + +Вот теперь, я думаю, вы согласитесь, что это довольно впечатляюще для скрипта из четырёх строк! + +Секрет кроется в последней строке, которая указывает `make` обратиться к системному makefile под названием [.filename]#bsd.port.mk#. Эту строку легко пропустить, но именно здесь начинается вся магия — кто-то написал makefile, который предписывает `make` выполнить все вышеперечисленные действия (плюс несколько других, которые я не упомянул, включая обработку возможных ошибок), и любой может получить доступ к этому функционалу, просто добавив одну строку в свой собственный makefile! + +Если вы хотите взглянуть на эти системные makefile-ы, они находятся в [.filename]#/usr/share/mk#, но, вероятно, лучше подождать, пока у вас не появится немного практики с makefile, так как они очень сложные (и если вы всё же решите их посмотреть, убедитесь, что у вас под рукой есть фляга крепкого кофе!) + +=== Более сложные способы использования `make` + +`Make` — это очень мощный инструмент, способный на гораздо большее, чем показано в простом примере выше. К сожалению, существует несколько различных версий `make`, и все они значительно отличаются друг от друга. Лучший способ узнать, на что они способны, — вероятно, прочитать документацию. Надеюсь, это введение дало вам основу, с которой вы сможете это сделать. В man:make[1] подробно обсуждаются переменные, аргументы и то, как использовать `make`. + +Многие приложения в портах используют GNU make, который имеет очень хороший набор страниц "info". Если вы установили любой из этих портов, GNU make будет автоматически установлен как `gmake`. Он также доступен как отдельный порт и пакет. + +Для просмотра справочных страниц (info) GNU make вам потребуется отредактировать файл [.filename]#dir# в каталоге [.filename]#/usr/local/info#, добавив соответствующую запись. Добавьте строку + +[.programlisting] +.... + * Make: (make). The GNU Make utility. +.... + +в файл. После этого вы можете ввести `info` и затем выбрать [.guimenuitem]#make# из меню (или в Emacs выполнить `C-h i`). + +[[debugging]] +== Отладка + +=== Обзор отладчиков, поставляемых в системе + +Использование отладчика позволяет запускать программу в более контролируемых условиях. Обычно можно выполнять программу построчно, проверять значения переменных, изменять их, указывать отладчику выполнение до определённой точки и затем останавливаться и так далее. Также можно подключиться к уже работающей программе или загрузить core-файл, чтобы исследовать причину аварийного завершения программы. + +Этот раздел представляет собой краткое введение в использование отладчиков и не затрагивает специализированные темы, такие как отладка ядра. Для получения дополнительной информации по этой теме обратитесь к главе crossref:kerneldebug[kerneldebug,Отладка ядра]. + +Стандартный отладчик, поставляемый с FreeBSD, называется `lldb` (LLVM debugger). Поскольку он является частью стандартной установки для данного выпуска, нет необходимости выполнять какие-либо дополнительные действия для его использования. Он обладает хорошей справкой по командам, доступной через команду `help`, а также https://lldb.llvm.org/[руководством и документацией в интернете]. + +[NOTE] +==== +Команда `lldb` также доступна extref:{handbook}ports/[из портов или пакетов, ports-using] как пакет package:devel/llvm[]. +==== + +Другой отладчик, доступный в FreeBSD, называется `gdb` (GNU debugger). В отличие от lldb, он не устанавливается по умолчанию в FreeBSD; для его использования необходимо extref:{handbook}#ports-using/[установить] пакет package:devel/gdb[] из портов или пакетов. Он обладает отличной встроенной справкой, а также набором info-страниц. + +Два отладчика обладают схожим набором функций, поэтому выбор между ними в основном зависит от личных предпочтений. Если вы знакомы только с одним из них, используйте его. Тем, кто не знаком ни с одним или знаком с обоими, но хочет использовать отладчик внутри Emacs, придётся выбрать `gdb`, так как `lldb` не поддерживается Emacs. В остальных случаях попробуйте оба и решите, какой вам больше нравится. + +=== Использование lldb + +==== Запуск lldb + +Запустите lldb, набрав + +[source, bash] +.... +% lldb -- progname +.... + +==== Запуск программы с lldb + +Скомпилируйте программу с `-g`, чтобы максимально использовать возможности `lldb`. Без этого флаг она будет работать, но отображать только имя текущей выполняемой функции вместо исходного кода. Если отображается строка вида: + +[source, bash] +.... +Breakpoint 1: where = temp`main, address = … +.... + +(без указания имени файла исходного кода и номера строки) при установке точки останова это означает, что программа не была скомпилирована с параметром `-g`. + +[TIP] +==== +Большинство команд `lldb` имеют более короткие формы, которые можно использовать вместо полных. Здесь используются полные формы для ясности. +==== + +На строке `lldb` введите `breakpoint set -n main`. Это укажет отладчику не показывать предварительный код настройки в запускаемой программе и остановить выполнение в начале кода программы. Теперь введите `process launch`, чтобы фактически запустить программу — она начнётся с кода настройки, а затем будет остановлена отладчиком при вызове `main()`. + +Для пошагового выполнения программы строка за строкой введите `thread step-over`. Когда программа дойдёт до вызова функции, войдите в неё, набрав `thread step-in`. Оказавшись внутри вызова функции, вернитесь из него с помощью команды `thread step-out` или используйте `up` и `down`, чтобы быстро посмотреть на вызывающий код. + +Вот простой пример того, как найти ошибку в программе с помощью `lldb`. Это наша программа (с умышленной ошибкой): + +[.programlisting] +.... +#include <stdio.h> + +int bazz(int anint); + +main() { + int i; + + printf("This is my program\n"); + bazz(i); + return 0; +} + +int bazz(int anint) { + printf("You gave me %d\n", anint); + return anint; +} +.... + +Эта программа устанавливает значение `i` равным `5` и передает его в функцию `bazz()`, которая выводит переданное число. + +Компиляция и запуск программы отображают + +[source, bash] +.... +% cc -g -o temp temp.c +% ./temp +This is my program +anint = -5360 +.... + +Это не то, что ожидалось! Пора разобраться, что происходит! + +[source, bash] +.... +% lldb -- temp +(lldb) target create "temp" +Current executable set to 'temp' (x86_64). +(lldb) breakpoint set -n main Skip the set-up code +Breakpoint 1: where = temp`main + 15 at temp.c:8:2, address = 0x00000000002012ef lldb puts breakpoint at main() +(lldb) process launch Run as far as main() +Process 9992 launching +Process 9992 launched: '/home/pauamma/tmp/temp' (x86_64) Program starts running + +Process 9992 stopped +* thread #1, name = 'temp', stop reason = breakpoint 1.1 lldb stops at main() + frame #0: 0x00000000002012ef temp`main at temp.c:8:2 + 5 main() { + 6 int i; + 7 +-> 8 printf("This is my program\n"); Indicates the line where it stopped + 9 bazz(i); + 10 return 0; + 11 } +(lldb) thread step-over Go to next line +This is my program Program prints out +Process 9992 stopped +* thread #1, name = 'temp', stop reason = step over + frame #0: 0x0000000000201300 temp`main at temp.c:9:7 + 6 int i; + 7 + 8 printf("This is my program\n"); +-> 9 bazz(i); + 10 return 0; + 11 } + 12 +(lldb) thread step-in step into bazz() +Process 9992 stopped +* thread #1, name = 'temp', stop reason = step in + frame #0: 0x000000000020132b temp`bazz(anint=-5360) at temp.c:14:29 lldb displays stack frame + 11 } + 12 + 13 int bazz(int anint) { +-> 14 printf("You gave me %d\n", anint); + 15 return anint; + 16 } +(lldb) +.... + +Подождите минуту! Как переменная `int` стала равна `-5360`? Разве она не была установлена в `5` в `main()`? Давайте поднимемся к `main()` и посмотрим. + +[source, bash] +.... +(lldb) up Move up call stack +frame #1: 0x000000000020130b temp`main at temp.c:9:2 lldb displays stack frame + 6 int i; + 7 + 8 printf("This is my program\n"); +-> 9 bazz(i); + 10 return 0; + 11 } + 12 +(lldb) frame variable i Show us the value of i +(int) i = -5360 lldb displays -5360 +.... + +О боже! Глядя на код, мы забыли инициализировать i. Мы хотели добавить + +[.programlisting] +.... +... +main() { + int i; + + i = 5; + printf("This is my program\n"); +... +.... + +но мы пропустили строку `i=5;`. Поскольку мы не инициализировали `i`, она содержала любое число, которое оказалось в той области памяти при запуске программы, и в данном случае это оказалось `-5360`. + +[NOTE] +==== +Команда `lldb` отображает стек вызовов каждый раз, когда мы входим в функцию или выходим из неё, даже если мы используем `up` и `down` для перемещения по стеку вызовов. Это показывает имя функции и значения её аргументов, что помогает отслеживать текущее положение и происходящее. (Стек — это область хранения, где программа сохраняет информацию об аргументах, переданных в функции, и о том, куда возвращаться после вызова функции.) +==== + +==== Изучение файла Core с помощью lldb + +Файл core — это, по сути, файл, содержащий полное состояние процесса на момент его аварийного завершения. В «старые добрые времена» программистам приходилось распечатывать шестнадцатеричные дампы файлов core и корпеть над руководствами по машинному коду, но сейчас жизнь стала немного проще. Кстати, в FreeBSD и других системах на базе 4.4BSD файл core называется [.filename]#progname.core#, а не просто [.filename]#core#, чтобы было понятнее, какой программе он принадлежит. + +Для анализа файла core укажите имя файла core в дополнение к самой программе. Вместо обычного запуска `lldb` введите `lldb -c _имя_программы_.core \-- _имя_программы_`. + +Отладчик отобразит что-то вроде этого: + +[source, bash, subs="verbatim,quotes"] +.... +% lldb -c [.filename]#progname.core# -- [.filename]#progname# +(lldb) target create "[.filename]#progname#" --core "[.filename]#progname#.core" +Core file '/home/pauamma/tmp/[.filename]#progname.core#' (x86_64) was loaded. +(lldb) +.... + +В этом случае программа называлась [.filename]#progname#, поэтому файл дампа имеет имя [.filename]#progname.core#. Отладчик не показывает, почему программа завершилась аварийно или где это произошло. Для этого используйте команду `thread backtrace all`. Она также покажет, как была вызвана функция, в которой программа завершилась дампом ядра. + +[source, bash, subs="verbatim,quotes"] +.... +(lldb) thread backtrace all +* thread #1, name = 'progname', stop reason = signal SIGSEGV + * frame #0: 0x0000000000201347 progname`bazz(anint=5) at temp2.c:17:10 + frame #1: 0x0000000000201312 progname`main at temp2.c:10:2 + frame #2: 0x000000000020110f progname`_start(ap=<unavailable>, cleanup=<unavailable>) at crt1.c:76:7 +(lldb) +.... + +`SIGSEGV` указывает, что программа пыталась получить доступ к памяти (обычно выполнить код или прочитать/записать данные) по адресу, который ей не принадлежит, но не предоставляет конкретных деталей. Для этого обратитесь к исходному коду на строке 10 файла temp2.c, в функции `bazz()`. Трассировка также показывает, что в данном случае `bazz()` была вызвана из `main()`. + +==== Подключение к работающей программе с помощью lldb + +Одной из самых замечательных особенностей `lldb` является возможность подключения к уже работающей программе. Конечно, для этого требуются соответствующие разрешения. Распространённая проблема — пошаговое выполнение программы, которая создаёт ответвления, с необходимостью отслеживать дочерний процесс, но отладчик отслеживает только родительский. + +Для этого запустите другой `lldb`, используйте `ps` для поиска идентификатора процесса дочернего процесса и выполните + +[source, bash] +.... +(lldb) process attach -p pid +.... + +в `lldb`, а затем отлаживайте как обычно. + +Для того чтобы это работало правильно, код, который вызывает `fork` для создания дочернего процесса, должен делать что-то вроде следующего (предоставлено из документации `gdb`): + +[.programlisting] +.... +... +if ((pid = fork()) < 0) /* _Always_ check this */ + error(); +else if (pid == 0) { /* child */ + int PauseMode = 1; + + while (PauseMode) + sleep(10); /* Wait until someone attaches to us */ + ... +} else { /* parent */ + ... +.... + +Вот все, что нужно сделать: подключиться к дочернему процессу, установить `PauseMode` в `0` с помощью `expr PauseMode = 0` и дождаться возврата из вызова `sleep()`. + +=== Удаленная отладка с использованием LLDB + +[NOTE] +==== +Описанная функциональность доступна начиная с версии LLDB 12.0.0. Пользователи релизов FreeBSD, содержащих более раннюю версию LLDB, могут воспользоваться снимком из extref:{handbook}[портов или пакетов, ports-using], как package:devel/llvm-devel[]. +==== + +Начиная с LLDB 12.0.0, удалённая отладка поддерживается в FreeBSD. Это означает, что `lldb-server` может быть запущен для отладки программы на одном узле, в то время как интерактивный клиент `lldb` подключается к нему с другого. + +Чтобы запустить новый процесс для удалённой отладки, выполните `lldb-server` на удалённом сервере, набрав + +[source, bash] +.... +% lldb-server g host:port -- progname +.... + +Процесс будет остановлен сразу после запуска, и `lldb-server` будет ожидать подключения клиента. + +Запустите `lldb` локально и введите следующую команду для подключения к удалённому серверу: + +[source, bash] +.... +(lldb) gdb-remote host:port +.... + +`lldb-server` также может присоединиться к работающему процессу. Для этого введите следующее на удалённом сервере: + +[source, bash] +.... +% lldb-server g host:port --attach pid-or-name +.... + +=== Использование gdb + +==== Запуск gdb + +Запустите gdb, набрав + +[source, bash] +.... +% gdb progname +.... + +хотя многие предпочитают запускать его внутри Emacs. Для этого введите: + +[source, bash] +.... + M-x gdb RET progname RET +.... + +Наконец, для тех, кого отпугивает текстовый интерфейс командной строки, существует графический интерфейс (package:devel/xxgdb[]) в Коллекции портов. + +==== Запуск программы под отладчиком gdb + +Скомпилируйте программу с `-g`, чтобы максимально использовать возможности `gdb`. Она будет работать и без этого, но отобразит только имя текущей выполняемой функции вместо исходного кода. Строка вида: + +[source, bash] +.... +... (no debugging symbols found) ... +.... + +когда `gdb` запускается, это означает, что программа не была скомпилирована с опцией `-g`. + +На приглашении `gdb` введите `break main`. Это укажет отладчику пропустить предварительный код настройки в выполняемой программе и остановить выполнение в начале кода программы. Теперь введите `run`, чтобы запустить программу — она начнётся с начала кода настройки, а затем будет остановлена отладчиком при вызове `main()`. + +Для пошагового выполнения программы нажимайте `n`. При вызове функции войдите в неё, нажав `s`. Оказавшись внутри функции, вернитесь из неё, нажав `f`, или используйте `up` и `down` для быстрого просмотра вызывающего кода. + +Вот простой пример того, как найти ошибку в программе с помощью `gdb`. Это наша программа (с умышленной ошибкой): + +[.programlisting] +.... +#include <stdio.h> + +int bazz(int anint); + +main() { + int i; + + printf("This is my program\n"); + bazz(i); + return 0; +} + +int bazz(int anint) { + printf("You gave me %d\n", anint); + return anint; +} +.... + +Эта программа устанавливает значение `i` равным `5` и передает его в функцию `bazz()`, которая выводит переданное число. + +Компиляция и запуск программы отображают + +[source, bash] +.... +% cc -g -o temp temp.c +% ./temp +This is my program +anint = 4231 +.... + +Это было не то, что мы ожидали! Пора разобраться, что происходит! + +[source, bash] +.... +% gdb temp +GDB is free software and you are welcome to distribute copies of it + under certain conditions; type "show copying" to see the conditions. +There is absolutely no warranty for GDB; type "show warranty" for details. +GDB 4.13 (i386-unknown-freebsd), Copyright 1994 Free Software Foundation, Inc. +(gdb) break main Skip the set-up code +Breakpoint 1 at 0x160f: file temp.c, line 9. gdb puts breakpoint at main() +(gdb) run Run as far as main() +Starting program: /home/james/tmp/temp Program starts running + +Breakpoint 1, main () at temp.c:9 gdb stops at main() +(gdb) n Go to next line +This is my program Program prints out +(gdb) s step into bazz() +bazz (anint=4231) at temp.c:17 gdb displays stack frame +(gdb) +.... + +Подождите минуту! Как `int` стал равен `4231`? Разве он не был установлен в `5` в `main()`? Давайте поднимемся к `main()` и посмотрим. + +[source, bash] +.... +(gdb) up Move up call stack +#1 0x1625 in main () at temp.c:11 gdb displays stack frame +(gdb) p i Show us the value of i +$1 = 4231 gdb displays 4231 +.... + +О боже! Глядя на код, мы забыли инициализировать i. Мы хотели добавить + +[.programlisting] +.... +... +main() { + int i; + + i = 5; + printf("This is my program\n"); +... +.... + +но мы пропустили строку `i=5;`. Поскольку мы не инициализировали `i`, она содержала любое число, которое оказалось в той области памяти при запуске программы, и в данном случае это оказалось `4231`. + +[NOTE] +==== +Команда `gdb` отображает стек вызовов каждый раз при входе в функцию или выходе из неё, даже при использовании `up` и `down` для перемещения по стеку вызовов. Это показывает имя функции и значения её аргументов, что помогает отслеживать текущее положение и происходящее. (Стек — это область хранения, где программа сохраняет информацию об аргументах, переданных в функции, и о месте, куда нужно вернуться после вызова функции.) +==== + +==== Изучение файла core с помощью gdb + +Файл core — это, по сути, файл, содержащий полное состояние процесса на момент его аварийного завершения. В «старые добрые времена» программистам приходилось распечатывать шестнадцатеричные дампы файлов core и корпеть над руководствами по машинному коду, но сейчас жизнь стала немного проще. Кстати, в FreeBSD и других системах на базе 4.4BSD файл core называется [.filename]#progname.core#, а не просто [.filename]#core#, чтобы было понятнее, какой программе он принадлежит. + +Для анализа файла core запустите `gdb` обычным способом. Вместо ввода команд `break` или `run` введите + +[source, bash] +.... +(gdb) core progname.core +.... + +Если файл core отсутствует в текущем каталоге, сначала введите `dir /путь/к/core/файлу`. + +Отладчик должен отобразить что-то вроде этого: + +[source, bash, subs="verbatim,quotes"] +.... +% gdb [.filename]#progname# +GDB is free software and you are welcome to distribute copies of it + under certain conditions; type "show copying" to see the conditions. +There is absolutely no warranty for GDB; type "show warranty" for details. +GDB 4.13 (i386-unknown-freebsd), Copyright 1994 Free Software Foundation, Inc. +(gdb) core [.filename]#progname.core# +Core was generated by `[.filename]#progname#'. +Program terminated with signal 11, Segmentation fault. +Cannot access memory at address 0x7020796d. +#0 0x164a in bazz (anint=0x5) at temp.c:17 +(gdb) +.... + +В этом случае программа называлась [.filename]#progname#, поэтому файл дампа памяти называется [.filename]#progname.core#. Мы видим, что программа завершилась аварийно из-за попытки доступа к области памяти, которая ей не доступна, в функции `bazz`. + +Иногда полезно увидеть, как была вызвана функция, поскольку проблема могла возникнуть гораздо выше по стеку вызовов в сложной программе. `bt` заставляет `gdb` вывести трассировку стека вызовов: + +[source, bash] +.... +(gdb) bt +#0 0x164a in bazz (anint=0x5) at temp.c:17 +#1 0xefbfd888 in end () +#2 0x162c in main () at temp.c:11 +(gdb) +.... + +Функция `end()` вызывается при аварийном завершении программы; в данном случае функция `bazz()` была вызвана из `main()`. + +==== Подключение к работающей программе с помощью gdb + +Одной из самых удобных функций `gdb` является возможность подключения к уже запущенной программе. Конечно, для этого требуются соответствующие разрешения. Частой проблемой является пошаговое выполнение программы, которая создает дочерний процесс, когда нужно отслеживать дочерний процесс, но отладчик продолжает отслеживать только родительский. + +Для этого запустите другой `gdb`, используйте `ps` для поиска идентификатора процесса дочернего элемента и выполните + +[source, bash] +.... +(gdb) attach pid +.... + +в `gdb`, а затем отлаживайте как обычно. + +Для того чтобы это работало правильно, код, который вызывает `fork` для создания дочернего процесса, должен делать что-то вроде следующего (предоставлено из документации `gdb`): + +[.programlisting] +.... +... +if ((pid = fork()) < 0) /* _Always_ check this */ + error(); +else if (pid == 0) { /* child */ + int PauseMode = 1; + + while (PauseMode) + sleep(10); /* Wait until someone attaches to us */ + ... +} else { /* parent */ + ... +.... + +Теперь осталось только подключиться к дочернему процессу, установить PauseMode в `0` и дождаться возврата из вызова `sleep()`! + +[[emacs]] +== Использование Emacs в качестве среды разработки + +=== Emacs + +Emacs — это высоконастраиваемый редактор — настолько, что его можно скорее назвать операционной системой, чем редактором! Многие разработчики и системные администраторы действительно проводят практически всё своё время, работая внутри Emacs, выходя из него только для завершения сеанса. + +Невозможно даже кратко описать все, что может делать Emacs, но вот некоторые особенности, которые могут быть интересны разработчикам: + +* Очень мощный редактор, позволяющий выполнять поиск и замену как строк, так и регулярных выражений (шаблонов), переход к началу/концу блока выражения и многое другое. +* Выпадающие меню и встроенная справка. +* Подсветка синтаксиса и форматирование отступов в зависимости от языка. +* Полностью настраиваемый. +* Вы можете компилировать и отлаживать программы из Emacs. +* При ошибке компиляции можно перейти к проблемной строке исходного кода. +* Дружелюбный интерфейс для программы `info`, используемой для чтения гипертекстовой документации GNU, включая документацию по самому Emacs. +* Дружелюбный интерфейс для `gdb`, позволяющий просматривать исходный код во время пошагового выполнения программы. + +И, несомненно, множество других, которые были упущены из виду. + +Emacs можно установить на FreeBSD с помощью пакета package:editors/emacs[]. + +После установки запустите его и выполните `C-h t`, чтобы прочитать руководство по Emacs — это означает, что нужно удерживать kbd:[control], нажать kbd:[h], отпустить kbd:[control], а затем нажать kbd:[t]. (Также можно использовать мышь для выбора [.guimenuitem]#Руководство по Emacs# в меню menu:Help[].) + +Хотя в Emacs и есть меню, стоит изучить сочетания клавиш, так как редактировать что-либо с их помощью гораздо быстрее, чем искать мышку и кликать в нужное место. Кроме того, общаясь с опытными пользователями Emacs, вы часто услышите выражения вроде «`M-x replace-s RET foo RET bar RET`» — полезно понимать, что они значат. Да и вообще, в Emacs столько полезных функций, что все они просто не поместятся на панелях меню. + +К счастью, освоить сочетания клавиш довольно легко, так как они отображаются рядом с пунктами меню. Мой совет — использовать пункты меню для, скажем, открытия файла, пока вы не разберётесь, как это работает, и не почувствуете себя уверенно, а затем попробуйте выполнить `C-x C-f`. Когда освоитесь с этим, переходите к следующей команде меню. + +Если вы не можете вспомнить, что делает определённая комбинация клавиш, выберите [.guimenuitem]#Описание Клавиши# в меню menu:Help[] и введите её — Emacs сообщит, что она делает. Вы также можете использовать пункт меню [.guimenuitem]#Command Apropos#, чтобы найти все команды, содержащие определённое слово, с указанием соответствующих клавишных сочетаний. + +Между прочим, выражение выше означает: удерживайте клавишу kbd:[Meta], нажмите kbd:[x], отпустите клавишу kbd:[Meta], введите `replace-s` (сокращение от `replace-string` — ещё одна особенность Emacs в том, что команды можно сокращать), нажмите клавишу kbd:[return], введите `foo` (строка, которую нужно заменить), нажмите клавишу kbd:[return], введите `bar` (строка, на которую нужно заменить `foo`) и снова нажмите kbd:[return]. Emacs выполнит операцию поиска и замены, которую вы только что запросили. + +Если вам интересно, что такое kbd:[Meta], то это специальная клавиша, которая есть на многих рабочих станциях UNIX(R). К сожалению, на PC её нет, поэтому обычно используется kbd:[alt] (или, если вам не повезло, kbd:[escape]). + +Ах да, чтобы выйти из Emacs, нажмите `C-x C-c` (это значит зажмите клавишу kbd:[control], нажмите kbd:[x], затем kbd:[c] и отпустите kbd:[control]). Если у вас есть несохранённые файлы, Emacs спросит, хотите ли вы их сохранить. (Игнорируйте часть документации, где говорится, что `C-z` — это обычный способ выхода из Emacs — это оставляет Emacs работающим в фоне и полезно только на системах без виртуальных терминалов). + +=== Настройка Emacs + +Emacs делает много замечательных вещей; некоторые из них встроены, некоторые требуют настройки. + +Вместо использования проприетарного языка макросов для конфигурации, Emacs применяет версию Lisp, специально адаптированную для редакторов, известную как Emacs Lisp. Работа с Emacs Lisp может быть весьма полезной, если вы хотите продолжить и изучить что-то вроде Common Lisp. Emacs Lisp обладает многими возможностями Common Lisp, хотя и значительно меньше (и, следовательно, проще для освоения). + +Лучший способ изучить Emacs Lisp — это прочитать онлайн-руководство link:https://www.gnu.org/software/emacs/manual/elisp.html[Emacs Reference]. + +Однако для начала настройки Emacs не обязательно знать Lisp, так как я включил пример файла [.filename]#.emacs#, которого должно быть достаточно для старта. Просто скопируйте его в свой домашний каталог и перезапустите Emacs, если он уже запущен; он прочитает команды из файла и (надеюсь) предоставит вам полезную базовую конфигурацию. + +=== Пример файла [.filename]#.emacs# + +К сожалению, здесь слишком много информации, чтобы объяснять всё подробно; однако есть один или два момента, которые стоит упомянуть. + +* Всё, что начинается с `;`, является комментарием и игнорируется Emacs. +* В первой строке `-*- Emacs-Lisp -*-` нужен для того, чтобы мы могли редактировать сам файл [.filename]#.emacs# в Emacs и использовать все удобные функции для редактирования Emacs Lisp. Обычно Emacs пытается угадать это по имени файла, но может не сделать это правильно для [.filename]#.emacs#. +* Клавиша kbd:[tab] связана с функцией отступа в некоторых режимах, поэтому при нажатии клавиши tab текущая строка кода будет с отступом. Если вы хотите вставить символ табуляции в текст, удерживайте клавишу kbd:[control] во время нажатия kbd:[tab]. +* Этот файл поддерживает подсветку синтаксиса для C, C++, Perl, Lisp и Scheme, определяя язык по имени файла. +* В Emacs уже есть предопределённая функция `next-error`. В окне вывода компиляции это позволяет переходить от одной ошибки компиляции к следующей с помощью `M-n`; мы определяем дополнительную функцию `previous-error`, которая позволяет вернуться к предыдущей ошибке с помощью `M-p`. Самое приятное — сочетание `C-c C-c` откроет исходный файл, в котором произошла ошибка, и перейдёт на соответствующую строку. +* Включаем возможность Emacs работать как сервер, так что если вы заняты чем-то вне Emacs и хотите отредактировать файл, можно просто ввести ++ +[source, bash] +.... +% emacsclient filename +.... ++ +и затем вы можете редактировать файл в вашем Emacs!footnote:[Многие пользователи Emacs устанавливают переменную окружения EDITOR в emacsclient, так что это происходит каждый раз, когда им нужно отредактировать файл.] + +.Пример файла [.filename]#.emacs# +==== +[.programlisting] +.... +;; -*-Emacs-Lisp-*- + +;; This file is designed to be re-evaled; use the variable first-time +;; to avoid any problems with this. +(defvar first-time t + "Flag signifying this is the first time that .emacs has been evaled") + +;; Meta +(global-set-key "\M- " 'set-mark-command) +(global-set-key "\M-\C-h" 'backward-kill-word) +(global-set-key "\M-\C-r" 'query-replace) +(global-set-key "\M-r" 'replace-string) +(global-set-key "\M-g" 'goto-line) +(global-set-key "\M-h" 'help-command) + +;; Function keys +(global-set-key [f1] 'manual-entry) +(global-set-key [f2] 'info) +(global-set-key [f3] 'repeat-complex-command) +(global-set-key [f4] 'advertised-undo) +(global-set-key [f5] 'eval-current-buffer) +(global-set-key [f6] 'buffer-menu) +(global-set-key [f7] 'other-window) +(global-set-key [f8] 'find-file) +(global-set-key [f9] 'save-buffer) +(global-set-key [f10] 'next-error) +(global-set-key [f11] 'compile) +(global-set-key [f12] 'grep) +(global-set-key [C-f1] 'compile) +(global-set-key [C-f2] 'grep) +(global-set-key [C-f3] 'next-error) +(global-set-key [C-f4] 'previous-error) +(global-set-key [C-f5] 'display-faces) +(global-set-key [C-f8] 'dired) +(global-set-key [C-f10] 'kill-compilation) + +;; Keypad bindings +(global-set-key [up] "\C-p") +(global-set-key [down] "\C-n") +(global-set-key [left] "\C-b") +(global-set-key [right] "\C-f") +(global-set-key [home] "\C-a") +(global-set-key [end] "\C-e") +(global-set-key [prior] "\M-v") +(global-set-key [next] "\C-v") +(global-set-key [C-up] "\M-\C-b") +(global-set-key [C-down] "\M-\C-f") +(global-set-key [C-left] "\M-b") +(global-set-key [C-right] "\M-f") +(global-set-key [C-home] "\M-<") +(global-set-key [C-end] "\M->") +(global-set-key [C-prior] "\M-<") +(global-set-key [C-next] "\M->") + +;; Mouse +(global-set-key [mouse-3] 'imenu) + +;; Misc +(global-set-key [C-tab] "\C-q\t") ; Control tab quotes a tab. +(setq backup-by-copying-when-mismatch t) + +;; Treat 'y' or <CR> as yes, 'n' as no. +(fset 'yes-or-no-p 'y-or-n-p) +(define-key query-replace-map [return] 'act) +(define-key query-replace-map [?\C-m] 'act) + +;; Load packages +(require 'desktop) +(require 'tar-mode) + +;; Pretty diff mode +(autoload 'ediff-buffers "ediff" "Intelligent Emacs interface to diff" t) +(autoload 'ediff-files "ediff" "Intelligent Emacs interface to diff" t) +(autoload 'ediff-files-remote "ediff" + "Intelligent Emacs interface to diff") + +(if first-time + (setq auto-mode-alist + (append '(("\\.cpp$" . c++-mode) + ("\\.hpp$" . c++-mode) + ("\\.lsp$" . lisp-mode) + ("\\.scm$" . scheme-mode) + ("\\.pl$" . perl-mode) + ) auto-mode-alist))) + +;; Auto font lock mode +(defvar font-lock-auto-mode-list + (list 'c-mode 'c++-mode 'c++-c-mode 'emacs-lisp-mode 'lisp-mode 'perl-mode 'scheme-mode) + "List of modes to always start in font-lock-mode") + +(defvar font-lock-mode-keyword-alist + '((c++-c-mode . c-font-lock-keywords) + (perl-mode . perl-font-lock-keywords)) + "Associations between modes and keywords") + +(defun font-lock-auto-mode-select () + "Automatically select font-lock-mode if the current major mode is in font-lock-auto-mode-list" + (if (memq major-mode font-lock-auto-mode-list) + (progn + (font-lock-mode t)) + ) + ) + +(global-set-key [M-f1] 'font-lock-fontify-buffer) + +;; New dabbrev stuff +;(require 'new-dabbrev) +(setq dabbrev-always-check-other-buffers t) +(setq dabbrev-abbrev-char-regexp "\\sw\\|\\s_") +(add-hook 'emacs-lisp-mode-hook + '(lambda () + (set (make-local-variable 'dabbrev-case-fold-search) nil) + (set (make-local-variable 'dabbrev-case-replace) nil))) +(add-hook 'c-mode-hook + '(lambda () + (set (make-local-variable 'dabbrev-case-fold-search) nil) + (set (make-local-variable 'dabbrev-case-replace) nil))) +(add-hook 'text-mode-hook + '(lambda () + (set (make-local-variable 'dabbrev-case-fold-search) t) + (set (make-local-variable 'dabbrev-case-replace) t))) + +;; C++ and C mode... +(defun my-c++-mode-hook () + (setq tab-width 4) + (define-key c++-mode-map "\C-m" 'reindent-then-newline-and-indent) + (define-key c++-mode-map "\C-ce" 'c-comment-edit) + (setq c++-auto-hungry-initial-state 'none) + (setq c++-delete-function 'backward-delete-char) + (setq c++-tab-always-indent t) + (setq c-indent-level 4) + (setq c-continued-statement-offset 4) + (setq c++-empty-arglist-indent 4)) + +(defun my-c-mode-hook () + (setq tab-width 4) + (define-key c-mode-map "\C-m" 'reindent-then-newline-and-indent) + (define-key c-mode-map "\C-ce" 'c-comment-edit) + (setq c-auto-hungry-initial-state 'none) + (setq c-delete-function 'backward-delete-char) + (setq c-tab-always-indent t) +;; BSD-ish indentation style + (setq c-indent-level 4) + (setq c-continued-statement-offset 4) + (setq c-brace-offset -4) + (setq c-argdecl-indent 0) + (setq c-label-offset -4)) + +;; Perl mode +(defun my-perl-mode-hook () + (setq tab-width 4) + (define-key c++-mode-map "\C-m" 'reindent-then-newline-and-indent) + (setq perl-indent-level 4) + (setq perl-continued-statement-offset 4)) + +;; Scheme mode... +(defun my-scheme-mode-hook () + (define-key scheme-mode-map "\C-m" 'reindent-then-newline-and-indent)) + +;; Emacs-Lisp mode... +(defun my-lisp-mode-hook () + (define-key lisp-mode-map "\C-m" 'reindent-then-newline-and-indent) + (define-key lisp-mode-map "\C-i" 'lisp-indent-line) + (define-key lisp-mode-map "\C-j" 'eval-print-last-sexp)) + +;; Add all of the hooks... +(add-hook 'c++-mode-hook 'my-c++-mode-hook) +(add-hook 'c-mode-hook 'my-c-mode-hook) +(add-hook 'scheme-mode-hook 'my-scheme-mode-hook) +(add-hook 'emacs-lisp-mode-hook 'my-lisp-mode-hook) +(add-hook 'lisp-mode-hook 'my-lisp-mode-hook) +(add-hook 'perl-mode-hook 'my-perl-mode-hook) + +;; Complement to next-error +(defun previous-error (n) + "Visit previous compilation error message and corresponding source code." + (interactive "p") + (next-error (- n))) + +;; Misc... +(transient-mark-mode 1) +(setq mark-even-if-inactive t) +(setq visible-bell nil) +(setq next-line-add-newlines nil) +(setq compile-command "make") +(setq suggest-key-bindings nil) +(put 'eval-expression 'disabled nil) +(put 'narrow-to-region 'disabled nil) +(put 'set-goal-column 'disabled nil) +(if (>= emacs-major-version 21) + (setq show-trailing-whitespace t)) + +;; Elisp archive searching +(autoload 'format-lisp-code-directory "lispdir" nil t) +(autoload 'lisp-dir-apropos "lispdir" nil t) +(autoload 'lisp-dir-retrieve "lispdir" nil t) +(autoload 'lisp-dir-verify "lispdir" nil t) + +;; Font lock mode +(defun my-make-face (face color &optional bold) + "Create a face from a color and optionally make it bold" + (make-face face) + (copy-face 'default face) + (set-face-foreground face color) + (if bold (make-face-bold face)) + ) + +(if (eq window-system 'x) + (progn + (my-make-face 'blue "blue") + (my-make-face 'red "red") + (my-make-face 'green "dark green") + (setq font-lock-comment-face 'blue) + (setq font-lock-string-face 'bold) + (setq font-lock-type-face 'bold) + (setq font-lock-keyword-face 'bold) + (setq font-lock-function-name-face 'red) + (setq font-lock-doc-string-face 'green) + (add-hook 'find-file-hooks 'font-lock-auto-mode-select) + + (setq baud-rate 1000000) + (global-set-key "\C-cmm" 'menu-bar-mode) + (global-set-key "\C-cms" 'scroll-bar-mode) + (global-set-key [backspace] 'backward-delete-char) + ; (global-set-key [delete] 'delete-char) + (standard-display-european t) + (load-library "iso-transl"))) + +;; X11 or PC using direct screen writes +(if window-system + (progn + ;; (global-set-key [M-f1] 'hilit-repaint-command) + ;; (global-set-key [M-f2] [?\C-u M-f1]) + (setq hilit-mode-enable-list + '(not text-mode c-mode c++-mode emacs-lisp-mode lisp-mode + scheme-mode) + hilit-auto-highlight nil + hilit-auto-rehighlight 'visible + hilit-inhibit-hooks nil + hilit-inhibit-rebinding t) + (require 'hilit19) + (require 'paren)) + (setq baud-rate 2400) ; For slow serial connections + ) + +;; TTY type terminal +(if (and (not window-system) + (not (equal system-type 'ms-dos))) + (progn + (if first-time + (progn + (keyboard-translate ?\C-h ?\C-?) + (keyboard-translate ?\C-? ?\C-h))))) + +;; Under UNIX +(if (not (equal system-type 'ms-dos)) + (progn + (if first-time + (server-start)))) + +;; Add any face changes here +(add-hook 'term-setup-hook 'my-term-setup-hook) +(defun my-term-setup-hook () + (if (eq window-system 'pc) + (progn +;; (set-face-background 'default "red") + ))) + +;; Restore the "desktop" - do this as late as possible +(if first-time + (progn + (desktop-load-default) + (desktop-read))) + +;; Indicate that this file has been read at least once +(setq first-time nil) + +;; No need to debug anything now + +(setq debug-on-error nil) + +;; All done +(message "All done, %s%s" (user-login-name) ".") +.... +==== + +=== Расширение списка языков, понимаемых Emacs + +Вот, это все хорошо, если вы хотите программировать только на языках, уже предусмотренных в [.filename]#.emacs# (C, C++, Perl, Lisp и Scheme), но что произойдет, если появится новый язык под названием "whizbang", полный захватывающих возможностей? + +Первое, что нужно сделать, — это выяснить, поставляются ли с whizbang какие-либо файлы, сообщающие Emacs о языке. Обычно они заканчиваются на [.filename]#.el#, что означает "Emacs Lisp". Например, если whizbang является портом FreeBSD, мы можем найти эти файлы, выполнив + +[source, bash] +.... +% find /usr/ports/lang/whizbang -name "*.el" -print +.... + +и установите их, скопировав в каталог Emacs, где находятся файлы Lisp (site Lisp). В FreeBSD это [.filename]#/usr/local/share/emacs/site-lisp#. + +Вот пример, если вывод команды find был + +[source, bash] +.... +/usr/ports/lang/whizbang/work/misc/whizbang.el +.... + +мы бы сделали + +[source, bash] +.... +# cp /usr/ports/lang/whizbang/work/misc/whizbang.el /usr/local/share/emacs/site-lisp +.... + +Далее нам нужно решить, какое расширение имеют исходные файлы whizbang. Допустим, для примера, что все они заканчиваются на [.filename]#.wiz#. Нам необходимо добавить запись в наш [.filename]#.emacs#, чтобы убедиться, что Emacs сможет использовать информацию из [.filename]#whizbang.el#. + +Найдите запись auto-mode-alist в файле [.filename]#.emacs# и добавьте строку для whizbang, например: + +[.programlisting] +.... +... +("\\.lsp$" . lisp-mode) +("\\.wiz$" . whizbang-mode) +("\\.scm$" . scheme-mode) +... +.... + +Это означает, что Emacs автоматически перейдёт в режим `whizbang-mode` при редактировании файла с расширением [.filename]#.wiz#. + +Непосредственно ниже вы найдете запись font-lock-auto-mode-list. Добавьте `whizbang-mode` в нее следующим образом: + +[.programlisting] +.... +;; Auto font lock mode +(defvar font-lock-auto-mode-list + (list 'c-mode 'c++-mode 'c++-c-mode 'emacs-lisp-mode 'whizbang-mode 'lisp-mode 'perl-mode 'scheme-mode) + "List of modes to always start in font-lock-mode") +.... + +Это означает, что Emacs всегда будет включать `font-lock-mode` (т.е. подсветку синтаксиса) при редактировании файла [.filename]#.wiz#. + +И это всё, что требуется. Если вам нужно, чтобы что-то ещё выполнялось автоматически при открытии [.filename]#.wiz#, вы можете добавить `whizbang-mode hook` (см. `my-scheme-mode-hook` для простого примера, который добавляет `auto-indent`). + +[[tools-reading]] +== Для дальнейшего ознакомления + +Для получения информации о настройке среды разработки для внесения исправлений в саму FreeBSD см. man:development[7]. + +* Brian Harvey and Matthew Wright _Simply Scheme_ MIT 1994. ISBN 0-262-08226-8 +* Randall Schwartz _Learning Perl_ O'Reilly 1993 ISBN 1-56592-042-2 +* Patrick Henry Winston and Berthold Klaus Paul Horn _Lisp (3rd Edition)_ Addison-Wesley 1989 ISBN 0-201-08319-1 +* Brian W. Kernighan and Rob Pike _The Unix Programming Environment_ Prentice-Hall 1984 ISBN 0-13-937681-X +* Brian W. Kernighan and Dennis M. Ritchie _The C Programming Language (2nd Edition)_ Prentice-Hall 1988 ISBN 0-13-110362-8 +* Bjarne Stroustrup _The C++ Programming Language_ Addison-Wesley 1991 ISBN 0-201-53992-6 +* W. Richard Stevens _Advanced Programming in the Unix Environment_ Addison-Wesley 1992 ISBN 0-201-56317-7 +* W. Richard Stevens _Unix Network Programming_ Prentice-Hall 1990 ISBN 0-13-949876-1 |