Разделы сайта
Выбор редакции:
- Лучшие компактные смартфоны по отзывам покупателей
- Как узнать какие платные подписки и услуги подключены на ваш номер МТС и отключить их?
- Бюджетная колонка S28 Portable Mini Wireless Bluetooth V3
- Распиновка микро USB разъема
- Кастрация защищенных аккумуляторов Sanyo и Panasonic и небольшой ликбез по Li-ion
- Из каких отелей в Алкмаре открываются красивые виды?
- Обновление Android Oreo для Samsung Galaxy (2018)
- Как узнать скрытый номер
- Тарифный план maxi smart мтс
- Автоматические настройки интернета "Мегафон": инструкция для пользователей
Реклама
Многозадачное программирование в Linux. Многозадачное программирование в Linux Фоновое и приоритетное управление процессами |
Linux - многозадачная и многопользовательская операционная система для образования, бизнеса, индивидуального программирования. Linux принадлежит к семейству UNIX-подобных операционных систем. Linux изначально был написан Линусом Торвальдсом, а затем улучшался бесчисленным количеством народа во всем мире. Он является клоном операционной системы Unix, одной из первых мощных операционных систем, разрабатываемых для компьютеров, но не бесплатной. Но ни Unix System Laboratories, создатели Unix, ни Университет Беркли, разработчики Berkeley Software Distribution (BSD), не участвовали в его создании. Один из наиболее интересных фактов из истории Linux"а - это то, что в его создании принимали участие одновременно люди со всех концов света - от Австралии до Финляндии - и продолжают это делать до сих пор. Вначале Linux разрабатывался для работы на 386 процессоре. Одним из первых проектов Линуса Торвальдса была программа, которая могла переключаться между процессами, один из которых печатал АААА, а другой - ВВВВ. Впоследствии эта программа выросла в Linux. Правильнее, правда будет сказать, что Линус разработал ядро ОС, и именно за его стабильность он отвечает. Linux поддерживает большую часть популярного Unix"овского программного обеспечения, включая графическую систему X Window, - а это огромное количество программ, но стоит подчеркнуть, что Linux поставляется АБСОЛЮТНО БЕСПЛАТНО. Максимум, за что приходится платить, так это за упаковку и CD, на которых записан дистрибутив Linux. Дистрибутив - это сама ОС + набор пакетов программ для Linux. Стоит также упомянуть, что все это поставляется с исходными текстами, и любую программу, написанную под Linux, можно переделать под себя. Это же позволяет перенести любую программу на любую платформу - Intel PC, Macintosh. Кстати, все вышеописанное получилось благодаря Free Software Foundation, фонду бесплатных программ, который является частью проекта GNU. И именно для этих целей была создана GPL - General Public License, исходя из которой Linux - бесплатен, как и весь софт под него, причем коммерческое использование программного обеспечения для Linux или его кусков запрещено. конфигурация система unix linux Кроме вышеописанного, Linux - очень мощная и стабильная ОС. Использование его в Сети оправдывает себя, да и взломать его не так уж и легко. На сегодняшний день, развитие Linux идет по двум ветвям. Первая, с четными номерами версий(2.0, 2.2, 2.4), считается более стабильной, надежной версией Linux. Вторая, чьи версии нумеруются нечетными номерами(2.1, 2.3), является более дерзкой и быстрее развивающейся и, следовательно (к сожалению), более богатой ошибками. Но это уже дело вкуса. В Linux нет разделения на диски С,D, и процесс общения с устройствами очень удобен. Все устройства имеют собственный системный файл, все диски подключаются к одной файловой системе и выглядит это все как бы монолитно, едино. Четкая структура каталогов позволяет находить любую информацию мгновенно. Для файлов библиотек - свой каталог, для запускаемых файлов - свой, для файлов с настройками - свой, для файлов устройств - свой, и так далее. Модульность ядра позволяет подключать любые сервисы ОС без перезагрузки компьютера. Кроме того, вы можете переделать само ядро ОС, благо исходные тексты ядра также имеются в любом дистрибутиве. В ОС Linux очень умело, если так можно выразиться, используется идея многозадачности, т.е. любые процессы в системе выполняются одновременно (сравните с Windows: копирование файлов на дискету и попытка слушать в этот момент музыку не всегда совместимы). Но, не все так просто. Linux чуть более сложен, чем Windows, и не всем так просто перейти на него после использования окошек. На первый взгляд, может даже показаться, что он очень неудобен и труднонастраиваем. Но это не так. Вся изюминка Linux"a в том, что его можно настроить под себя, настроить так, что от пользования этой ОС вы будете испытывать огромное удовлетворение. Огромное количество настроек позволяет изменить внешний (да и внутренний) вид ОС, причем ни одна Linux-система не будет похожа на вашу. В Linux у вас есть выбор в использовании графической оболочки, есть несколько офисных пакетов, программы-серверы, файерволы… Просто целая куча разнообразных программ на любой вкус. В 1998 Linux была самой быстро развивающейся операционной системой для серверов, распространение которой увеличилось в том же году на 212 %. Сегодня пользователей Linux насчитывается более 20,000,000. Под Linux существует множество приложений, предназначенных как для домашнего использования, так и для полностью функциональных рабочих станций UNIX и серверов Internet. Linux уже не просто операционная система. Linux все больше и больше начинает напоминать некий культ. Докопаться до истины в случае культа становится все труднее и труднее. Начнем с фактов. Итак, Linux - это:
Новичков в Linux прежде всего привлекает то, что это "круто" и модно. Существует миф о том, что на самом деле для конечного пользователя эта операционная система не подходит. Для того чтобы собрать надежный и устойчивый к взлому сервер, - это более чем хорошее решение, но не для простого пользователя, которому требуется комфорт, удобство и совершенно не хочется понимать и чувствовать ту систему, с которой он сейчас работает. Это не совсем так. Настроенная Linux-система с графическим интерфейсом проста в использовании и интуитивна не меньше, чем операционная система от Майкрософт. Вот только для того чтобы настроить Linux, сил и знаний потребуется достаточно много. В результате таких особенностей своего создания и развития Linux приобрел весьма специфические "черты характера". С одной стороны, это типичная UNIX-система, многопользовательская и многозадачная. С другой стороны - типичная система хакеров, студентов и вообще любых людей, которым нравиться непрерывно учиться и разбираться во всем до мельчайших подробностей. В гибкости настройки и применения Linux, наверное, просто нет равных. Вы можете пользоваться ей на уровне, на котором работает win95, - т. е. иметь графический десктоп со всеми признаками оного под Windows: значками, панелью задач, контекстным меню, и т. д. Мало того - вы можете установить десктоп, который вообще не будет отличаться по внешнему виду и функциям от "Windows". (Вообще говоря, вариантов оконных менеджеров под Linux просто немеряно, от суперспартанского icewm, до супернавороченного Enlightment + Gnome). С другой стороны, Linux дает вам беспрецедентные возможности приближения к "железу" на любом уровне доступности. Правда, для этого уже мало будет уметь хлопать правой кнопкой мыши, придется выучить СИ и архитектуру компьютера. Но человек, однажды ощутивший этот запах мысли, это вдохновение программиста, когда ты держишь машину "за уши" и можешь сделать с ней буквально все, на что она способна - такой человек уже никогда не сможет вернуться в мягкие лапы "виндозы". Если при использовании коммерческой операционной системы пользователь вынужден ждать выхода следующей версии для того, чтобы получить систему без глюков и багов предыдущей версии, то модульность Линукса позволяет скачать новое ядро, которое выходит не реже раза в два месяца, а то и чаще (стабильная версия). Ответов на вопрос "А что же такое Linux?" можно найти множество. Очень многие считают, что Linux - это только ядро. Но одно только ядро бесполезно для пользователя. Хотя ядро, несомненно, основа ОС Linux, пользователю все время приходится работать с прикладными программами. Эти программы не менее важны, чем ядро. Поэтому Linux - это совокупность ядра и основных прикладных программ, которые обычно бывают установлены на каждом компьютере с этой операционной системой. Объединение ядра и прикладных программ в единое целое проявляется и в названии системы: GNU/Linux. GNU - это проект по созданию комплекса программ, подобного тому, что обычно сопровождает Unix-подобную систему. Сторонникам Linux часто предъявляются претензии, что при разговоре о преимуществах Linux они перечисляют недостатки Windows. Но зачастую это бывает неизбежно, поскольку все познается в сравнении, а большинство пользователей компьютеров сейчас знакомы только с Windows. Итак, что же дает Linux? Оригинал: Light-Weight Processes: Dissecting Linux Threads
В этой статье, предназначенной для разработчиков Linux и студентов факультетов компьютерных наук, рассматриваются основы работы программных потоков и их реализация на основе легковесных процессов в ОС Linux, а для лучшего понимания приводятся примеры исходного кода. Программные потоки являются базовым элементом многозадачного программного окружения. Программный поток может быть описан как среда выполнения процесса; поэтому каждый процесс имеет как минимум один программный поток. Многопоточность предполагает наличие нескольких параллельно работающих (на многопроцессорных системах) и обычно синхронизируемых сред выполнения процесса. Программные потоки имеют свои идентификаторы (thread ID) и могут выполняться независимо друг от друга. Они делят между собой одно адресное пространство процесса и используют эту особенность в качестве преимущества, позволяющего не использовать каналы IPC (систем межпроцессного взаимодействия - разделяемой памяти, каналов и других систем) для обмена данными. Программные потоки процесса могут взаимодействовать - например, независимые потоки могут получать/изменять значение глобальной переменной. Эта модель взаимодействия исключает лишние затраты ресурсов на вызовы IPC на уровне ядра. Поскольку потоки работают в едином адресном пространстве, переключения контекста для потока быстры и не ресурсоемки. Каждый программный поток может обрабатываться планировщиком задач индивидуально, поэтому многопоточные приложения хорошо подходят для параллельного выполнения на многопроцессорных системах. Кроме того, создание и уничтожение потоков происходит быстро. В отличие от вызова fork() , в случае потока не создается копии адресного пространства родительского процесса, вместо этого потоки используют совместно адресное пространство вместе с другими ресурсами, включая дескрипторы файлов и обработчики сигналов. Многопоточное приложение использует ресурсы оптимально и максимально эффективно. В таком приложении программные потоки занимаются различными задачами с учетом оптимального использования системы. Один поток может читать файл с диска, другой записывать данные в сокет. Оба потока будут работать в тандеме, будучи независимыми друг от друга. Эта модель оптимизирует использование системы, тем самым улучшая производительность. Несколько любопытных особенностейНаиболее известной особенностью работы с потоками является их синхронизация, особенно при наличии разделяемого ресурса, отмеченного как критический сегмент. Это сегмент кода, в котором осуществляется доступ к разделяемому ресурсу и который не должен быть доступен более чем одному потоку в любой момент времени. Поскольку каждый поток может исполняться независимо, доступ к разделяемому ресурсу не контролируется, что приводит к необходимости использования примитивов синхронизации, включающих в себя мьютексы (mutual exclusion - взаимное исключение), семафоры, блокировки чтения/записи и другие. Эти примитивы позволяют программистам контролировать доступ к разделяемому ресурсу. В дополнение к вышесказанному, потоки, как и процессы, подвержены переходу в состояния блокировки или ожидания в случае некорректного проектирования модели синхронизации. Отладка и анализ многопоточных приложений также могут быть довольно обременительными. Как программные потоки реализованы в Linux?Linux позволяет разрабатывать и использовать многопоточные приложения. На пользовательском уровне реализация потоков в Linux соответствует открытому стандарту POSIX (Portable Operating System Interface for uniX - Переносимый интерфейс операционных систем Unix), обозначенному как IEEE 1003. Библиотека пользовательского уровня (glibc.so в Ubuntu) предоставляет реализацию API POSIX для потоков. В Linux программные потоки существуют в двух отдельных пространствах - пространстве пользователя и пространстве ядра. В пространстве пользователя потоки создаются при помощи POSIX-совместимого API библиотеки pthread . Эти потоки пространства пользователя неразрывно связаны с потоками пространства ядра. В Linux потоки пространства ядра воспринимаются как "легковесные процессы". Легковесный процесс является единицей основной среды исполнения. В отличие от различных вариантов UNIX, включая такие системы, как HP-UX и SunOS, в Linux не существует отдельной системы для работы с потоками. Процесс или поток в Linux рассматривается как "задача" (task) и использует одинаковые внутренние структуры (ряд структур struct task_structs ). Для ряда потоков процесса, созданных в пространстве пользователя, в ядре существует ряд связанных с ними легковесных процессов. Пример иллюстрирует это утверждение:
#include При помощи утилиты ps можно получить информацию о процессах, а также о легковесных процессах/потоках этих процессов: kanaujia@ubuntu:~/Desktop$ ps -fL UID PID PPID LWP C NLWP STIME TTY TIME CMD kanaujia 17281 5191 17281 0 1 Jun11 pts/2 00:00:02 bash kanaujia 22838 17281 22838 0 1 08:47 pts/2 00:00:00 ps -fL kanaujia 17647 14111 17647 0 2 00:06 pts/0 00:00:00 vi clone.s Что такое легковесные процессы?Легковесным процессом является процесс, поддерживающий работу потока пространства пользователя. Каждый поток пространства пользователя неразрывно связан с легковесным процессом. Процедура создания легковесного процесса отличается от процедуры создания обычного процесса; у пользовательского процесса "P" может существовать ряд связанных легковесных процессов с одинаковым идентификатором группы (group ID). Группировка позволяет ядру производить разделение ресурсов (ресурсы включают в себя адресное пространство, страницы физической памяти (VM), обработчики сигналов и дескрипторы файлов). Это также позволяет ядру избежать переключений контекста при работе с этими процессами. Исчерпывающее разделение ресурсов является причиной наименования этих процессов легковесными. Как Linux создает легковесные процессы?В Linux создание легковесных процессов осуществляется при помощи нестандартизированного системного вызова clone() . Он похож на вызов fork() , но с более широкими возможностями. Вообще, вызов fork() реализуется при помощи вызова clone() с дополнительными параметрами, указывающими на ресурсы, которые будут разделены между процессами. Вызов clone() создает процесс, при этом дочерний процесс разделяет с родительским элементы среды исполнения, включая память, дескрипторы файлов и обработчики сигналов. Библиотека pthread также использует вызов clone() для реализации потоков. Обратитесь к файлу исходного кода ./nptl/sysdeps/pthread/createthread.c в директории исходных кодов glibc версии 2.11.2. Создание своего легковесного процессаПродемонстрируем пример использования вызова clone() . Посмотрите на исходный код из файла demo.c , приведенный ниже:
#include Программа demo.c
позволяет создавать потоки по своей сути тем же способом, что и библиотека pthread
. Тем не менее, прямое использование вызова clone()
нежелательно, поскольку в случае неправильного использования разрабатываемое приложение может завершиться с ошибкой. Синтаксис функции clone()
в Linux представлен ниже:
#include Первым аргументом является функция потока; она вызывается во время запуска потока. После того, как вызов clone() успешно завершается, функция fn начинает исполняться одновременно с вызывающим процессом. Следующим аргументом является указатель на участок памяти для стека дочернего процесса. За шаг до вызова fork() и clone() от программиста требуются действия по резервированию памяти и передаче указателя для использования ее в качестве стека дочернего процесса, так как родительский и дочерний процесс делят между собой страницы памяти - они включают в себя и стек. Дочерний процесс может вызвать функцию, отличную от родительского процесса, поэтому и требуется отдельный стек. В нашей программе мы резервируем этот участок памяти в куче при помощи функции malloc() . Размер стека был установлен равным 64 Кб. Так как стек на архитектуре x86 растет вниз, необходимо симулировать аналогичное поведение, используя выделенную память с конца участка. По этой причине мы передаем следующий адрес функции clone() : (char*) stack + STACK Следующий аргумент flags особо важен. Он позволяет указать, какие ресурсы необходимо разделять с созданным процессом. Мы выбрали битовую маску SIGCHLD | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_VM , описанную ниже:
Последним параметром является аргумент, передаваемый функции (threadFunction ), в нашем случае это файловый дескриптор. Пожалуйста, обратитесь к примеру работы с легковесными процессами demo.c , опубликованному нами ранее. Поток закрывает файл (/dev/null ), открытый родительским процессом. Поскольку родительский процесс и поток делят таблицу файловых дескрипторов, операция закрытия файла затрагивает и родительский процесс, что приводит к неудаче при последующем вызове write() . Родительский процесс ожидает завершения работы потока (момента приема сигнала SIGCHLD ). После этого он освобождает зарезервированную память и возвращает управление. Скомпилируйте и запустите программу обычным образом; вывод должен быть аналогичен приведенному ниже: $gcc demo.c $./a.out Creating child thread child thread entering child thread exiting Parent: child closed our file descriptor $ Linux предоставляет поддержку эффективной, простой и масштабируемой инфраструктуры для работы с потоками. Это стимулирует интерес программистов к экспериментам и разработке библиотек для работы с потоками, использующих clone() в качестве основной функции. Продолжаем тему многопоточности в ядре Linux. В прошлый раз я рассказывала про прерывания, их обработку и tasklet’ы, и так как изначально предполагалось, что это будет одна статья, в своем рассказе о workqueue я буду ссылаться на tasklet’ы, считая, что читатель уже с ними знаком. Статьи цикла:
WorkqueueWorkqueue - это более сложные и тяжеловесные сущности, чем tasklet’ы. Я даже не буду пытаться описать здесь все тонкости реализации, но самое важное, надеюсь, я разберу более или менее подробно.Workqueue, как и tasklet’ы, служат для отложенной обработки прерываний (хотя их можно использовать и для других целей), но, в отличие от tasklet’ов, выполняются в контексте kernel-процесса, соответственно, они не обязаны быть атомарными и могут использовать функцию sleep(), различные средства синхронизации и т.п. Давайте сначала разберемся, как в целом организован процесс обработки workqueue. На картинке он показан очень приближенно и упрощенно, как все происходит на самом деле, подробно описано ниже. В этом темном деле замешаны несколько сущностей. Вообще, worker’ы - это kernel-потоки, то есть ими управляет основной планировщик ядра Linux. Но worker’ы частично вмешиваются в планирование для дополнительной организации параллельного исполнения work’ов. Про это подробнее пойдет ниже. Чтобы очертить основные возможности механизма workqueue, я предлагаю изучить API. Про очередь и ее созданиеalloc_workqueue(fmt, flags, max_active, args...)Параметры fmt и args - это printf-формат для имени и аргументы к нему. Параметр max_activate отвечает за максимальное число work’ов, которые из этой очереди могут исполняться параллельно на одном CPU. Очередь можно создать со следующими флагами:
В привязанных очередях work’и при добавлении привязываются к текущему CPU, то есть в таких очередях work’и исполняются на том ядре, которое его планирует. В этом плане привязанные очереди напоминают tasklet’ы. В непривязанных очередях work’и могут исполняться на любом ядре. Важным свойством реализации workqueue в ядре Linux является дополнительная организация параллельного исполнения, которая присутствует у привязанных очередей. Про нее подробнее написано ниже, сейчас скажу, что осуществляется таким образом, чтобы использовалось как можно меньше памяти, и чтобы при этом процессор не простаивал. Реализовано это все с предположением, что один work не использует слишком много тактов процессора. По аналогии с tasklet’ами, work’ам можно присваивать приоритет исполнения, нормальный или высокий. Приоритет общий на всю очередь. По умолчанию очередь имеет нормальный приоритетом, а если задать флаг WQ_HIGHPRI , то, соответственно, высокий. Флаг WQ_CPU_INTENSIVE имеет смысл только для привязанных очередей. Этот флаг - отказ от участия в дополнительной организации параллельного исполнения. Этот флаг следует использовать, когда ожидается, что work’и будут расходовать много процессорного времени, в этом случае лучше переложить ответственность на планировщик. Про это подробнее написано ниже. Флаги WQ_FREEZABLE и WQ_MEM_RECLAIM специфичны и выходят за рамки темы, поэтому подробно на них останавливаться не будем. Иногда есть смысл не создавать свои собственные очереди, а использовать общие. Основные из них:
Про work’и и их планированиеТеперь разберемся с work’ами. Сначала взглянем на макросы инициализации, декларации и подготовки:DECLARE(_DELAYED)_WORK(name, void (*function)(struct work_struct *work)); /* на этапе компиляции */ INIT(_DELAYED)_WORK(_work, _func); /* во время исполнения */ PREPARE(_DELAYED)_WORK(_work, _func); /* для изменения исполняемой функции */ В очереди work’и добавляются с помощью функций: bool queue_work(struct workqueue_struct *wq, struct work_struct *work); bool queue_delayed_work(struct workqueue_struct *wq, struct delayed_work *dwork, unsigned long delay); /* work будет добавлен в очередь только по истечению delay */ Вот на этом стоит остановиться поподробнее. Хотя мы в качестве параметра указываем очередь, на самом деле, work’и кладутся не в сами workqueue, как это может показаться, а в совершенно другую сущность - в список-очередь структуры worker_pool. Структура worker_pool , по сути, самая главная сущность в организации механизма workqueue, хотя для пользователя она остается за кулисами. Именно с ними работают worker’ы, и именно в них содержится вся основная информация. Теперь посмотрим, какие пулы есть в системе. Для непривязанных очередей worker pool’ы выделяются динамически. Все очереди можно разбить на классы эквивалентности по их параметрам, и для каждого такого класса создается свой worker pool. Доступ к ним осуществляется с помощью специальной хэш-таблицы, где ключом служит набор параметров, а значением, соответственно, worker pool. Всякие мелочиЕще приведу несколько функций из API для полноты картины, но подробно о них говорить не буду:/* Принудительное завершение */ bool flush_work(struct work_struct *work); bool flush_delayed_work(struct delayed_work *dwork); /* Отменить выполнение work */ bool cancel_work_sync(struct work_struct *work); bool cancel_delayed_work(struct delayed_work *dwork); bool cancel_delayed_work_sync(struct delayed_work *dwork); /* Удалить очередь */ void destroy_workqueue(struct workqueue_struct *wq); Как worker’ы справляются со своей работойТеперь, как мы познакомились с API, давайте попробуем подробнее разобраться, как это все работает и управляется.У каждого пула есть набор worker’ов, которые разгребают задачи. Причем, количество worker’ов меняется динамически, подстраиваясь под текущую ситуацию. Как мы уже выяснили, worker’ы - это потоки, которые в контексте ядра выполняют work’и. Worker достает их по порядку один за другим из ассоциированного с ним worker pool, причем work’и, как мы уже знаем, могут принадлежать к разным исходным очередям. Worker’ы условно могут находиться в трех логических состояниях: они могут быть простаивающими, запущенными или управляющими.
Учет работающих worker’ов осуществляется прямо из основного планировщика ядра Linux. Такой механизм управления обеспечивает оптимальный уровень параллельности (concurrency level), не давая workqueue создавать слишком много worker’ов, но и не заставляя work’и без нужды ждать слишком долго. Те, кому интересно, могут посмотреть функцию worker’а в ядре, называется она worker_thread(). Со всеми описанными функциями и структурами можно подробнее ознакомиться в файлах include/linux/workqueue.h , kernel/workqueue.c и kernel/workqueue_internal.h . Также по workqueue есть документация в Documentation/workqueue.txt . Еще стоит отметить, что механизм workqueue используется в ядре не только для отложенной обработки прерываний (хотя это довольно частый сценарий). Таким образом, мы рассмотрели механизмы отложенной обработки прерываний в ядре Linux - tasklet и workqueue, которые представляют собой особую форму многозадачности. Про прерывания, tasklet’ы и workqueue можно почитать в книге "Linux Device Drivers " авторов Jonathan Corbet, Greg Kroah-Hartman, Alessandro Rubini, правда, информация там временами устаревшая.
ЛАБОРАТОРНАЯ РАБОТА №3 МНОГОЗАДАЧНОЕ ПРОГРАММИРОВАНИЕ В LINUX 1. Цель работы: Ознакомиться с компилятором gcc, методикой отладки программ, функциями работы с процессами. 2. Краткие теоретические сведения. Минимальным набором ключей компилятора gcc являются - Wall (выводить все ошибки и предупреждения) и - o (output file): gcc - Wall - o print_pid print_pid. c Команда создаст исполняемый файл print_pid. Стандартная библиотека C (libc, реализованная в Linux в glibc), использует возможности многозадачности Unix System V (далее SysV). В libc тип pid_t определен как целое, способное вместить в себе pid. Функция, которая сообщает pid текущего процесса, имеет прототип pid_t getpid (void) и определена вместе с pid_t в unistd. h и sys/types. h). Для создания нового процесса используется функция fork: pid_t fork(void) Вставляя задержку случайной длины при помощи функций sleep и rand, можно нагляднее увидеть эффект многозадачности: это заставит программу "заснуть" на случайное число секунд: от 0 до 3. Чтобы в качестве дочернего процесса вызвать функцию, достаточно вызвать ее после ветвления: // если выполняется дочерний процесс, то вызовем функцию pid=process(arg); // выход из процесса Часто в качестве дочернего процесса необходимо запускать другую программу. Для этого применяется функции семейства exec: // если выполняется дочерний процесс, то вызов программы if (execl("./file","file",arg, NULL)<0) { printf("ERROR while start process\n"); else printf("process started (pid=%d)\n", pid); // выход из процесса Часто родительскому процессу необходимо обмениваться информацией с дочерними или хотя бы синхронизироваться с ними, чтобы выполнять операции в нужное время. Один из способов синхронизации процессов - функции wait и waitpid: #include #include pid_t wait(int *status) - приостанавливает выполнение текущего процесса до завершения какого-либо из его процессов-потомков. pid_t waitpid (pid_t pid, int *status, int options) - приостанавливает выполнение текущего процесса до завершения заданного процесса или проверяет завершение заданного процесса. Если необходимо узнать состояние порожденного процесса при его завершении и возвращенное им значение, то используют макрос WEXITSTATUS, передавая ему в качестве параметра статус дочернего процесса. status=waitpid(pid,&status, WNOHANG); if (pid == status) { printf("PID: %d, Result = %d\n", pid, WEXITSTATUS(status)); } Для изменения приоритетов порожденных процессов используются функции setpriority и. Приоритеты задаются в диапазоне от -20 (высший) до 20 (низший), нормальное значение - 0. Заметим, что повысить приоритет выше нормального может только суперпользователь! #include #include int process(int i) { setpriority(PRIO_PROCESS, getpid(),i); printf("Process %d ThreadID: %d working with priority %d\n",i, getpid(),getpriority(PRIO_PROCESS, getpid())); return(getpriority(PRIO_PROCESS, getpid())); Для уничтожения процесса служит функция kill: #include #include int kill(pid_t pid, int sig); Если pid > 0, то он задает PID процесса, которому посылается сигнал. Если pid = 0, то сигнал посылается всем процессам той группы, к которой принадлежит текущий процесс. sig - тип сигнала. Некоторые типы сигналов в Linux: SIGKILL Этот сигнал приводит к немедленному завершению процесса. Этот сигнал процесс не может игнорировать. SIGTERM Этот сигнал является запросом на завершение процесса. SIGCHLD Система посылает этот сигнал процессу при завершении одного из его дочерних процессов. Пример: if (pid[i] == status) { printf("ThreadID: %d finished with status %d\n", pid[i], WEXITSTATUS(status)); else kill(pid[i],SIGKILL); 3. Методические указания. 3.1. Для ознакомления с опциями компилятора gcc, описанием функций языка С используйте инструкции man и info. 3.2. Для отладки программ удобно использовать встроенный редактор файлового менеджера Midnight Commander (MC), выделяющий цветом различные языковые конструкции и указывающий в верхней строке экрана положение курсора в файле (строка, столбец). 3.3. В файловом менеджере Midnight Commander имеется буфер команд, вызываемый сочетанием клавиш 3.4. Помните, что текущая директория не содержится в path, поэтому из командной строки необходимо запускать программу как "./print_pid". В MC достаточно навести курсор на файл и нажать 3.5. Для просмотра результата выполнения программы используйте сочетание клавиш 3.6. Для протоколирования результатов выполнения программ целесообразно использовать перенаправление вывода с консоли в файл: ./test > result. txt 3.7. Для доступа к файлам, созданным на сервере Linux, применяйте протокол ftp, клиентская программа которого имеется в Windows 2000 и встроена в файловый менеджер FAR. При этом учетная запись и пароль те же, что и при подключении по протоколу ssh. 4.1. Ознакомиться с опциями компилятора gcc, методикой отладки программ. 4.2. Для вариантов заданий из лабораторной работы №1 написать и отладить программу, реализующую порожденный процесс. 4.3. Для вариантов заданий из лабораторной работы №1 написать и отладить программу, реализующую родительский процесс, вызывающий и отслеживающий состояние порожденных процессов - программ (ждущий их завершения или уничтожающий их, в зависимости от варианта). 4.4. Для вариантов заданий из лабораторной работы №1 написать и отладить программу, реализующую родительский процесс, вызывающий и отслеживающий состояние порожденных процессов - функций (ждущий их завершения или уничтожающий их, в зависимости от варианта). 5. Варианты заданий. См. варианты заданий из лабораторной работы №1 6. Содержание отчета. 6.1. Цель работы. 6.2. Вариант задания. 6.3. Листинги программ. 6.4. Протоколы выполнения программ. 7. Контрольные вопросы. 7.1. Особенности компиляции и запуска С-программ в Linux. 7.2. Что такое pid, как его определить в операционной системе и программе? 7.3. Функция fork - назначение, применение, возвращаемое значение. 7.4. Как запустить на выполнение в порожденном процессе функцию? Программу? 7.5. Способы синхронизации родительского и дочерних процессов. 7.6. Как узнать состояние порожденного процесса при его завершении и возвращенное им значение? 7.7. Как управлять приоритетами процессов? 7.8. Как уничтожить процесс в операционной системе и программе? Наберите в своей оболочке следующую команду: −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− На экран будут выведен список всех работающих в системе процессов. Если хотите посчитать количество процессов, наберите что-нибудь, наподобие этого: −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− $ ps -e --no-headers | nl | tail -n 1 74 4650 pts/0 00:00:00 tail −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− Первое число - это количество работающих в системе процессов. Пользователи KDE могут воспользоваться программой kpm, а пользователи Gnome - программой gnome-system-monitor для получения информации о процессах. На то он и Linux, чтобы позволять пользователю делать одно и то же разными способами. Возникает вопрос: «Что такое процесс?». Процессы в Linux, как и файлы, являются аксиоматическими понятиями. Иногда процесс отождествляют с запущенной программой, однако, это не всегда так. Будем считать, что процесс - это рабочая единица системы, которая выполняет что-то. Многозадачность - это возможность одновременного сосуществования нескольких процессов в одной системе. Linux - многозадачная операционная система. Это означает, что процессы в ней работают одновременно. Естественно, это условная формулировка. Ядро Linux постоянно переключает процессы, то есть время от времени дает каждому из них сколько-нибудь процессорного времени. Переключение происходит довольно быстро, поэтому нам кажется, что процессы работают одновременно. Одни процессы могут порождать другие процессы, образовывая древовидную структуру. Порождающие процессы называются родителями или родительскими процессами, а порожденные - потомками или дочерними процессами. На вершине этого «дерева» находится процесс init, который порождается автоматически ядром в процессе загрузки системы. К каждому процессу в системе привязана пара целых неотрицательных чисел: идентификатор процесса PID (Process IDentifier) и идентификатор родительского процесса PPID (Parent Process IDentifier). Для каждого процесса PID является уникальным (в конкретный момент времени), а PPID равен идентификатору процесса-родителя. Если ввести в оболочку команду ps -ef, то на экран будет выведен список процессов со значениями их PID и PPID (вторая и третья колонки соотв.). Пример работы такой команды: −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− UID PID PPID C STIME TTY TIME CMD root 1 0 0 17:08? 00:00:00 /sbin/init root 2 0 0 17:08? 00:00:00 root 3 2 0 17:08? 00:00:00 root 4 2 0 17:08? 00:00:00 root 5 2 0 17:08? 00:00:00 root 6 2 0 17:08? 00:00:00 root 7 2 0 17:08? 00:00:00 root 8 2 0 17:08? 00:00:00 root 9 2 0 17:08? 00:00:00 root 10 2 0 17:08? 00:00:00 root 11 2 0 17:08? 00:00:00 root 12 2 0 17:08? 00:00:00 root 13 2 0 17:08? 00:00:00 root 14 2 0 17:08? 00:00:00 root 15 2 0 17:08? 00:00:00 root 16 2 0 17:08? 00:00:00 root 17 2 0 17:08? 00:00:00 root 18 2 0 17:08? 00:00:00 root 19 2 0 17:08? 00:00:00 df00 16389 16387 0 20:10 pts/1 00:00:00 /bin/bash df00 17446 2538 0 20:26? 00:00:00 df00 18544 2932 0 20:41 pts/2 00:00:00 /bin/bash -l df00 19010 18544 0 20:48 pts/2 00:00:00 ps -ef −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− Надо отметить, что процесс init всегда имеет идентификатор 1 и PPID равный 0. Хотя в реальности процесса с идентификатором 0 не существует. Дерево процессов можно также представить в наглядном виде при помощи опции --forest программы ps: −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− 2? 00:00:00 kthreadd 3 ?00:00:00 \_ migration/0 4 ?00:00:00 \_ ksoftirqd/0 5 ?00:00:00 \_ watchdog/0 6 ?00:00:00 \_ migration/1 7 ?00:00:00 \_ ksoftirqd/1 8 ?00:00:00 \_ watchdog/1 9 ?00:00:00 \_ events/0 10 ?00:00:00 \_ events/1 11 ?00:00:00 \_ cpuset 12 ?00:00:00 \_ khelper 13 ?00:00:00 \_ netns 14 ?00:00:00 \_ async/mgr 15 ?00:00:00 \_ kintegrityd/0 16 ?00:00:00 \_ kintegrityd/1 18544 pts/2 00:00:00 \_ bash 16388 ?00:00:00 \_ gnome-pty-helpe 16389 pts/1 00:00:00 \_ bash −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− Если вызвать программу ps без аргументов, то будет выведен список процессов, принадлежащих текущей группе, то есть работающих под текущим терминалом. Использование getpid() и getppid() Процесс может узнать свой идентификатор (PID), а также родительский идентификатор (PPID) при помощи системных вызовов getpid() и getppid(). Системные вызовы getpid() и getppid() имеют следующие прототипы: −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− pid_t getpid (void); pid_t getppid (void); −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− Для использования getpid() и getppid() в программу должны быть включены директивой #include заголовочные файлы unistd.h и sys/types.h (для типа pid_t). Вызов getpid() возвращает идентификатор текущего процесса (PID), а getppid() возвращает идентификатор родителя (PPID). pid_t - это целый тип, размерность которого зависит от конкретной системы. Значениями этого типа можно оперировать как обычными целыми числами типа int. Рассмотрим теперь простую программу, которая выводит на экран PID и PPID, а затем «замирает» до тех пор, пока пользователь не нажмет −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− #include #include #include pid_t pid, ppid; pid = getpid (); ppid = getppid (); printf («PID: %d\n», pid); printf («PPID: %d\n», ppid); fprintf (stderr, «Press −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− Проверим теперь, как работает эта программа. Для этого откомпилируем и запустим ее: −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− $ gcc -o getpid getpid.c Press −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− Теперь, не нажимая −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− $ ps -ef | grep getpid df00 19724 19702 0 20:58 pts/3 00:00:00 ./main df00 19856 18544 0 21:00 pts/2 00:00:00 grep --colour=auto main −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− |
Популярное:
Языки программирования. Программное обеспечение. Операционная система Какой язык понимает компьютер |
Новое
- Как узнать какие платные подписки и услуги подключены на ваш номер МТС и отключить их?
- Бюджетная колонка S28 Portable Mini Wireless Bluetooth V3
- Распиновка микро USB разъема
- Кастрация защищенных аккумуляторов Sanyo и Panasonic и небольшой ликбез по Li-ion
- Из каких отелей в Алкмаре открываются красивые виды?
- Обновление Android Oreo для Samsung Galaxy (2018)
- Как узнать скрытый номер
- Тарифный план maxi smart мтс
- Автоматические настройки интернета "Мегафон": инструкция для пользователей
- Каталог партнерских программ Партнерская программа какая лучше