Главная страница
Навигация по странице:

  • Глава 2. Файловый ввод-вывод.

  • Глава 3. Буферизованный ввод-вывод.

  • Глава 4. Расширенный файловый ввод-вывод.

  • Глава 6. Расширенное управление процессами.

  • Глава 8. Управление файлами и каталогами.

  • Глава 9. Управление памятью.

  • Версии, рассмотренные в книге

  • Зачем изучать системное программирование

  • Краеугольные камни системного программирования

  • Системное программирование Линукс. Linux. Системное программирование. Вступление


    Скачать 0.65 Mb.
    НазваниеLinux. Системное программирование. Вступление
    АнкорСистемное программирование Линукс
    Дата23.01.2023
    Размер0.65 Mb.
    Формат файлаpdf
    Имя файлаLinuxSystemProgramming.pdf
    ТипКраткое содержание
    #900372
    страница3 из 14
    1   2   3   4   5   6   7   8   9   ...   14
    Глава 1. Введение и основополагающие концепции. Она — введение в пробле­
    му. Здесь делается обзор Linux, системного программирования, ядра, библио­
    теки C и компилятора C. Главу следует изучить даже самым опытным пользо­
    вателям.
    
    Глава 2. Файловый ввод-вывод. Тут дается вводная информация о фай­
    лах — наиболее важной абстракции в экосистеме UNIX, а также файловом вводе/выводе, который является основой процесса программирования для
    Linux. Подробно рассматриваются считывание информации из файлов и запись информации в них, а также другие базовые операции файлового ввода­выво­
    да. Итоговая часть главы рассказывает, как ядро Linux внедряет (реализует) концепцию файлов и управляет ими.
    
    Глава 3. Буферизованный ввод-вывод. Здесь обсуждается проблема, связанная с базовыми интерфейсами ввода­вывода — управление размером буфера, — и рассказывается о буферизованном вводе­выводе вообще, а также стандартном вводе­выводе в частности как о возможных решениях.
    
    Глава 4. Расширенный файловый ввод-вывод. Завершает трио тем о вводе­
    выводе и рассказывает о продвинутых интерфейсах ввода­вывода, способах распределения памяти и методах оптимизации. В заключение главы мы пого­
    ворим о том, как избегать подвода головок, и о роли планировщика ввода­выво­
    да, работающего в ядре Linux.
    Вступление

    21
    
    Глава 5. Управление процессами. В ней читатель познакомится со второй по важности абстракцией UNIX — процессом — и семейством системных вызовов, предназначенных для базового управления процессами, в частности древним феноменом ветвления (
    fork
    ).
    
    Глава 6. Расширенное управление процессами. Здесь продолжается обсужде­
    ние процессов. Глава начинается с рассмотрения продвинутых способов управ­
    ления процессами, в частности управления в реальном времени.
    
    Глава 7. Поточность. Здесь обсуждаются потоки и многопоточное программи­
    рование. Глава посвящена в основном высокоуровневым концепциям проекти­
    рования. В частности, в ней читатель познакомится с API многопоточности
    POSIX, который называется Pthreads.
    
    Глава 8. Управление файлами и каталогами. Тут обсуждаются вопросы созда­
    ния, перемещения, копирования, удаления и других приемов, связанных с управ­
    лением файлами и каталогами.
    
    Глава 9. Управление памятью. В ней рассказывается об управлении памятью.
    Глава начинается с ознакомления с основными концепциями UNIX, связанны­
    ми с памятью, в частности с адресным пространством процесса и подкачкой страниц. Далее мы поговорим об интерфейсах, к которым можно обращаться для получения памяти и через которые можно возвращать память обратно в ядро.
    В заключение мы ознакомимся с продвинутыми интерфейсами, предназначен­
    ными для управления памятью.
    
    Глава 10. Сигналы. Здесь рассматриваются сигналы. Глава начинается с обсу­
    ждения природы сигналов и их роли в системе UNIX. Затем описываются сигнальные интерфейсы, от самых простых к наиболее сложным.
    
    Глава 11. Время. Она посвящена обсуждению времени, спящего режима и управления часами. Здесь рассмотрены все базовые интерфейсы вплоть до часов POSIX и таймеров высокого разрешения.
    
    Приложение А. В нем рассматриваются многие языковые расширения, предо­
    ставляемые gcc и GNU C, в частности атрибуты, позволяющие сделать функцию константной, чистой или внутристрочной.
    
    Приложение Б. Здесь собрана библиография работ, которые я рекомендую для дальнейшего изучения. Они служат не только важным дополнением к изложен­
    ному в книге материалу, но и рассказывают об обязательных темах, не затрону­
    тых в моей работе.
    Версии, рассмотренные в книге
    Системный интерфейс Linux определяется как бинарный (двоичный) интерфейс приложений и интерфейс программирования приложений, предоставляемый бла­
    годаря взаимодействию трех сущностей: ядра Linux (центра операционной систе­
    мы), библиотеки GNU C (
    glibc
    ) и компилятора GNU C (
    gcc
    — в настоящее время
    Версии, рассмотренные в книге

    22
    он официально называется набором компиляторов для GNU и применяется для работы с различными языками, но нас интересует только C). В этой книге рассмот­
    рен системный интерфейс, определенный с применением версии ядра Linux 3.9, версий glibc
    2.17 и gcc
    4.8. Более новые интерфейсы этих компонентов должны и далее соответствовать интерфейсам и поведениям, документированным в данной книге. Аналогично многие интерфейсы, о которых нам предстоит поговорить, дав­
    но используются в составе Linux и поэтому обладают обратной совместимостью с более ранними версиями ядра, glibc и gcc
    Если любую развивающуюся операционную систему можно сравнить со сколь­
    зящей мишенью, то Linux — это просто гепард в прыжке. Прогресс измеряется днями, а не годами, частые релизы ядра и других компонентов постоянно меняют и правила игры, и само игровое поле. Ни в одной книге не удалось бы сделать до­
    статочно долговечный слепок такого динамичного явления.
    Тем не менее экосистема, в которой протекает системное программирование,
    очень стабильна. Разработчикам ядра приходится проявлять недюжинную изо­
    бретательность, чтобы не повредить системные вызовы, разработчики glibc край­
    не высоко ценят прямую и обратную совместимость, а цепочка инструментов Linux
    (набор программ для написания кода) создает взаимно совместимый код в раз­
    личных версиях. Следовательно, при всей динамичности Linux системное про­
    граммирование для этой операционной системы остается стабильным. Книга, представляющая собой «мгновенный снимок» системы, особенно на современном этапе развития Linux, обладает исключительной фактической долговечностью.
    Я пытаюсь сказать: не беспокойтесь, что системные интерфейсы вскоре изменят­
    ся, и смело покупайте эту книгу!
    Условные обозначения
    В книге применяются следующие условные обозначения.
    Курсивный шрифт
    Им обозначаются новые термины и понятия.
    Шрифт для названий
    Используется для обозначения URL, адресов электронной почты, а также соче­
    таний клавиш и названий элементов интерфейса.
    Шрифт для команд
    Применяется для обозначения программных элементов — переменных и названий функций, типов данных, переменных окружения, операторов и ключевых слов и т. д.
    Шрифт для листингов
    Используется в листингах программного кода.
    ПРИМЕЧАНИЕ
    Данная врезка содержит совет, замечание практического характера или общее замечание.
    Вступление

    23
    ВНИМАНИЕ
    Такая врезка содержит какое-либо предостережение.
    Большинство примеров кода в книге представляют собой краткие фрагменты, которые легко можно использовать повторно. Они выглядят примерно так:
    while (1) {
    int ret;
    ret = fork ();
    if(ret== –1)
    perror("fork");
    }
    Пришлось проделать огромную работу, чтобы фрагменты кода получились столь краткими и при этом не утратили практической ценности. Для работы вам не по­
    требуется никаких специальных заголовочных файлов, переполненных безумными макросами и сокращениями, о смысле которых остается только догадываться. Я не писал нескольких гигантских программ, а ограничился многочисленными, но сжа­
    тыми примерами, которые, будучи практическими и наглядными, сделаны макси­
    мально компактными и ясными. Надеюсь, при первом прочтении книги они послу­
    жат вам удобным пособием, а на последующих этапах работы станут хорошим справочным материалом.
    Почти все примеры являются самодостаточными. Это означает, что вы можете просто скопировать их в текстовый редактор и смело использовать на практике.
    Если не указано иное, сборка всех фрагментов кода должна происходить без при­
    менения каких­либо специальных индикаторов компилятора (в отдельных случа­
    ях понадобится связь со специальной библиотекой). Рекомендую следующую команду для компиляции файла исходников:
    $ gcc -Wall -Wextra -O2 -g -o snippet snippet.c
    Она собирает файл исходного кода snippet.c в исполняемый бинарный файл snippet
    , обеспечивая выполнение многих предупреждающих проверок, значитель­
    ных, но разумных оптимизаций, а также отладку. Код из книги должен компили­
    роваться без возникновения ошибок или предупреждений — хотя, конечно, вам для начала может потребоваться построить скелетное приложение на базе того или иного фрагмента кода.
    Когда в каком­либо разделе вы знакомитесь с новой функцией, она записывается в обычном для UNIX формате справочной страницы такого вида:
    #include
    int posix_fadvise (int fd, off_t pos, off_t len, int advice);
    Все необходимые заголовки и определения находятся вверху, за ними следует полный прототип вызова.
    Условные обозначения

    24
    Работа с примерами кода
    Эта книга написана, чтобы помочь вам при работе. В принципе, вы можете исполь­
    зовать код, содержащийся в ней, в ваших программах и документации. Можете не связываться с нами и не спрашивать разрешения, если собираетесь воспользовать­
    ся небольшим фрагментом кода. Например, если вы пишете программу и кое­где вставляете в нее код из книги, никакого особого разрешения не требуется. Однако если вы запишете на диск примеры из книги и начнете раздавать или продавать такие диски, то на это необходимо получить разрешение. Если вы цитируете это издание, отвечая на вопрос, или воспроизводите код из него в качестве примера, разрешение не нужно. Если вы включаете значительный фрагмент кода из данной книги в документацию по вашему продукту, необходимо разрешение.
    Вступление

    28
    концепций. Даже при программировании в среде разработки, например в системе
    X Window, в полной мере задействовались системные API ядра UNIX. Соответ­
    ственно, можно сказать, что эта книга — о программировании для Linux вообще.
    Однако учтите, что в книге не рассматриваются среды разработки для Linux, напри­
    мер вообще не затрагивается тема make
    . Основное содержание книги — это API системного программирования, предоставляемые для использования на современной машине Linux.
    Можно сравнить системное программирование с программированием прило­
    жений — и мы сразу заметим как значительное сходство, так и важные различия этих областей. Важная черта системного программирования заключается в том, что программист, специализирующийся в этой области, должен обладать глубокими знаниями оборудования и операционной системы, с которыми он имеет дело. Сис­
    темные программы взаимодействуют в первую очередь с ядром и системными библиотеками, а прикладные опираются и на высокоуровневые библиотеки. Такие высокоуровневые библиотеки абстрагируют детальные характеристики оборудо­
    вания и операционной системы. У подобного абстрагирования есть несколько целей: переносимость между различными системами, совместимость с разными версиями этих систем, создание удобного в использовании (либо более мощного, либо и то и другое) высокоуровневого инструментария. Соотношение, насколько активно конкретное приложение использует высокоуровневые библиотеки и на­
    сколько — систему, зависит от уровня стека, для которого было написано приложе­
    ние. Некоторые приложения создаются для взаимодействия исключительно с вы­
    сокоуровневыми абстракциями. Однако даже такие абстракции, весьма отдаленные от самых низких уровней системы, лучше всего получаются у специалиста, имею­
    щего навыки системного программирования. Те же проверенные методы и пони­
    мание базовой системы обеспечивают более информативное и разумное програм­
    мирование для всех уровней стека.
    Зачем изучать системное программирование
    В течение прошедшего десятилетия в написании приложений наблюдалась тен­
    денция к уходу от системного программирования к высокоуровневой разработке.
    Это делалось как с помощью веб­инструментов (например, JavaScript), так и по­
    средством управляемого кода (Java). Тем не менее такие разработки не свидетель­
    ствуют об отмирании системного программирования. Действительно, ведь кому­то приходится писать и интерпретатор JavaScript, и виртуальную машину Java, кото­
    рые создаются именно на уровне системного программирования. Более того, даже разработчики, которые программируют на Python, Ruby или Scala, только выигра­
    ют от знаний в области системного программирования, поскольку будут понимать всю подноготную машины. Качество кода при этом гарантированно улучшится независимо от части стека, для которой он будет создаваться.
    Несмотря на описанную тенденцию в программировании приложений, большая часть кода для UNIX и Linux по­прежнему создается на системном уровне. Этот код написан преимущественно на C и C++ и существует в основном на базе
    Глава 1. Введение и основополагающие концепции

    29
    интерфейсов, предоставляемых библиотекой C и ядром. Это традиционное сис­
    темное программирование с применением Apache, bash
    ,
    cp
    , Emacs, init
    ,
    gcc
    ,
    gdb
    ,
    glibc
    ,
    ls
    ,
    mv
    , vim и X. В обозримом будущем эти приложения не сойдут со сцены.
    К области системного программирования часто относят и разработку ядра или как минимум написание драйверов устройств. Однако эта книга, как и большин­
    ство работ по системному программированию, никак не касается разработки ядра.
    Ее основной фокус — системное программирование для пользовательского про­
    странства, то есть уровень, который находится выше ядра. Тем не менее знания о ядре будут полезным дополнительным багажом при чтении последующего текста.
    Написание драйверов устройств — это большая и объемная тема, которая подроб­
    но описана в книгах, посвященных конкретно данному вопросу.
    Что такое системный интерфейс и как я пишу системные приложения для Linux?
    Что именно при этом мне предоставляют ядро и библиотека C? Как мне удается создавать оптимальный код, какие приемы возможны в Linux? Какие интересные системные вызовы есть в Linux, но отсутствуют в других UNIX­подобных системах?
    Как все это работает? Именно эти вопросы составляют суть данной книги.
    Краеугольные камни системного
    программирования
    В системном программировании для Linux можно выделить три основных крае­
    угольных камня: системные вызовы, библиотеку C и компилятор C. О каждом из этих феноменов следует рассказать отдельно.
    Системные вызовы
    Системные вызовы — это начало и конец системного программирования. Системные вызовы (в англоязычной литературе встречается сокращение syscall) — это вызовы функций, совершаемые из пользовательского пространства. Они направлены из приложений (например, текстового редактора или вашей любимой игры) к ядру.
    Смысл системного вызова — запросить у операционной системы определенную службу или ресурс. Системные вызовы включают как всем знакомые операции, например read()
    и write()
    , так и довольно экзотические, в частности get_thread_area()
    и set_tid_address()
    В Linux реализуется гораздо меньше системных вызовов, чем в ядрах большин­
    ства других операционных систем. Например, в системах с архитектурой x86­64 таких вызовов насчитывается около 300 — сравните это с Microsoft Windows, где предположительно задействуются тысячи подобных вызовов. При работе с ядром
    Linux каждая машинная архитектура (например, Alpha, x86­64 или PowerPC) может дополнять этот стандартный набор системных вызовов своими собственными. Сле­
    довательно, системные вызовы, доступные в конкретной архитектуре, могут отли­
    чаться от доступных в другой. Тем не менее значительное подмножество всех систем­
    ных вызовов — более 90 % — реализуется во всех архитектурах. К этим разделяемым
    90 % относятся и общие интерфейсы, о которых мы поговорим в данной книге.
    Системное программирование

    30
    Активация системных вызовов. Невозможно напрямую связать приложения пользовательского пространства с пространством ядра. По причинам, связанным с обеспечением безопасности и надежности, приложениям пользовательского про­
    странства нельзя разрешать непосредственно исполнять код ядра или манипулиро­
    вать данными ядра. Вместо этого ядро должно предоставлять механизм, с помощью которого пользовательские приложения будут «сигнализировать» ядру о требовании активировать системный вызов. После этого приложение сможет осуществить сис-
    темное прерывание ядра (trap) в соответствии с этим строго определенным меха­
    низмом и выполнить только тот код, который разрешит выполнить ядро. Детали этого механизма в разных архитектурах немного различаются. Например, в процес­
    сорах i386 пользовательское приложение выполняет инструкцию программного прерывания int со значением
    0x80
    . Эта инструкция осуществляет переключение на работу с пространством ядра — защищенной областью, — где ядром выполняется обработчик программного прерывания. Что же такое обработчик прерывания
    0x80
    ?
    Это не что иное, как обработчик системного вызова!
    Приложение сообщает ядру, какой системный вызов требуется выполнить и с ка­
    кими параметрами. Это делается посредством аппаратных регистров. Системные вызовы обозначаются по номерам, начиная с
    0
    . В архитектуре i386, чтобы запросить системный вызов
    5
    (обычно это вызов open()
    ), пользовательское приложение за­
    писывает
    5
    в регистр eax
    , после чего выдает инструкцию int
    Передача параметров обрабатывается схожим образом. Так, в архитектуре i386 регистр применяется для всех возможных параметров — например, регистры ebx
    , ecx
    , edx
    , esi и edi в таком же порядке содержат первые пять параметров. В редких случаях, когда системный вызов имеет более пяти параметров, всего один регистр применяется для указания на буфер в пользовательском пространстве, где хранятся все эти парамет­
    ры. Разумеется, у большинства системных вызовов имеется всего пара параметров.
    В других архитектурах активация системных вызовов обрабатывается иначе, хотя принцип остается тем же. Вам, как системному программисту, обычно не нуж­
    но знать, как именно ядро обрабатывает системные вызовы. Эта информация уже интегрирована в стандартные соглашения вызова, соблюдаемые в конкретной ар­
    хитектуре, и автоматически обрабатывается компилятором и библиотекой C.
    1   2   3   4   5   6   7   8   9   ...   14


    написать администратору сайта