А.П. Строляров, И.Г. Головин, И.А. Волкова - Операционная система Unix (1114677), страница 10
Текст из файла (страница 10)
При этом выдается сообщениеSegmentation fault (core dumped)Это означает, что в текущей директории аварийно завершенного процесса создан файл с именем core (или prog.core, где prog – имя вашейпрограммы), в который система записала содержимое сегмента данныхи стека программы на момент ее аварийного завершения.С помощью отладчика gdb можно проанализировать этот файл,узнав, в частности, при выполнении какой строки программы произошла авария, откуда и с какими параметрами была вызвана функция,содержащая эту строку, каковы были значения переменных на моментаварии и т.д.Чтобы запустить отладчик в режиме анализа core-файла, необходимо дать команду:gdb prog prog.coreгде prog – имя исполняемого файла вашей программы, а prog.core –имя созданного системой core-файла17 .
Очень важно при этом, чтобыв качестве исполняемого файла выступал именно тот файл, при исполнении которого получен core-файл. Так, если уже после полученияcore-файла вы перекомпилируете свою программу, анализ core-файла,скорее всего, приведет к ошибочным результатам.Сразу после запуска отладчика в режиме анализа core-файла рекомендуется дать команду backtrace (или просто bt).4.2.3Анализ причин зацикливанияЕсли ваш процесс зациклился, не торопитесь его убивать. С помощью отладчика можно понять, какой фрагмент кода выполняется в настоящий момент, и проанализировать причины зацикливания. Для этого нужно присоединить отладчик к существующему процессу.
Определите номер процесса с помощью команды ps. Допустим, имя исполняемого файла вашей программы – prog, и она выполняется как процессномер 12345. Тогда команда запуска отладчика должна выглядеть так:17 Подобное имя core-файла характерно для ОС FreeBSD. В ОС Linux файл, скореевсего, будет называться просто core.52gdb prog 12345При подключении отладчика выполнение программы будет приостановлено, однако вы сможете при необходимости продолжить его командойcont.
В случае повторного зацикливания можно приказать отладчикувновь приостановить выполнение нажатием комбинации Ctrl-C.4.3Утилита makeПри сборке ваших программ могут оказаться полезны возможности,предоставляемые утилитой make. Кратко говоря, эта утилита позволяет автоматически строить одни файлы на основании других (например,исполняемые файлы на основании исходных текстов программы) в соответствии с заданными правилами. При этом make отслеживает датыпоследней модификации файлов и производит перестроение только техцелевых файлов, для которых исходные файлы претерпели изменения.Существует несколько различных версий утилиты make.Изложенное в данном параграфе соответствует варианту GnuMake; именно этот вариант используется в большинстве дистрибутивов Linux. При работе с ОС FreeBSD для вызова GnuMake необходимо использовать команду gmake вместо make.
Вслучае, если в системе такой команды нет, обратитесь к системномуадминистратору с просьбой её установить.Правила для утилиты make задаются в файле Makefile, которыйутилита ищет в текущей директории.4.3.1Простейший MakefileДопустим, ваша программа состоит из главного модуля main.c,содержащего функцию main(), а также из дополнительных модулейmod1.c и mod2.c, имеющих заголовочные файлы mod1.h и mod2.h. Соответственно, для сборки исполняемого файла (назовем его prog) необходимо дать следующие команды:$ gcc -g -Wall -c mod1.c -o mod1.o$ gcc -g -Wall -c mod2.c -o mod2.o$ gcc -g -Wall main.c mod1.o mod2.o -o progПервые две команды даются для компиляции дополнительных модулей.
Полученные в результате файлы mod1.o и mod2.o используются втретьей команде для сборки исполняемого файла.53Допустим, мы уже произвели компиляцию программы, после чеговнесли изменения в файл mod1.c и хотим получить исполняемый файл сучетом внесенных изменений. При этом нам надо будет дать только двекоманды (первую и третью), поскольку перекомпиляции модуля mod2не требуется.Чтобы подобные ситуации отслеживались автоматически, мы можемиспользовать утилиту make. Для этого напишем следующий Makefile:mod1.o: mod1.c mod1.hgcc -g -Wall -c mod1.c -o mod1.omod2.o: mod2.c mod2.hgcc -g -Wall -c mod2.c -o mod2.oprog: main.c mod1.o mod2.ogcc -g -Wall main.c mod1.o mod2.o -o progПоясним, что файл состоит из так называемых целей (в нашем случае таких целей три - mod1.o, mod2.o и prog).
Описание каждой целисостоит из заголовка и списка команд. Заголовок цели – это одна строка, начинающаяся всегда с первой позиции (т.е. перед ней не допускаются пробелы и т.п.). В начале строки пишется имя цели (обычно этопросто имя файла, который необходимо построить). Оставшаяся частьзаголовка отделяется от имени цели двоеточием. После двоеточия перечисляется, от каких файлов (или, в более общем случае, от каких целей)зависит построение файла. В данном случае мы указали, что модули зависят от их исходных текстов и заголовочных файлов, а исполняемыйфайл – от основного исходного файла и от двух объектных файлов.После строки заголовка идет список команд (в нашем случае всетри списка имеют по одной команде). Строка команды всегда начинается с символа табуляции, причем замена табуляции пробеламинедопустима и ведет к ошибке. Утилита make считает признаком концасписка команд первую строку, начинающуюся с символа, отличного оттабуляции.Имея в текущей директории вышеописанный Makefile, мы можемдля сборки нашей программы дать команду make prog.4.3.2ПеременныеВ предыдущем параграфе описан Makefile, в котором можно обнаружить несколько повторяющихся фрагментов.
Так, строка параметров54компилятора “-g -Wall” встречается во всех трех целях. Помимо необходимости повторения одного и того же текста, мы можем столкнутьсяс проблемами при модификации. Предположим, нам понадобится задать компилятору режим оптимизации кода (флаг -O2). Для этого нампришлось бы внести совершенно одинаковые изменения в три разныйстроки файла. В более сложном случае таких строк может понадобиться несколько десятков и даже сотен.Аналогичная проблема встанет, например, если мы захотим произвести сборку другим компилятором.Решить проблему позволяет введение make-переменных. Обозначимимя компилятора C переменной CC, а общие параметры компиляции –переменной CFLAGS18 .Тогда наш Makefile можно переписать следующим образом:CC = gccCFLAGS = -g -Wall -ansi -pedanticmod1.o: mod1.c mod1.h$(CC) $(CFLAGS) -c mod1.c -o mod1.omod2.o: mod2.c mod2.h$(CC) $(CFLAGS) -c mod2.c -o mod2.oprog: main.c mod1.o mod2.o$(CC) $(CFLAGS) main.c mod1.o mod2.o -o prog4.3.3Предопределенные переменные и псевдопеременныеСуществуют определенные соглашения об именах переменных, причем некоторым переменным утилита make присваивает значения сама,если соответствующие значения не заданы явно.Вот некоторые традиционные имена переменных:• CC – команда вызова компилятора языка C;• CFLAGS – параметры для компилятора языка C;• CXX – команда вызова компилятора языка C++;• CXXFLAGS – параметры для компилятора языка C++;18 Причины выбора именно таких обозначений станут ясны из дальнейшего изложения.55• CPPFLAGS – параметры препроцессора (обычно сюда помещаютпредопределенные макропеременные);• LD – команда вызова системного линкера (редактора связей);• MAKE – команда вызова утилиты make со всеми параметрами.По умолчанию переменные CC, CXX, LD и MAKE имеют соответствующиезначения, справедливые для данной системы и в данной ситуации.
Значения остальных перечисленных переменных по умолчанию пусты.Таким образом, при написании Makefile из предыдущего параграфамы могли бы пропустить строку, в которой задается значение переменной CC, в надежде, что соответствующее значение переменная получитбез нашей помощи.Кроме таких переменных общего назначения, в каждой цели могутиспользоваться так называемые псевдопеременные.
Перечислим наиболее интересные из них:• $@ – имя текущей цели;• $< – имя первой цели из списка зависимостей;• $^ – весь список зависимостей.С использованием этих переменных мы можем переписать нашMakefile следующим образом:CFLAGS = -g -Wallmod1.o: mod1.c mod1.h$(CC) $(CFLAGS) -c $< -o $@mod2.o: mod2.c mod2.h$(CC) $(CFLAGS) -c $< -o $@prog: main.c mod1.o mod2.o$(CC) $(CFLAGS) $^ -o $@4.3.4Обобщенные целиКак можно заметить, в том варианте Makefile, который мы написали в конце предыдущего параграфа, правила для сборки обоих дополнительных модулей оказались совершенно одинаковыми.
Можно пойти56дальше и задать одно обобщенное правило для построения объектного файла для любого модуля, написанного на языке C, исходный файлкоторого имеет имя с суффиксом .c, а заголовочный файл – имя с суффиксом .h:%.o: %.c %.h$(CC) $(CFLAGS) -c $< -o $@Если теперь задать список дополнительных модулей с помощью переменной, получим следующий вариант Makefile:OBJMODULES = mod1.o mod2.oCFLAGS = -g -Wall -ansi -pedantic%.o: %.c %.h$(CC) $(CFLAGS) -c $< -o $@prog: main.c $(OBJMODULES)$(CC) $(CFLAGS) $^ -o $@Теперь для добавления к программе нового модуля нам достаточно добавить имя его объектного файла к значению переменной OBJMODULES.Если перечисление модулей через имена объектных файлов представляется неестественным, можно заменить первую строку Makefileследующими двумя строками:SRCMODULES = mod1.c mod2.cOBJMODULES = $(SRCMODULES:.c=.o)Запись $(SRCMODULES:.c=.o) означает, что необходимо взять значение переменной SRCMODULES и в каждом входящем в это значение словезаменить суффикс .c на .o.4.3.5ПсевдоцелиУтилиту make можно использовать не только для построения файлов, но и для выполнения, вообще говоря, произвольных действий.
Добавим к нашему файлу две дополнительные цели:run: prog./progclean:rm -f *.o prog57Теперь по команде make run утилита make произведет, если необходимо, сборку нашей программы и запустит ее. С помощью же команды make clean мы можем очистить рабочую директорию от объектныхи исполняемых файлов (например, если нам понадобится произвестисборку программы с нуля).Такие цели обычно называют псевдоцелями, поскольку их имена необозначают имен создаваемых файлов.4.3.6Автоматическое отслеживание зависимостейВ более сложых проектах модули могут использовать заголовочныефайлы других модулей, что делает необходимой перекомпиляцию модуля при изменении заголовочного файла, не относящегося к этому модулю.
Информацию о том, какой модуль от каких файлов зависит, можнозадать вручную, однако этот способ приведет к трудностям в большихпрограммах, поскольку программист при модификации исходных файлов может случайно забыть внести изменения в Makefile.Более правильным решением будет поручить отслеживание зависимостей компьютеру. Утилита make позволяет наряду с обобщеннымправилом указать список зависимостей для построения конкретных модулей.
Например:%.o: %.c$(CC) $(CFLAGS) -c $< -o $@mod1.o: mod1.c mod1.h mod2.h mod3.hВ этом случае для построения файла mod1.o будет использовано обобщенное правило (поскольку никаких команд в цели mod1.o мы не указали), но список зависимостей будет использован из цели mod1.o.Списки зависимостей можно построить с помощью компилятора.Чтобы получить строку, подобную последней строке вышеприведенногопримера, необходимо дать командуgcc -MM mod1.cЕсли результат выполнения такой команды перенаправить в файл, тоэтот файл можно будет включить в наш Makefile директивой include.Эта директива имеет специальную форму со знаком -, при использовании которой make не выдает ошибок, если файл не найден. Если использовать для файла зависимостей имя deps.mk, соответствующая директива будет выглядеть так:58-include deps.mkБолее того, если предусмотреть цель для генерации файла, включаемого такой директивой, например:deps.mk: $(SRCMODULES)$(CC) -MM $^ > $@утилита make, прежде чем начать построение любых других целей, будет пытаться построить включаемый файл.Отметим, что такое поведение нежелательно для псевдоцели clean,поскольку для очистки рабочей директории от мусора построение файлов зависимостей не нужно и только отнимает лишнее время.