Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология Образование Политология Производство Психология Стандартизация Технологии |
Лабораторная работа № 8. Разработка программ сложной структуры ⇐ ПредыдущаяСтр 9 из 9
1. Цель работы Целью работы является изучение основных принципов обработки программ сложной структуры, состоящих из нескольких программных модулей, а также приобретение практических навыков по использованию системы управления версиями CVS. 2. Методические указания В лабораторной работе № 7 была изучена технология обработки программы простой структуры, состоящей из одного программного модуля. Такая программа может содержать несколько функций, но физически представляет собой один файл, который обрабатывается компилятором. Простая структура характерна для небольших программ, разрабатываемых одним программистом. Современные сложные программы разрабатываются коллективом программистов и состоят из большого числа программных модулей, представленных в виде набора отдельных файлов, каждый из которых компилируется и отлаживается независимо от других модулей. После отладки всех модулей проводится сборка программы в единый исполняемый файл. В общем случае сборка программы сложной структуры должна выполняться в следующей последовательности: · компиляция всех исходных модулей программы, представленных файлами типа.c; для исходных модулей, представленных файлами типа.h, отдельная компиляция не требуется; · компоновка объектных модулей, входящих в программу и хранящихся в файлах типа.o, включая ранее откомпилированные модули, служебные объектные модули (файлы типа.o) и библиотеки (файлы типа.a и.so). В данной работе изучается система управления версиями CVS, используемая для организации коллективной работы над программным проектом, а также методика сборки программы сложной структуры с помощью компоновщика ld. 2.1 Система управления версиями 2.1.1 Общие принципы работы CVS При разработке сложных программ желательно отслеживать изменения исходного кода (кто вносил изменения, когда и какова причина изменений). Если несколько человек работают над проектом программы, важно, чтобы они не делали изменений одного и того же кода одновременно. ОС Linux имеет систему управления версиями CVS (Concurrent Versions System), которая хранит историю изменений определённого набора текстовых файлов (например, исходного кода программ) и облегчает совместную работу группы программистов над одним проектом. Все файлы располагаются в специальном хранилище - репозитории, который обычно размещается на отдельном выделенном сервере, но может находиться и на локальном компьютере пользователя, т.к. учет версий может быть удобен и в случае однопользовательской работы. CVS реализован на основе архитектуры клиент-сервер, причем серверная часть управляет репозиторием, а клиентские части устанавливаются на локальные компьютеры пользователей. Объектом управления CVS является модуль, под которым понимается отдельный блок информации, который весь целиком может быть запрошен пользователем. Обычно для каждого проекта или группы проектов заводится свой CVS-модуль, который хранится в одном или нескольких каталогах репозитория. В репозитории хранится только последняя версия модуля и история всех внесенных в него изменений, начиная с начальной версии. Поэтому в случае необходимости всегда есть возможность вернуться к любой предыдущей версии проекта. Регистрация изменений от лица конкретного пользователя хранится с точностью до строки. Имена версий файлов хранятся в виде имя_файла номер_версии, например myfile.cpp 1.1, myfile.cpp 1.2 и т.д. Для работы с текущей версией исходной программы на локальном компьютере создается рабочий каталог, который не должен находиться в одном каталоге с репозиторием. Рабочий каталог создается отдельно для каждого программного проекта. Локальная копия модуля, полученная из репозитория в рабочий каталог с помощью CVS- клиента, называется рабочей копией. Упрощенная схема системы работы CVS включает следующие основные шаги: · создание хранилища (репозитория); · создание рабочего каталога и размещение в нем текущей версии отлаживаемой программы; · сохранение текущей версии исходного файла в репозитории. Отладка и изменение исходного файла должны выполняться в рабочем каталоге. После внесения изменений текущая версия передается в хранилище с соответствующим комментарием. После передачи в хранилище соответствующий проект может быть удален из рабочего каталога, чтобы исключить возможность внесения в него изменений без поддержки CVS. Имя каталога, в котором располагается хранилище, можно записать в глобальную переменную $CVSROOT для того, чтобы его не указывать в каждой команде CVS. 2.1.2 Основные команды CVS Общий синтаксис команд выглядит следующим образом: cvs [опции_cvs] команда [опции _команды] [аргументы] Здесь опции_cvs используются для управления режимами CVS, опции _команды уточняют действие конкретной команды, аргументы задают имена объектов, над которыми выполняется команда. В таблице 25 приведены основные команды CVS. Таблица 25
2.1.3 Пример работы с CVS Исходные данные: имя домашнего каталога – /home/brigades/ pmi4501, имя файла – file.c. 1. Создание хранилища: а) в домашнем каталоге создаем каталог cvsroot. б) в каталоге cvsroot создаем хранилище: cvs -d /home/brigades/pmi4501/cvsroot init 2. Создание рабочего каталога: а) в домашнем каталоге создаем каталог workdir б) в каталоге workdir создаем каталог project, в котором будут находиться файлы проекта; в) в каталог project запишем файл file.c. 3. Связывание рабочего каталога с хранилищем: а) сделать рабочий каталог текущим; б) выполнить команду cvs -d /home/brigades/ pmi4501/cvsroot checkout -l. 4. Передача проекта (каталога project) и файла file.c в хранилище следующими командами: cvs -d /home/brigades/ pmi4501/cvsroot add project cvs -d /home/brigades/ pmi4501/cvsroot add project /file.c cvs -d /home/brigades/ pmi4501/cvsroot commit При выполнении команды commit будет вызван редактор vi для ввода комментария, например: “Пользователь pmi4501передал файл file.c под управление CVS”. После ввода комментария выполняется стандартные действия: ESC, ”: ”, “wq”. Далее для записи новых версий файла необходимо использовать только команду commit, а команду add использовать не надо, так как там уже есть директория project и файл file.c. 5. Работа в рабочем каталоге (отладка файла file.c): а) получаем исполняемый код путем вызова компилятора gcc; б) исполняемый код запускаем на выполнение; если работает, то переходим на пункт е); в) получаем исполняемый код для отладчика (gcc с ключом -g); г) запускаем отладчик, находим ошибку и выходим из отладчика; д) запускаем редактор vi и исправляем ошибку; е) исправленную версию записываем в хранилище; ж) переходим в пункт а). 6. Вывести обзор исправлений в программе file.c сvs -d ~/cvsroot rdiff -r 1.1. 7. Вывести журнал изменений версий файла file.c cvs -d ~/cvsroot log file.c Упрощенная схема работы CVS представлена на рис. 27.
Рис. 27 2.2 Сборка программы сложной структуры Компоновка программы – процесс организации межпрограммных связей, позволяющий объединить независимо компилированные объектные модули в единый исполняемый (загрузочный) модуль. Компоновщик (или редактор связей) последовательно разрешает все внешние ссылки, получая информацию о них из словарей внешних символов каждого объектного модуля, включаемого в формируемый исполняемый модуль. Процесс разрешения внешней ссылки заключается в поиске модуля с именем, указанным в ESD, и включении его в состав исполняемого модуля, причем поиск может проводиться только в файлах, указанных пользователем, или в известных компоновщику библиотеках. Компоновщик ld имеет большое число управляющих опций и позволяет обеспечить максимально возможный контроль за процессом сборки исполняемого модуля. В лабораторной работе могут потребоваться следующие опции, которые могут находиться в командной строке в любом порядке: -lимя – добавляет статическую библиотеку с именем libимя.a в список файлов для компоновки; например, опция -lm добавляет в список библиотеку математических функций libm.a; -Lкаталог – добавляет указанный каталог в список каталогов для поиска библиотек; -Map=имя – записывает в файл с указанным именем структуру исполняемого модуля, содержащую информацию об относительных адресах, именах и размерах всех объектных модулей, включенных в состав исполняемого модуля, а также диагностические сообщения, возникшие на этапе компоновки; этот файл называется картой памяти. Компоновщик поддерживает два метода компоновки - статический и динамический. Статическая компоновка проводится с использованием библиотечных файлов с расширением .a путем включения в состав исполняемого модуля всех необходимых функций из библиотек. Динамическая компоновка основана на том, что в состав исполняемого модуля включаются только ссылки на библиотечные модули, а непосредственное их извлечение из библиотек и загрузка в оперативную память проводятся на этапе выполнения программы. Для этого метода можно использовать только специальные библиотеки динамической компоновки, находящиеся в файлах типа .so. Компоновщик может вызываться неявным или явным способами. Неявный вызов проводится с помощью компилятора и является наиболее простым способом компоновки. Применение явного вызова позволяет максимально полно использовать все возможности компоновщика, например, сформировать карту памяти, но в этом случае необходимо указать имена всех дополнительных модулей, создающих среду выполнения программы. Примеры: а) неявный вызов gcc func1.o main.o -o myprogram б) явный вызов: ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o myprogram -Map=abcdmap /usr/lib64/crt1.o /usr/lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtbegin.o main.o func1.o -lc /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtend.o /usr/lib64/crtn.o
Здесь в первом случае вызывается компилятор gcc и на его вход подаются два объектных файла – func1.o и main.o, результат записывается в файл myprogram и для компоновки используется стандартная библиотека libc.a, а во втором – вызывается компоновщик ld, которому на вход подаются не только файлы пользователя func1.o и main.o, но и служебные файлы (crt1.o, crti.o, crtbegin.o, crtend.o, crtn.o). Назначение служебных модулей: crt1.o – содержит код, инициализирующий среду исполнения языка C и вызывающий определенную пользователем функцию main; crti.o – содержит пролог функции _init, помещаемый в начало секции.init; crtn.o – содержит эпилог функции _init, помещаемый в конец секции.init; crtbegin.o, crtend.o – обрабатывают глобальные конструкторы и деструкторы языка C++; Обратите внимание: в имени файла crt1.o используется символ «единица»! Для того, чтобы не указывать абсолютный путь к служебным файлам, при выполнении лабораторной работы рекомендуется скопировать все эти файлы в Ваш рабочий каталог. В этом случае в командной строке можно указывать только имена файлов. 2.3 Тестовый пример для лабораторной работы В качестве тестового примера используется программа abcd.c из лабораторной работы № 7. Для преобразования этой программы в программу сложной структуры необходимо вынести из нее функции PrintWords и GetWords в отдельные файлы, которые в основной функции main должны быть описаны как внешние. Возможны два способа описания внешних функций – с помощью предложений externилиinclude. В первом случае внешняя функция хранится в виде файла типа.c, компилируется отдельно от основной программы и включается в исполняемый файл компоновщиком при сборке программы. Во втором случае функция хранится в файле типа.h и подключается компилятором к основной программе на этапе препроцессорной обработки. Ниже приведен пример программы abcd2.c c использованием внешней функции PrintWords, которая хранится в файле printwords.c: #include < stdio.h> #include < ctype.h> #include < string.h> /* Manifests for state machine to parse input line */ #define WORD 0 #define IGNORE 1 /* Globals, used by both subroutines. */ char *Words[BUFSIZ/2]; /* Worst case, single letters. */ int WordCount; extern void PrintWords(int wc, char match, char *Words[]);
int GetWords (buf) char buf[]; /* The input buffer. */ { register char *cp; /* Pointer for scanning. */ int end = strlen(buf); /* length of the buffer. */ register int wc = 0; /* Number of words found. */ int state = IGNORE; /* Current state. */ /* For each character in the buffer. */ for (cp = & buf[0]; cp < & buf[end]; cp++) { /* A simple state machine to process * the current character in the buffer. */ switch(state) { case IGNORE: if (! isspace(*cp)) { Words[wc++] = cp; /* Just started a word? Save it. */ state = WORD; /* Reset the state. */ } break; case WORD: if (isspace(*cp)) { *cp = '\0'; /* Just completed aword? terminate it. */ state = IGNORE; /* Reset the state. */ } break; }} return wc; /* Return the word count. */ }
int main(argc, argv) int argc; char *argv[]; { char buf[BUFSIZ], match; /* Check command line arguments. */ if (argc < 2) match = '\0'; /* No command line argument, match all words. */ else match = *++argv[1]; /* match the char after the first - */ /* Until no more input on stdin. */ while(gets(buf)! = (char *)NULL) { WordCount = GetWords(buf); /* Paste the input buffer. */ PrintWords(WordCount, match, Words); /* Print the matching words. */ } return(0); /* Return success to the shell. */ }
Файл printwords.c:
#include < stdio.h> #include < ctype.h> #include < string.h> void PrintWords(wc, match, Words) int wc; /* Number of words in Words[] */ char match; char *Words[BUFSIZ/2]; /* Attempt to match this character. */ { register int ix; /* Index in Words[]. */ register char *cp; /* Pointer for searching. */ for (ix=0; ix < wc; ix++) { cp = Words[ix]; /* Try to match the given character. * Scan the word, attempting to match, * or until the end of the word is found. */ while ((*cp) & & (*cp! = match)) ++cp; if (*cp == match) /* Found a match? Write the word on stdout. */ (void) printf(" %s\n", Words[ix]); } return; } При использовании второго способа описания внешняя функция PrintWords должна быть сохранена в файле printwords.h, для подключения которого в главной программе необходимо использовать директиву #include < printwords.h>. Аналогично можно вынести из главной программы функцию GetWords. В таблице 26 приведены варианты заданий для выполнения лабораторной работы. Таблица 26
3. Порядок выполнения работы 1. Создайте в домашнем каталоге подкаталоги cvsroot и workdir. 2. В каталоге workdir создайте подкаталог project. 3. В каталоге cvsroot создайте репозиторий CVS. 4. В каталог projectскопируйте файл abcd.c с устраненными синтаксическими ошибками (п.5 задания к лабораторной работе № 7). 5. Передайте каталог project и файл abcd.c в репозиторий. При выполнении команды commit с помощью редактора vi вводите комментарий, например: “Пользователь pmi4501передал файл abcd.c под управление CVS”. 6. С помощью отладчика gdb выполните поиск и устранение семантических ошибок в программе abcd (п.7 задания к лабораторной работе № 7). Каждое исправление в программе должно сопровождаться записью в репозиторий новой версии с комментарием, поясняющим на русском языке сущность исправлений (например, номер строки программы abcd.c и причина исправления). Занесите в отчет результаты тестирования программы. 7. После получения корректных результатов выполнения программы abcd с помощью редактора vi в начало отлаженной программы введите комментарий: " Программа abcd отлажена с помощью отладчика gdb дд.мм.гг. бригадой группы ПМ-ХХ в составе: ФИО1, ФИО2..." 8. Выведите на монитор и в файл всю последовательность осуществленных модификаций содержимого файла abcd.c, занесите данные из файла в отчет. 9. Выполните разбиение программы на функции в соответствии с вариантом задания из таблицы 26. Обратите внимание на тип функции (внутренняя или внешняя), тип файла (.c, .h или.o) и тип модуля (исходный или объектный). 10. Выполните сборку программы в соответствии вариантом задания, используя неявный вызов компоновщика и задав имя исполняемого файла adcd2_1; проверьте корректность работы программы и занесите в отчет результаты ее тестирования. 11. Выполните поиск во внешней памяти каталога obj, скопируйте его содержимое в Ваш рабочий каталог и определите назначение скопированных файлов. 12. Выполните сборку программы в соответствии вариантом задания, используя явный вызов компоновщика. Результатом сборки должны быть исполняемый файл abcd2_2и карта памяти abcd2_map; проверьте корректность работы программы и занесите в отчет результаты ее тестирования. 13. Из карты памяти abcd2_map определите размеры машинного кода модулей abcd2.o, printwords.oиgetwords.o, сравните их с размерами исходного и объектного кода этих модулей (файлы типа .с и .о). Результат представьте в виде таблицы 27: Таблица 27
14. Добавьте в make-файл, разработанный при выполнении лабораторной работы № 7, два новых правила, реализующие п. 10 и 12 задания. Проверьте корректность его работы. 4. Контрольные вопросы 1. Назначение и архитектура CVS. 2. Основные этапы в схеме функционирования CVS. 3. Основные команды CVS, примеры их использования. 4. Что является объектом управления CVS. 5. Понятие и структура репозитория CVS. 6. Поясните различия между программами простой и сложной структуры. 7. Понятие внешней функции, способы их описания в языках С и С++. 8. Алгоритм работы компоновщика. 9. Дайте сравнение явного и неявного вызова компоновщика. 10. Карта памяти: назначение и структура. Список источников 1. Котельников Е. Введение во внутреннее устройство Windows – [Электронный ресурс] – http: //www.intuit.ru/studies/courses/10471/1078/info 2. Описание редактора связей GNU ld – [Электронный ресурс] –https: //www.opennet.ru/docs/RUS/gnu_ld/gnuld.html#toc1 3. Программирование под Linux – [Электронный ресурс] – http: //www.firststeps.ru/linux/general1.html 4. Игнатов В.. Эффективное использование GNU Make – [Электронный ресурс] – http: //citforum.ru/operating_systems/gnumake/gnumake_04.shtml 5. CVS – система поддержки версий текстов – [Электронный ресурс] – http: //dbserv.pnpi.spb.ru/~shevel/Book/node110.html 6. Jim Blandy. Введение в CVS. Конспект первого дня двухдневного курса по CVS. Перевод на русский язык: Алексей Махоткин. – [Электронный ресурс] – http: //citforum.ru/programming/digest/cvsintrorus.shtml 7. Пояснения по служебным модулям библиотеки C Runtime. http: //www.borisenko.by
Популярное:
|
Последнее изменение этой страницы: 2017-03-08; Просмотров: 1061; Нарушение авторского права страницы