Начало - Браузъри
Многозадачно програмиране в Linux. Предистория на многозадачното програмиране на Linux и управление на приоритетни процеси

Linux- многозадачност и много потребители операционна системаза образование, бизнес, индивидуално програмиране. Linux принадлежи към семейството на UNIX-подобни операционни системи.

Linux първоначално е написан от Линус Торвалдс и след това е подобрен от безброй хора по света. Това е клонинг на операционната система Unix, една от първите мощни операционни системи, разработени за компютри, но не е безплатна. Но нито Unix System Laboratories, създателите на Unix, нито университетът Бъркли, разработчиците на Berkeley Software Distribution (BSD), са участвали в създаването му. Един от най интересни фактиот историята на Linux е, че хора от цял ​​свят са участвали едновременно в създаването му - от Австралия до Финландия - и продължават да го правят и до днес.

Linux първоначално е проектиран да работи на 386 процесор. Един от първите проекти на Линус Торвалдс беше програма, която можеше да превключва между процеси, единият от които щеше да отпечата AAAA, а другият да отпечата BBBB. Тази програма впоследствие прерасна в Linux. Би било по-правилно да се каже, че Линус е разработил ядрото на ОС и той е отговорен за неговата стабилност.

Linux поддържа повечето популярни Unix софтуер, включително графична система X Window - и това е огромен брой програми, но си струва да се подчертае, че Linux идва АБСОЛЮТНО БЕЗПЛАТНО. Най-много, което трябва да платите, е опаковката и компактдиска, на който е записана дистрибуцията на Linux. Дистрибуцията е самата ОС + набор от софтуерни пакети за Linux. Също така си струва да споменем, че всичко това идва с изходния код и всяка програма, написана под Linux, може да бъде модифицирана, за да отговаря на вашите нужди. Това също ви позволява да прехвърляте всяка програма на всяка платформа - Intel PC, Macintosh. Между другото, всичко по-горе се случи благодарение на Фондацията за свободен софтуер, фонд безплатни програми, който е част от проекта GNU. И именно за тези цели е създаден GPL - General Public License, въз основа на който Linux е безплатен, както и целият софтуер за него, а търговската употреба на софтуер за Linux или негови части е забранена. конфигурационна система unix linux

Освен горното, Linux е много мощна и стабилна операционна система. Използването му в интернет се отплаща, а хакването му не е толкова лесно.

Днес развитието на Linux следва два клона. Първият, с четни номера на версията (2.0, 2.2, 2.4), се счита за по-стабилна и надеждна версия на Linux. Вторият, чиито версии са номерирани с нечетни номера (2.1, 2.3), е по-смел и по-бързо развиващ се и следователно (за съжаление) по-богат на грешки. Но това е въпрос на вкус.

В Linux няма разделение задвижва C, D, а процесът на комуникация с устройства е много удобен. Всички устройства имат свои собствени системен файл, всички дискове са свързани към една файлова система и всичко изглежда монолитно, едно. Ясната структура на директорията ви позволява незабавно да намерите всяка информация. За библиотечни файлове - собствена директория, за стартирани файлове - собствена, за файлове с настройки - собствена, за файлове на устройство - собствена и т.н.

Модулността на ядрото ви позволява да свържете всякакви OS услуги, без да рестартирате компютъра. Освен това можете да преработите самото ядро ​​на ОС, тъй като изходните кодове на ядрото също са налични във всяка дистрибуция.

Linux OS много умело, така да се каже, използва идеята за многозадачност, т.е. всички процеси в системата се изпълняват едновременно (сравнете с Windows: копирането на файлове на флопи диск и опитът за слушане на музика в този момент не винаги са съвместими).

Но не всичко е толкова просто. Linux е малко по-сложен от Windows и не всеки може лесно да премине към него след използване на Windows. На пръв поглед може дори да изглежда, че е много неудобно и трудно за конфигуриране. Но това не е вярно. Целият акцент на Linux е, че можете да го персонализирате за себе си, да го конфигурирате така, че да изпитате голямо удовлетворение от използването на тази операционна система. Огромен брой настройки ви позволяват да промените външния (и вътрешния) вид на операционната система, а не една единствена Linux система ще бъде подобна на вашата. В Linux имате избор при използване на графична обвивка, има няколко. офис пакети, сървърни програми, защитни стени... Просто цял куп различни програмиза всеки вкус.

През 1998 г. Linux беше най-бързо развиващата се сървърна операционна система, като приемането се увеличи с 212% през тази година. Днес има повече от 20 000 000 потребители на Linux. Под Linux има много приложения, предназначени и за двете домашна употреба, както и за напълно функционални UNIX работни станции и интернет сървъри.

Linux вече не е просто операционна система. Linux все повече се превръща в култ. Достигането до истината в случай на култ става все по-трудно. Да започнем с фактите. Така че Linux е:

  • * безплатен (или по-скоро свободно разпространяван) Unix клонинг;
  • * операционна система с истинска многозадачност;
  • * ОС, която всеки „потребител“ може да модифицира, тъй като можете да намерите изходни кодове за почти всяка част от нея;
  • * който е конфигуриран точно както желаете, а не както предпочита производителят.

Новобранците в Linux са привлечени преди всичко от факта, че е „готин“ и модерен. Има мит, че тази операционна система не е наистина подходяща за крайния потребител. За да се сглоби надежден и устойчив на хакове сървър, това е повече от добро решение, но не и за прост потребителкойто иска комфорт, удобство и изобщо не иска да разбере и усети системата, с която работи в момента. Това не е съвсем вярно. Персонализираната Linux система с графичен интерфейс е лесна за използване и интуитивна като операционна система на Microsoft. Просто настройването на Linux изисква много усилия и знания.

В резултат на тези характеристики на своето създаване и развитие, Linux придоби много специфични „черти на характера“. От една страна, това е типична UNIX система, многопотребителска и многозадачна. От друга страна, има типична система от хакери, студенти и изобщо всякакви хора, които обичат непрекъснато да учат и разбират всичко до най-малкия детайл. Гъвкавостта на настройката и използването на Linux вероятно няма равна. Можете да го използвате на нивото, на което работи win95 - тоест да имате графичен десктоп с всички негови функции под Windows: икони, лента на задачите, контекстно менюи т.н. Освен това можете да инсталирате работен плот, който няма да се различава по нищо външен види функции от Windows. (Най-общо казано, опции мениджъри на прозорципод Linux просто няма край, от супер-спартанския icewm до супер-сложния Enlightment + Gnome). От друга страна, Linux ви дава безпрецедентен достъп до хардуера на всяко ниво на достъпност. Вярно, за това няма да е достатъчно да можете да пляскате с десния бутон на мишката; ще трябва да научите SI и компютърната архитектура. Но човек, който веднъж е усетил тази миризма на мисъл, това вдъхновение на програмист, когато държите машината „за ушите“ и можете да правите с нея буквално всичко, на което е способна - такъв човек никога няма да може да се върне меките лапи на Windows.

Ако при използване на търговска операционна система потребителят е принуден да изчака пускането на следващата версия, за да получи система без проблеми и грешки от предишната версия, тогава модулността на Linux ви позволява да изтеглите ново ядро, който се пуска поне веднъж на два месеца или дори по-често (стабилна версия) .

Отговори на въпроса "Какво е Linux?" можете да намерите много. Много хора вярват, че Linux е само ядро. Но само ядрото не е от полза за потребителя. Въпреки че ядрото несъмнено е основата на операционната система Linux, потребителят трябва да работи с приложни програми през цялото време. Тези програми са не по-малко важни от ядрото. Следователно Linux е колекция от ядро ​​и основни приложни програми, които обикновено се инсталират на всеки компютър с тази операционна система. Комбинацията от ядрото и приложните програми в едно цяло е отразено и в името на системата: GNU/Linux. GNU е проект за създаване на набор от програми, подобни на това, което обикновено придружава Unix-подобна система.

Често срещано оплакване на поддръжниците на Linux е, че когато говорят за предимствата на Linux, те изброяват недостатъците на Windows. Но това често е неизбежно, тъй като всичко се научава чрез сравнение и повечето компютърни потребители вече са запознати само с Windows. И така, какво ви дава Linux?

Оригинал: Леки процеси: Дисекция на Linux нишки
От: Vishal Kanaujia, Chetan Giridhar
Дата на публикуване: 1 август 2011 г
Превод: А. Панин
Дата на публикуване на превода: 22 октомври 2012 г

Тази статия, предназначена за разработчици на Linux и студенти по компютърни науки, обхваща основите на нишките и тяхното внедряване с помощта на леки процеси в Linux, с примери за изходен код за по-добро разбиране.

Програмните нишки са основният елемент на многозадачна софтуерна среда. Програмната нишка може да бъде описана като среда за изпълнение на процес; следователно всеки процес има поне една нишка. Многонишковостта включва наличието на множество паралелни (на многопроцесорни системи) и обикновено синхронизирани среди за изпълнение на процес.

Програмните нишки имат свои собствени идентификатори (ID на нишка) и могат да се изпълняват независимо една от друга. Те споделят едно нещо помежду си адресно пространствопроцес и да използват тази функция като предимство, което им позволява да не използват IPC канали (междупроцесни комуникационни системи - споделена памет, канали и други системи) за обмен на данни. Нишките на процес могат да си взаимодействат - например независими нишки могат да получат/променят стойността на глобална променлива. Този комуникационен модел елиминира допълнителните разходи за IPC повиквания на ниво ядро. Тъй като нишките работят в едно адресно пространство, превключванията на контекста на нишките са бързи и не изискват много ресурси.

Всяка програмна нишка може да се обработва индивидуално от планировчика на задачи, така че многонишковите приложения са много подходящи за паралелно изпълнение на многопроцесорни системи. Освен това създаването и унищожаването на нишки е бързо. За разлика от повикването fork(), нишката не създава копие на адресното пространство на родителския процес, вместо това нишките споделят адресното пространство заедно с други ресурси, включително манипулатори на файлове и манипулатори на сигнали.

Многопоточното приложение използва ресурсите оптимално и възможно най-ефективно. В такова приложение програмните нишки се справят с различни задачи, като се има предвид оптималното използване на системата. Една нишка може да чете файл от диск, друга може да записва данни в сокет. И двете нишки ще работят в тандем, като са независими една от друга. Този модел оптимизира използването на системата, като по този начин подобрява производителността.

Няколко интересни функции

Най-известната функция при работа с нишки е тяхната синхронизация, особено когато има споделен ресурс, маркиран като критичен сегмент. Това е сегмент от код, който има достъп до споделен ресурс и не трябва да бъде достъпен от повече от една нишка по всяко време. Тъй като всяка нишка може да се изпълнява независимо, достъпът до споделения ресурс не се контролира, което води до необходимостта от използване на примитиви за синхронизация, включително мутекси (взаимно изключване), семафори, брави за четене/запис и други.

Тези примитиви позволяват на програмистите да контролират достъпа до споделен ресурс. В допълнение към горното, нишките, подобно на процесите, са податливи на влизане в състояния на блокиране или изчакване, ако моделът на синхронизация не е проектиран правилно. Отстраняването на грешки и анализирането на многонишкови приложения също може да бъде доста тромаво.

Как се реализират нишките в Linux?

Linux ви позволява да разработвате и използвате многонишкови приложения. На потребителско ниво внедряването на нишки в Linux следва отворения стандарт POSIX (интерфейс на преносима операционна система за uniX). Unix системи), обозначен като IEEE 1003. Библиотеката на потребителско ниво (glibc.so на Ubuntu) осигурява изпълнение на POSIX API за нишки.

В Linux програмните нишки съществуват в две отделни пространства - потребителско пространство и пространство на ядрото. В потребителското пространство нишките се създават с помощта на API, съвместим с POSIX pthread библиотека. Тези нишки на потребителското пространство са неразривно свързани с нишките на пространството на ядрото. В Linux нишките на пространството на ядрото се третират като "леки процеси". Лекият процес е единица от основната среда за изпълнение. За разлика от различните варианти на UNIX, включително системи като HP-UX и SunOS, Linux няма отделна система за нишки. Процес или нишка в Linux се третира като "задача" и използва същите вътрешни структури (серия от структури struct task_structs).

За редица нишки на процеси, създадени в потребителското пространство, има редица леки процеси, свързани с тях в ядрото. Един пример илюстрира тази точка: #include #включи #включи Int main() ( pthread_t tid = pthread_self(); int sid = syscall(SYS_gettid); printf("LWP id е %dn", sid); printf("POSIX нишка id е %dn", tid); return 0; )

Използвайки помощната програма 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 клонинг. s

Какво представляват леките процеси?

Лекият процес е процес, който поддържа нишката на потребителското пространство. Всяка нишка на потребителското пространство е неразривно свързана с лек процес. Процедурата за създаване на лек процес е различна от процедурата за създаване на обикновен процес; Потребителският процес "P" може да има редица свързани олекотени процеси със същия идентификатор на група. Групирането позволява на ядрото да разделя ресурси (ресурси включват адресно пространство, страници физическа памет(VM), манипулатори на сигнали и файлови дескриптори). Това също така позволява на ядрото да избягва контекстни превключвания, когато работи с тези процеси. Изчерпателното споделяне на ресурси е причината тези процеси да се наричат ​​леки.

Как Linux създава леки процеси?

IN Създаване на Linuxолекотените процеси се изпълняват с помощта на нестандартизираното системно извикване clone(). Подобно е на fork(), но с повече функционалност. По принцип извикването на fork() се реализира чрез извикване на clone() с допълнителни параметри, указващи ресурси, които ще бъдат споделени между процесите. Извикването на clone() създава процес, като детето споделя елементи на времето за изпълнение с родителя, включително памет, манипулатори на файлове и манипулатори на сигнали. Библиотеката pthread също използва извикването clone() за внедряване на нишки. Вижте файла с изходния код ./nptl/sysdeps/pthread/createthread.cв директорията с изходния код на glibc версия 2.11.2.

Създаване на ваш собствен лек процес

Нека демонстрираме пример за използване на извикването clone(). Вижте изходен кодот файла demo.c по-долу:

#включи #включи #включи #включи #включи #включи #включи //размер на стека 64kB #define STACK 1024*64 // Дъщерната нишка ще изпълни тази функция int threadFunction(void* аргумент) ( printf("влизане на дъщерна нишка\n"); close((int*)аргумент); printf( "излизане на дъщерна нишка\n"); return 0; ) int main() ( void* stack; pid_t pid; int fd; fd = open("/dev/null", O_RDWR); if (fd< 0) { perror("/dev/null"); exit(1); } // Резервирование памяти для стека stack = malloc(STACK); if (stack == 0) { perror("malloc: could not allocate stack"); exit(1); } printf("Creating child thread\n"); // Вызов clone() для создания дочернего потока pid = clone(&threadFunction, (char*) stack + STACK, SIGCHLD | CLONE_FS | CLONE_FILES |\ CLONE_SIGHAND | CLONE_VM, (void*)fd); if (pid == -1) { perror("clone"); exit(2); } // Ожидание завершения дочернего потока pid = waitpid(pid, 0, 0); if (pid == -1) { perror("waitpid"); exit(3); } // Попытка записи в файл закончится неудачей, так как поток // закрыл файл if (write(fd, "c", 1) < 0) { printf("Parent:\t child closed our file descriptor\n"); } // Освободить память, используемую для стека free(stack); return 0; }

Програмата demo.c ви позволява да създавате нишки по същество по същия начин като библиотеката pthread. Директното използване на clone() обаче не е препоръчително, защото ако се използва неправилно, разработваното приложение може да се провали. Синтаксисът на функцията clone() в Linux е даден по-долу: #include int клонинг (int (*fn) (void *), void *child_stack, int флагове, void *arg);

Първият аргумент е функцията на нишката; извиква се при стартиране на нишката. След като извикването на clone() завърши успешно, функцията fn започва да се изпълнява едновременно с процеса на извикване.

Следващият аргумент е указател към място в паметта за стека на дъщерния процес. Стъпката преди извикването на fork() и clone() изисква от програмиста да резервира памет и да предаде указател за използване като стек на дъщерния процес, тъй като родителският и дъщерният процес споделят страници с памет - те включват стека. Дъщерен процес може да извика функция, различна от родителския процес, поради което е необходим отделен стек. В нашата програма запазваме тази част от паметта в купчината с помощта на функцията malloc(). Размерът на стека беше зададен на 64 KB. Тъй като стекът x86 расте надолу, е необходимо да се симулира подобно поведение чрез използване на разпределена памет от края на секцията. Поради тази причина предаваме следния адрес на функцията clone(): (char*) stack + STACK

Следващият аргумент за знамена е особено важен. Тя ви позволява да посочите кои ресурси да се споделят със създадения процес. Ние избрахме битовата маска SIGCHLD | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_VMописано по-долу:

  • SIGCHLD: Нишката изпраща сигнала SIGCHLD към родителския процес след завършване. Задаването на тази опция позволява на родителския процес да използва функцията wait(), за да изчака завършването на всички нишки.
  • CLONE_FS : Споделете информация за файловата система между родителския процес и нишката. Информацията включва root файлова система, работна директория и стойност на umask.
  • CLONE_FILES : Споделете таблицата с файлови дескриптори между родителския процес и нишката. Промените в таблицата се отразяват в родителския процес и всички нишки.
  • CLOSE_SIGHAND : Споделете таблицата на манипулатора на сигнали между родителските и дъщерните процеси. Отново, ако родителският процес или една от нишките промени манипулатора на сигнала, промяната ще бъде отразена в таблиците на другите процеси.
  • CLONE_VM: Родителски процес и нишки се изпълняват в едно и също пространство на паметта. Всички записи в паметта или картографиране, направени от един от тях, са достъпни за други процеси.

Последният параметър е аргументът, предаден на функцията (threadFunction), в нашия случай файлов дескриптор.

Моля, вижте примера за работа с леки процеси demo.c, който публикувахме по-рано.

Нишката затваря файла (/dev/null), отворен от родителския процес. Тъй като родителският процес и нишката споделят таблицата с файлови дескриптори, операцията по затваряне на файла засяга и родителския процес, което води до неуспех на последващите извиквания write(). Родителският процес чака нишката да приключи (в момента, в който получи сигнала SIGCHLD). След това освобождава запазена памети връща контрола.

Компилирайте и стартирайте програмата както обикновено; изходът трябва да бъде подобен на този по-долу: $gcc demo.c $./a.out Създаване на дъщерна нишка дъщерна нишка влизане в дъщерна нишка излизане Родител: дете затвори нашия файлов дескриптор $

Linux осигурява поддръжка за ефективна, проста и мащабируема нишкова инфраструктура. Това насърчава програмистите да експериментират и да разработват библиотеки с нишки, които използват clone() като основна функция.

Продължаваме темата за многопоточността в ядрото на Linux. Последният път, когато говорих за прекъсвания, тяхната обработка и тасклети, и тъй като първоначално беше замислено това да е една статия, в моята история за workqueue ще се позова на тасклети, ако приемем, че читателят вече е запознат с тях.
Както миналия път, ще се опитам да направя историята си възможно най-подробна и подробна.

Статии от поредицата:

  1. Многозадачност в ядрото на Linux: работна опашка

Работна опашка

Работна опашка- това са по-сложни и тежки единици от tasklets. Дори няма да се опитвам да опиша всички тънкости на изпълнението тук, но се надявам да анализирам най-важните неща повече или по-малко подробно.
Workqueues, подобно на tasklets, служат за обработка на отложено прекъсване (въпреки че могат да се използват и за други цели), но за разлика от tasklets, те се изпълняват в контекста на процес на ядрото; съответно не е необходимо да са атомарни и могат да използват спящ режим (), различни инструменти за синхронизиране и др.

Нека първо разберем как като цяло е организиран процесът на обработка на работната опашка. Картината го показва много приблизително и опростено, как всъщност се случва всичко е описано подробно по-долу.

Няколко субекта участват в тази тъмна материя.
първо, работен елемент(накратко работа) е структура, която описва функцията (например манипулатор на прекъсвания), която искаме да планираме. Може да се разглежда като аналог на структурата на тасклета. При планирането Tasklets бяха добавени към опашки, скрити от потребителя, но сега трябва да използваме специална опашка - работна опашка.
Задачите се рейкват от функцията за планиране, а работната опашка се обработва от специални нишки, наречени работници.
работникосигуряват асинхронно изпълнение на работа от работната опашка. Въпреки че наричат ​​работа по ред на ротация, в общия случай не става дума за стриктно, последователно изпълнение: в края на краищата тук се извършват изпреварване, сън, изчакване и т.н.

По принцип работниците са нишки на ядрото, т.е. те се контролират от основния планировчик на ядрото на Linux. Но работниците частично се намесват в планирането на допълнителна организация на паралелното изпълнение на работата. Това ще бъде разгледано по-подробно по-долу.

За да очертая основните възможности на механизма на работната опашка, предлагам да проучите API.

За опашката и нейното създаване

alloc_workqueue(fmt, флагове, max_active, аргументи...)
Параметрите fmt и args са форматът printf за името и аргументите към него. Параметърът max_activate отговаря за максималния брой работи, които от тази опашка могат да бъдат изпълнени паралелно на един CPU.
Опашка може да бъде създадена със следните флагове:
  • WQ_HIGHPRI
  • WQ_UNBOUND
  • WQ_CPU_INTENSIVE
  • WQ_FREEZABLE
  • WQ_MEM_RECLAIM
Особено внимание трябва да се обърне на знамето WQ_UNBOUND. Въз основа на наличието на този флаг, опашките се разделят на свързани и необвързани.
В свързани опашкиКогато се добавят, работата е обвързана с текущия процесор, т.е. в такива опашки работата се изпълнява в ядрото, което я планира. В това отношение обвързаните опашки приличат на tasklets.
В необвързани опашкиработата може да се изпълнява на всяко ядро.

Важна характеристика на реализацията на workqueue в ядрото на Linux е допълнителната организация на паралелното изпълнение, която присъства в обвързаните опашки. По-подробно е написано по-долу, но сега ще кажа, че се извършва по такъв начин, че да се използва възможно най-малко памет и така че процесорът да не стои празен. Всичко това се реализира с предположението, че една работа не използва твърде много процесорни цикли.
Това не е случаят с неприкрепените опашки. По същество такива опашки просто предоставят контекст на работниците и ги стартират възможно най-рано.
По този начин трябва да се използват неприкрепени опашки, ако се очаква интензивно натоварване на процесора, тъй като в този случай планировчикът ще се погрижи за паралелното изпълнение на множество ядра.

По аналогия със задачите, на работите може да се присвои приоритет на изпълнение, нормален или висок. Приоритетът е общ за цялата опашка. По подразбиране опашката има нормален приоритет и ако зададете флага WQ_HIGHPRI, тогава, съответно, високо.

Флаг WQ_CPU_INTENSIVEима смисъл само за обвързани опашки. Този флаг е отказ от участие в допълнителна организация на паралелно изпълнение. Този флаг трябва да се използва, когато се очаква работата да изразходва много процесорно време, в който случай е по-добре да прехвърлите отговорността на планировчика. Това е описано по-подробно по-долу.

Знамена WQ_FREEZABLEИ WQ_MEM_RECLAIMса специфични и извън рамките на темата, затова няма да се спираме подробно на тях.

Понякога има смисъл да не създавате свои собствени опашки, а да използвате общи. Основните от тях:

  • system_wq - обвързана опашка за бърза работа
  • system_long_wq - обвързана опашка за работи, чието изпълнение се очаква да отнеме много време
  • system_unbound_wq - необвързана опашка

За работата и планирането им

Сега нека се заемем с произведенията. Първо, нека да разгледаме макросите за инициализация, декларация и подготовка:
DECLARE(_DELAYED)_WORK(име, void (*функция)(struct work_struct *work)); /* по време на компилиране */ INIT(_DELAYED)_WORK(_work, _func); /* по време на изпълнение */ PREPARE(_DELAYED)_WORK(_work, _func); /* за промяна на изпълняваната функция */
Работите се добавят към опашката с помощта на функциите:
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); /* работата ще бъде добавена към опашката само след изтичане на забавянето */
На това си струва да се спрем по-подробно. Въпреки че посочваме опашка като параметър, всъщност работата не се поставя в самата работна опашка, както може да изглежда, а в съвсем различен обект - в списъка с опашки на структурата worker_pool. Структура пул_работницивсъщност е най-важният субект в организирането на механизма на работната опашка, въпреки че за потребителя остава зад кулисите. Именно с тях работят работниците и именно в тях се съдържа цялата основна информация.

Сега нека видим какви пулове има в системата.
Като начало, пулове за обвързани опашки (на снимката). За всеки CPU статично се разпределят два работни пула: единият за работа с висок приоритет, другият за работа с нормален приоритет. Тоест, ако имаме четири ядра, тогава ще има само осем прикачени пула, въпреки факта, че работната опашка може да бъде колкото желаете.
Когато създаваме работна опашка, тя има услуга, разпределена за всеки процесор pool_workqueue(pwq). Всеки такъв pool_workqueue е свързан с работен пул, който е разпределен на същия CPU и съответства по приоритет на типа опашка. Чрез тях работната опашка взаимодейства с групата работници.
Работниците изпълняват работа от групата работници безразборно, без да разграничават към коя работна опашка са принадлежали първоначално.

За неприкрепени опашки работните групи се разпределят динамично. Всички опашки могат да бъдат разделени на класове на еквивалентност според техните параметри, като за всеки такъв клас се създава собствен пул от работници. Достъпът до тях се осъществява чрез специална хеш-таблица, където ключът е набор от параметри, а стойността, съответно, е работният пул.
Всъщност за необвързани опашки всичко е малко по-сложно: ако за обвързани опашки pwq и опашки са създадени за всеки процесор, тук те се създават за всеки NUMA възел, но това е допълнителна оптимизация, която няма да разгледаме подробно.

Всякакви дреболии

Ще дам и няколко функции от API, за да допълня картината, но няма да говоря за тях в подробности:
/* Принудително завършване */ bool flush_work(struct work_struct *work); bool flush_delayed_work(struct delayed_work *dwork); /* Отмяна на изпълнението на работа */ 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);

Как работниците си вършат работата

Сега, след като сме запознати с API, нека се опитаме да разберем по-подробно как всичко работи и се управлява.
Всеки пул има набор от работници, които се справят със задачите. Освен това броят на работниците се променя динамично, адаптирайки се към текущата ситуация.
Както вече разбрахме, работниците са нишки, които извършват работа в контекста на ядрото. Работникът ги извлича в ред, един след друг, от групата работници, свързани с него, и работниците, както вече знаем, могат да принадлежат към различни опашки източници.

Работниците могат условно да бъдат в три логически състояния: те могат да бъдат бездействащи, работещи или управляващи.
Работник може бездействами не прави нищо. Това е например, когато цялата работа вече се изпълнява. Когато работник влезе в това състояние, той заспива и съответно няма да изпълнява, докато не бъде събуден;
Ако управлението на пула не е необходимо и списъкът с планирани работи не е празен, тогава работникът започва да ги изпълнява. Условно ще наричаме такива работници бягане.
Ако е необходимо, работникът поема ролята мениджърбасейн. Един пул може да има или само един управляващ работник, или изобщо да няма работник. Неговата задача е да поддържа оптимален брой работници на пул. Как го прави? Първо се изтриват работници, които са били бездействащи дълго време. Второ, нови работници се създават, ако са изпълнени три условия наведнъж:

  • все още има задачи за изпълнение (работи в пула)
  • без празни работници
  • няма работещи работници (т.е. активни и не спящи)
Последното условие обаче има свои собствени нюанси. Ако опашките на пула са неприкрепени, тогава работещите работници не се вземат предвид за тях това условие винаги е вярно. Същото важи и в случай на работник, изпълняващ задача от свързана, но с флага WQ_CPU_INTENSIVE, опашки. Освен това, в случай на обвързани опашки, тъй като работниците работят с произведения от общия пул (който е един от двата за всяко ядро ​​на снимката по-горе), се оказва, че някои от тях се отчитат като работещи, а други не. От това следва също, че извършването на работа от WQ_CPU_INTENSIVEопашките може да не започнат веднага, но самите те не пречат на изпълнението на друга работа. Сега трябва да е ясно защо този флаг се нарича така и защо се използва, когато очакваме работата да отнеме много време за завършване.

Отчитането на работещите работници се извършва директно от основния планировчик на ядрото на Linux. Този контролен механизъм осигурява оптимално ниво на паралелност, предотвратявайки създаването на твърде много работници в работната опашка, но също така не кара работата да чака ненужно твърде дълго.

Който се интересува може да разгледа работната функция в ядрото, тя се казва worker_thread().

Всички описани функции и структури могат да бъдат намерени по-подробно във файловете include/linux/workqueue.h, ядро/workqueue.cИ kernel/workqueue_internal.h. Има и документация за работна опашка в Документация/workqueue.txt.

Също така си струва да се отбележи, че механизмът на работната опашка се използва в ядрото не само за обработка на отложено прекъсване (въпреки че това е доста често срещан сценарий).

По този начин разгледахме механизмите за обработка на отложено прекъсване в ядрото на Linux - tasklet и workqueue, които са специална форма на многозадачност. Можете да прочетете за прекъсванията, задачите и работните опашки в книгата „Драйвери на устройства за Linux“ от Джонатан Корбет, Грег Кроа-Хартман, Алесандро Рубини, въпреки че информацията там понякога е остаряла.

ЛАБОРАТОРНА РАБОТА №3

МНОГОЗАДАЧНО ПРОГРАМИРАНЕ ВLINUX

1. Цел на работата:Запознайте се с компилатора gcc, техниките за отстраняване на грешки в програмата и функциите за работа с процеси.

2. Кратка теоретична информация.

Минималният набор от превключватели на gcc компилатора са - Wall (показва всички грешки и предупреждения) и - o (изходен файл):

gcc - Стена - 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/типове. з).

За да създадете нов процес, използвайте функцията fork:

pid_t fork(void)

Чрез вмъкване на забавяне с произволна дължина, използвайки функциите за заспиване и ранд, можете да видите по-ясно ефекта от многозадачността:

това ще накара програмата да "заспи" за произволен брой секунди: от 0 до 3.

За да извикате функция като дъщерен процес, просто я извикайте след разклоняване:

// ако се изпълнява дъщерен процес, извикайте функцията

pid=процес(арг);

// изход от процеса

Често е необходимо да стартирате друга програма като дъщерен процес. За да направите това, използвайте функциите на семейството exec:

// ако се изпълнява дъщерен процес, извикайте програмата


if (execl("./file","file",arg, NULL)<0) {

printf("ГРЕШКА при стартиране на процес\n");

else printf("процесът стартира (pid=%d)\n", pid);

// изход от процеса

Често родителският процес трябва да обменя информация със своите деца или поне да се синхронизира с тях, за да извършва операции в точното време. Един от начините за синхронизиране на процесите е с функциите wait и waitpid:

#включи

#включи

pid_t wait(int *status) - спира изпълнението на текущия процес, докато някой от неговите дъщерни процеси не приключи.

pid_t waitpid (pid_t pid, int *status, int options) - спира изпълнението на текущия процес, докато посоченият процес завърши или провери за завършване на посочения процес.

Ако трябва да разберете състоянието на дъщерния процес, когато той приключи и стойността, която връща, тогава използвайте макроса WEXITSTATUS, като му предадете състоянието на дъщерния процес като параметър.

status=waitpid(pid,&status, WNOHANG);

ако (pid == състояние) (

printf("PID: %d, Резултат = %d\n", pid, WEXITSTATUS(състояние)); )

За да промените приоритетите на създадените процеси, се използват функциите setpriority и . Приоритетите са зададени в диапазона от -20 (най-висок) до 20 (най-нисък), нормалната стойност е 0. Имайте предвид, че само суперпотребител може да увеличи приоритета над нормалния!

#включи

#включи

int процес(int i) (

setpriority(PRIO_PROCESS, getpid(),i);

printf("Процес %d ThreadID: %d работи с приоритет %d\n",i, getpid(),getpriority(PRIO_PROCESS, getpid()));

return(getpriority(PRIO_PROCESS, getpid()));

За да убиете процес, използвайте функцията kill:

#включи

#включи

int kill(pid_t pid, int sig);

Ако pid > 0, тогава той определя PID на процеса, към който е изпратен сигналът. Ако pid = 0, тогава сигналът се изпраща до всички процеси от групата, към която принадлежи текущият процес.

sig - тип сигнал. Някои видове сигнали в Linux:

SIGKILL Този сигнал кара процеса да прекрати незабавно. Процесът не може да игнорира този сигнал.

SIGTERM Този сигнал е искане за прекратяване на процеса.

SIGCHLD Системата изпраща този сигнал към процес, когато един от неговите дъщерни процеси приключи. Пример:

if (pid[i] == състояние) (

printf("ThreadID: %d завърши със състояние %d\n", pid[i], WEXITSTATUS(статус));

else kill(pid[i],SIGKILL);

3. Методически указания.

3.1. За да се запознаете с опциите на компилатора на gcc и описанията на функциите на езика C, използвайте инструкциите за човек и информация.

3.2. За отстраняване на грешки в програми е удобно да използвате вградения редактор на файловия мениджър Midnight Commander (MC), който подчертава различни езикови конструкции в цвят и показва позицията на курсора във файла (ред, колона) в горния ред на екрана.

3.3. Файловият мениджър на Midnight Commander има команден буфер, който може да бъде извикан чрез клавишна комбинация - H, който може да се мести с помощта на стрелките на курсора (нагоре и надолу). За да вмъкнете команда от буфера в командния ред, използвайте клавиша , за редактиране на команда от буфера - ключове<- и ->, И .


3.4. Не забравяйте, че текущата директория не се съдържа в path, така че от команден редтрябва да стартирате програмата като "./print_pid". В MC просто задръжте курсора на мишката над файла и щракнете .

3.5. За да видите резултата от изпълнението на програмата, използвайте клавишната комбинация - О. Работят и в режим на редактиране на файлове.

3.6. За да регистрирате резултатите от изпълнението на програмата, препоръчително е да използвате пренасочване на изхода от конзолата към файл: ./test > result. txt

3.7. За достъп до файлове, създадени на Linux сървър, кандидатствайте ftp протокол, клиентска програмакойто е наличен в Windows 2000 и е вграден в файлов мениджърДАЛЕЧ. В същото време сметкаи паролата е същата като при свързване през 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. Характеристики на компилиране и изпълнение на C програми на Linux.

7.2. Какво е pid, как да го определите в операционната система и програмата?

7.3. Функция Fork – предназначение, приложение, връщана стойност.

7.4. Как да стартирате функция в създаден процес? програма?

7.5. Начини за синхронизиране на родителски и дъщерни процеси.

7.6. Как мога да разбера състоянието на създадения процес, когато той приключи, и стойността, която връща?

7.7. Как да управлявате приоритетите на процеса?

7.8. Как да убия процес в операционната система и програмата?

Въведете следната команда във вашата обвивка:

−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

На екрана ще се покаже списък с всички процеси, изпълнявани в системата. Ако искате да преброите броя на процесите, напишете нещо подобно:

−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

$ ps -e --no-headers | nl | опашка -n 1

74 4650 точки/0 00:00:00 опашка

−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

Първото число е броят на процесите, изпълнявани в системата. Потребителите на KDE могат да използват програмата kpm, а потребителите на Gnome могат да използват програмата gnome-system-monitor, за да получат информация за процесите. Това е целта на Linux, за да позволи на потребителя да прави същото по различни начини.

Възниква въпросът: "Какво е процес?" Процесите в Linux, подобно на файловете, са аксиоматични понятия. Понякога процесът се идентифицира с работеща програматова обаче не винаги е така. Да приемем, че процесът е работеща единица на система, която прави нещо. Многозадачността е способността няколко процеса да съществуват едновременно в една система.

Linux е многозадачна операционна система. Това означава, че процесите в него работят едновременно. Естествено, това е условна формулировка. Ядрото на Linux непрекъснато превключва процесите, тоест от време на време дава на всеки от тях малко процесорно време. Превключването става доста бързо, така че ни се струва, че процесите се изпълняват едновременно.

Някои процеси могат да генерират други процеси, образувайки дървовидна структура. Произвеждащите процеси се наричат ​​родители или родителски процеси, а децата се наричат ​​деца или дъщерни процеси. В горната част на това „дърво“ е процесът на стартиране, който се заражда автоматично от ядрото по време на процеса на зареждане на системата.

Всеки процес в системата е свързан с двойка неотрицателни цели числа: идентификатор на процес PID (Process IDentifier) ​​​​и идентификатор на родителски процес PPID (Parent Process IDentifier). За всеки процес PID е уникален (в определен момент от време) и PPID е равен на ID на родителския процес. Ако въведете командата ps -ef в обвивката, ще се покаже списък с процеси със стойностите на техните PID и PPID (съответно втора и трета колона). Пример за тази команда:

−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

UID PID PPID C STIME TTY TIME CMD

корен 1 0 0 17:08? 00:00:00 /sbin/init

корен 2 0 0 17:08? 00:00:00

корен 3 2 0 17:08? 00:00:00

корен 4 2 0 17:08? 00:00:00

корен 5 2 0 17:08? 00:00:00

корен 6 2 0 17:08? 00:00:00

корен 7 2 0 17:08? 00:00:00

корен 8 2 0 17:08? 00:00:00

корен 9 2 0 17:08? 00:00:00

корен 10 2 0 17:08? 00:00:00

корен 11 2 0 17:08? 00:00:00

корен 12 2 0 17:08? 00:00:00

корен 13 2 0 17:08? 00:00:00

корен 14 2 0 17:08? 00:00:00

корен 15 2 0 17:08? 00:00:00

корен 16 2 0 17:08? 00:00:00

корен 17 2 0 17:08? 00:00:00

корен 18 2 0 17:08? 00:00:00

корен 19 2 0 17:08? 00:00:00

df00 16389 16387 0 20:10 точки/1 00:00:00 /bin/bash

df00 17446 2538 0 20:26? 00:00:00

df00 18544 2932 0 20:41 точки/2 00:00:00 /bin/bash -l

df00 19010 18544 0 20:48 точки/2 00:00:00 ps -ef

−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

Трябва да се отбележи, че процесът на init винаги има идентификатор 1 и PPID 0. Въпреки че в действителност няма процес с идентификатор 0. Дървото на процеса може също да се визуализира с помощта на опцията --forest на програмата ps:

−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

2? 00:00:00 kthread

3 ?00:00:00 \_ миграция/0

4 ?00:00:00 \_ ksoftirqd/0

5 ?00:00:00 \_ пазач/0

6 ?00:00:00 \_ миграция/1

7 ?00:00:00 \_ ksoftirqd/1

8 ?00:00:00 \_ пазач/1

9 ?00:00:00 \_ събития/0

10 ?00:00:00 \_ събития/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 точки/2 00:00:00 \_ bash

16388 ?00:00:00 \_ gnome-pty-helpe

16389 точки/1 00:00:00 \_ bash

−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

Ако извикате програмата ps без аргументи, ще се покаже списък с процеси, принадлежащи към текущата група, тоест изпълняващи се под текущия терминал.

Използване на getpid() и getppid()

Процесът може да открие своя PID, както и своя родителски PPID, използвайки системните извиквания getpid() и getppid().

Системните извиквания getpid() и getppid() имат следните прототипи:

−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

pid_t getpid(void);

pid_t getppid(void);

−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

За да използвате getpid() и getppid(), заглавните файлове unistd.h и sys/types.h (за типа pid_t) трябва да бъдат включени в програмата с помощта на директивата #include. Извикването getpid() връща идентификатора на текущия процес (PID), а getppid() връща родителския идентификатор (PPID). pid_t е целочислен тип, чието измерение зависи от конкретната система. Стойностите от този тип могат да се оперират като редовни цели числа от тип int.

Нека сега да разгледаме проста програма, който показва PID и PPID и след това замръзва, докато потребителят не натисне .

−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

#включи

#включи

#включи

pid_t pid, ppid;

pid = getpid();

ppid = getppid();

printf("PID: %d\n", pid);

printf("PPID: %d\n", ppid);

fprintf(stderr, "Натисни за излизане...");

−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

Нека сега да проверим как работи тази програма. За да направите това, нека го компилираме и стартираме:

−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

$ gcc -o getpid getpid.c

Натиснете за излизане...

−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

Сега, без да натискате , отворете друг терминален прозорец и проверете дали системните извиквания getpid() и getppid() работят правилно:

−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

$ ps -ef | grep getpid

df00 19724 19702 0 20:58 точки/3 00:00:00 ./главен

df00 19856 18544 0 21:00 точки/2 00:00:00 grep --colour=auto main

−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−



 


Прочетете:



Всички цветове на калъфа Galaxy S8 и кой е по-добър за закупуване?

Всички цветове на калъфа Galaxy S8 и кой е по-добър за закупуване?

В края на март 2017 г. Samsung представи нови флагмански устройства – смартфоните Samsung Galaxy S8 и Galaxy S8+. Ключът им...

Mikrotik hAP AC - Рутер за всички случаи Преди да започнете да тествате

Mikrotik hAP AC - Рутер за всички случаи Преди да започнете да тествате

Рутерите Mikrotik отдавна си останаха устройства за професионалисти, но с разрастването на функционалността на RouterOS се разви и уеб конфигураторът...

Как най-добре да изчислим басрефлекса за акустична система

Как най-добре да изчислим басрефлекса за акустична система

Корпус за субуфер - бас рефлекс (FI) Като част от обсъждането на избора на субуфер, ще разгледаме такъв случай като бас рефлекс. Бас рефлекс, за разлика от...

Технология Thunderbolt: как работи и какви са нейните предимства

Технология Thunderbolt: как работи и какви са нейните предимства

Мисля, че почти всички знаете, че има такъв интерфейс като Thunderbolt 3 (TB3). Това е най-новата версия на Thunderbolt. Първа версия на TB...

feed-image RSS