Книжная полка Сохранить
Размер шрифта:
А
А
А
|  Шрифт:
Arial
Times
|  Интервал:
Стандартный
Средний
Большой
|  Цвет сайта:
Ц
Ц
Ц
Ц
Ц

Системное программирование в среде Linux

Покупка
Основная коллекция
Артикул: 779575.01.99
В данном пособии описан интерфейс прикладного программирования (API) UNIX-совместимых операционных систем: от файловых операций и использования библиотек до методов и средств разработки многозадачного и многопоточного программного обеспечения, а также средства межзадачной (IPC) и межпоточной коммуникации: программные каналы и каналы FIFO, очереди сообщений, семафоры, разделяемая память System V и POSIX, взаимные исключения и условные переменные. Кроме того, кратко обсуждаются средства коммуникации процессов по сети и особенности взаимодействия приложений и системных служб. Учебное пособие предназначено для студентов IV курса, обучающихся по направлению 27.03.04 «Управление в технических системах», а также может быть полезно студентам ряда других технических специальностей, связанных с разработкой многозадачного и многопоточного программного обеспечения в среде операционных систем семейства Linux.
Гунько, А. В. Системное программирование в среде Linux : учебное пособие / А. В. Гунько. - Новосибирск : Изд-во НГТУ, 2020. - 235 с. - ISBN 978-5-7782-4160-2. - Текст : электронный. - URL: https://znanium.com/catalog/product/1870577 (дата обращения: 21.11.2024). – Режим доступа: по подписке.
Фрагмент текстового слоя документа размещен для индексирующих роботов
Министерство науки и высшего образования Российской Федерации 

НОВОСИБИРСКИЙ ГОСУДАРСТВЕННЫЙ ТЕХНИЧЕСКИЙ УНИВЕРСИТЕТ 
__________________________________________________________________________ 
 
 
 
 
 
 
 
 
А.В. ГУНЬКО 
 
 
 
СИСТЕМНОЕ ПРОГРАММИРОВАНИЕ 
В СРЕДЕ LINUX 
 
 
Утверждено Редакционно-издательским советом университета 
в качестве учебного пособия 
 
 
 
 
 
 
 
 
 
 
 
 
 
НОВОСИБИРСК 
2020 

УДК 004.45 (075.8) 
Г 948 
 
Рецензенты: 
А.А. Малявко, канд. техн. наук, доцент 
А.Б. Колкер, канд. техн. наук, доцент 
 
 
Гунько А.В.  
Г 948  
Системное программирование в среде Linux: учебное пособие / А.В. Гунько. – Новосибирск: Изд-во НГТУ, 2020. – 235 с. 

ISBN 978-5-7782-4160-2 

В данном пособии описан интерфейс прикладного программирования (API) UNIX-совместимых операционных систем: от файловых 
операций и использования библиотек до методов и средств разработки многозадачного и многопоточного программного обеспечения, а 
также средства межзадачной (IPC) и межпоточной коммуникации: 
программные каналы и каналы FIFO, очереди сообщений, семафоры, 
разделяемая память System V и POSIX, взаимные исключения и 
условные переменные. 
Кроме того, кратко обсуждаются средства коммуникации процессов по сети и особенности взаимодействия приложений и системных 
служб. 
Учебное пособие предназначено для студентов IV курса, обучающихся по направлению 27.03.04 «Управление в технических системах», а также может быть полезно студентам ряда других технических 
специальностей, связанных с разработкой многозадачного и многопоточного программного обеспечения в среде операционных систем семейства Linux. 
 
 
Работа подготовлена на кафедре автоматики НГТУ 
 
 
 
УДК 004.45 (075.8) 
 
ISBN 978-5-7782-4160-2 
© Гунько А.В., 2020 
 
© Новосибирский государственный  
 
технический университет, 2020 

 
 
 
 
 
 
 
Предисловие 
 
Данное учебное пособие можно рассматривать как вторую часть 
учебного пособия «Программирование (в среде Windows)» [1], поскольку в нем с тех же позиций описывается примерно тот же набор 
системных вызовов, характерных для любой современной операционной системы (ОС).  
Пособие базируется на существенно переработанном конспекте 
лекций по дисциплине «Системное программное обеспечение» [2], читаемой автором с 2004 г. Переработка заключалась не только в удалении глав, относящихся к Windows API, но и в добавлении разделов, 
посвященных низкоуровневым операциям с файлами, программированию и применению статических и динамических библиотек функций, 
средств IPC стандарта POSIX, сетевому программированию, системным службам (демонам). 
Издание ориентировано на студентов, умеющих программировать 
на классическом (ANSI C) языке С, имеющих представление об общих 
принципах построения современных операционных систем и знающих 
основные команды UNIX-систем. 
 
 
 

Глава 1. Введение в системное  
программирование 
 
Любая современная операционная система (ОС) всегда выступает 
как интерфейс между аппаратной частью компьютера и пользователем 
с его прикладными программами, которым предоставляется среда для 
их выполнения. Под интерфейсом операционных систем следует понимать специальные интерфейсы системного и прикладного программирования, предназначенные для выполнения следующих задач [3]: 
1. Управление процессами, которое включает в себя следующий 
набор основных функций: 
 запуск, приостановка и снятие задачи с выполнения; 
 задание или изменение приоритета задачи; 
 взаимодействие задач между собой (используя механизмы сигналов, семафоры, очереди, каналы); 
 RPC (remote procedure call) – удаленный вызов подпрограмм.  
2. Управление памятью: 
 запрос на выделение блока памяти;  
 освобождение памяти; 
 изменение параметров блока памяти (память может быть заблокирована процессом либо предоставлена в общий доступ); 
 отображение файлов на память (имеется не во всех системах). 
3. Управление вводом-выводом: 
 запрос на управление виртуальными устройствами (управление 
вводом-выводом является привилегированной функцией ОС); 
 файловые операции (запросы к системе управления файлами на 
создание, изменение и удаление данных, собранных в файлы). 
Обычно этот интерфейс называют ядром (kernel), так как оно имеет 
относительно небольшой объем и составляет основу ОС. На рис. 1.1 
изображена схема, отражающая архитектуру системы UNIX [4]. 

Интерфейс ядра – это слой программного обеспечения, называемый системными вызовами (область с заливкой на рис. 1.1). Библиотеки функций общего пользования основываются на интерфейсе системных вызовов, но прикладная программа может свободно пользоваться 
как теми, так и другими. Командная оболочка (shell) – это особое приложение, которое предоставляет интерфейс для запуска других приложений.  
В более широком смысле операционная система – это ядро и все 
остальное программное обеспечение, которое делает компьютер пригодным к использованию и обеспечивает его индивидуальность. В состав этого программного обеспечения входят системные утилиты, прикладные программы, командные оболочки, библиотеки функций общего пользования и т. п. 
 

 
Рис. 1.1. Архитектура системы UNIX 

Любая операционная система дает прикладным программам возможность обращаться к системным службам. Во всех реализациях 
UNIX имеется строго определенное число точек входа в ядро, которые 
называются системными вызовами (см. рис. 1.1). В Linux с ядром версии 3.2.0 имеется 380 системных вызовов, а в FreeBSD 8.0 их более 450. 

Интерфейс системных вызовов определяется на языке C независимо от конкретных реализаций, использующих системные вызовы в той 
или иной системе. В этом его отличие от многих старых систем, которые традиционно определяли точки входа в ядро на языке ассемблера. 
В системе UNIX для каждого системного вызова предусматривается одноименная функция в стандартной библиотеке языка C (библиотечная функция). Пользовательский процесс вызывает эту функцию 
как обычно, а она вызывает соответствующую службу ядра, применяя 
способ обращения, принятый в данной системе. Далее будем рассматривать системные вызовы и библиотечные функции как обычные 
функции языка C. И те и другие предназначены для обслуживания 
прикладных программ. Однако при этом нужно понимать, что библиотечные функции можно заменить, если в этом возникнет необходимость, а системные вызовы – нет. Соответственно, под системным 
программированием будем понимать программирование прикладных 
приложений с использованием системных вызовов. 
Рассмотрим в качестве примера функцию выделения памяти 
malloc. Существует масса способов распределения памяти и алгоритмов «сборки мусора», но нет единой методики, оптимальной абсолютно для всех возможных ситуаций. Системный вызов sbrk (см. 
man 2 sbrk или info sbrk в вашей UNIX-системе), описанный в 
файле включения unistd.h, который занимается выделением памяти, не 
является диспетчером памяти общего назначения. Он лишь увеличивает или уменьшает адресное пространство процесса на заданное количество байтов, а управление этим пространством возлагается на сам 
процесс. Функция malloc реализует одну конкретную модель распределения памяти. На самом деле многие программные пакеты реализуют собственные алгоритмы распределения памяти с использованием 
системного вызова sbrk. На рис. 1.2 показаны взаимоотношения между приложением, функцией malloc и системным вызовом sbrk. 
Здесь мы видим четкое разделение обязанностей: системный вызов 
выделяет дополнительную область памяти от имени процесса, а библиотечная функция malloc распоряжается этой областью. 
Прикладная программа может обращаться к системному вызову и к 
библиотечной функции. Кроме того, следует помнить, что библиотечные функции в свою очередь также могут обращаться к системным 
вызовам. Это наглядно показано на рис. 1.3. 

Рис. 1.2. Разделение обязанностей функции malloc  
и системного вызова sbrk 

 

 
Рис. 1.3. Разделение обязанностей библиотечных функций  
и системных вызовов 

Другое отличие системных вызовов от библиотечных функций заключается в том, что системные вызовы обеспечивают лишь минимально необходимую функциональность, тогда как библиотечные 
функции часто обладают более широкими возможностями. Мы уже 
видели это различие на примере сравнения системного вызова sbrk с 
библиотечной функцией malloc. Мы еще столкнемся с этим различием, когда будем сравнивать функции низкоуровневого ввода-вывода и 
стандартные функции ввода-вывода (глава 3). 
Системные вызовы управления процессами (fork, exec и 
waitpid, см. главу 5) обычно вызываются пользовательским процессом напрямую. Но существуют также библиотечные функции, которые 
служат для упрощения самых распространенных случаев: например, 
функция popen, которая будет рассмотрена в разделе 6.2. 

Вопросы для самопроверки 

1. Перечислите основные задачи, решаемые программными интерфейсами операционных систем. 
2. Что понимается под термином «ядро ОС»? 
3. Дайте иное, чем описано выше, определение системных вызовов. 
4. Что понимается под термином «системное программирование»? 
5. В чем разница между библиотечными функциями и системными 
вызовами? 
 
 
 
 

Глава 2. Общие принципы Linux API 

2.1. Стандарты, лежащие в основе Linux API 

Первая реализация UNIX была разработана в 1969 году (в год 
рождения Линуса Торвальдса (Linus Torvalds)) Кеном Томпсоном 
(Ken Thompson) в компании Bell Laboratories, являвшейся подразделением телефонной корпорации AT&T. Эта реализация была написана на ассемблере для мини-компьютера Digital PDP-7. К 1973 году 
язык С уже был доведен до состояния, позволившего почти полностью переписать на нем ядро UNIX. Таким образом, UNIX стала одной из самых ранних ОС, написанных на языке высокого уровня, 
что позволило в дальнейшем портировать ее на другие аппаратные 
архитектуры. 
Версия UNIX, включавшая в себя свой собственный исходный код, 
получила весьма широкое распространение под названием Berkeley 
Software Distribution (BSD). Первым полноценным дистрибутивом, появившимся в декабре 1979 года, стал 3BSD. 
В 1981 году состоялся выпуск UNIX System III (три). Эта версия 
была создана организованной в компании AT&T группой поддержки 
UNIX (UNIX Support Group, USG). 
В 1983 году последовал первый выпуск System V (пять). Несколько 
последующих выпусков привели к тому, что в 1989 году состоялся 
окончательный выпуск System V Release 4 (SVR4), ко времени которого в System V было перенесено множество свойств из BSD, включая 
сетевые объекты. Лицензия на System V была выдана множеству коммерческих поставщиков, использовавших эту версию как основу своих 
собственных реализаций UNIX. 
Ввиду широкого разнообразия реализаций UNIX, основанных 
как на BSD, так и на System V, усложнилось портирование программных продуктов с одной реализации UNIX на другую. Эта си
туация показала, что требовалась стандартизация языка программирования С и системы UNIX, чтобы упростить портирование приложений с одной системы на другую. Рассмотрим выработанные в 
итоге стандарты. 

2.1.1. Стандарт языка С 

Стандарт языка С, на котором написано ядро UINX, приводился в 
вышедшей в 1978 году книге Кернигана (Kernighan) и Ритчи (Ritchie) 
«Язык программирования Си». С появлением в 1985 году языка C++ 
проявились конкретные улучшения и дополнения, которые могли быть 
привнесены в С без нарушения совместимости с существующими программами. В частности, сюда можно отнести прототипы функций, 
присваивание структур, спецификаторы типов (const и volatile), перечисляемые типы и ключевое слово void. Эти факторы побудили к 
стандартизации языка С. Ее кульминацией в 1989 году стало утверждение Американским институтом национальных стандартов (ANSI) 
стандарта языка С (ХЗ.159-1989), который в 1990 году был принят в 
качестве стандарта (ISO/IEC 9899:1990) Международной организацией 
по стандартизации (ISO). Наряду с определением синтаксиса и семантики языка С в этом стандарте давалось описание стандартной библиотеки С, включающей возможности stdio, функции обработки строк, 
математические функции, различные файлы заголовков и т. д. Эту версию С обычно называют С89 или (значительно реже) ISO С90, и она 
полностью рассмотрена во втором издании (1988 года) книги Кернигана и Ритчи «Язык программирования Си». 
Пересмотренное издание стандарта языка С было принято ISO в 
1999 году. Его обычно называют С99, и он включает несколько изменений языка и его стандартной библиотеки. В частности, там описаны 
добавление типов данных long long и логического (булева), присущий 
C++ стиль комментариев (//), ограниченные указатели и массивы переменной длины. 
Исторически С89 часто называли ANSI С, и это название до сих 
пор иногда употребляется в таком значении. Но после того, как комитет ANSI принял пересмотренную версию С99, будет правильным считать, что стандартом ANSI С следует называть С99. 
Стандарты языка С не зависят от особенностей операционной системы, то есть они не привязаны к UNIX-системе. Это означает, что 
программы на языке С, для написания которых использовалась только