<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>O Torvalds Linus</title>
	<atom:link href="http://otorvald.com/feed" rel="self" type="application/rss+xml" />
	<link>http://otorvald.com</link>
	<description>Сайт о *nix системах и всем что с ними связано</description>
	<lastBuildDate>Mon, 16 Apr 2012 23:04:19 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>Удаление процессов</title>
		<link>http://otorvald.com/udalenie-protsessov</link>
		<comments>http://otorvald.com/udalenie-protsessov#comments</comments>
		<pubDate>Mon, 26 Mar 2012 23:03:40 +0000</pubDate>
		<dc:creator>root</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[kernel]]></category>
		<category><![CDATA[thread]]></category>
		<category><![CDATA[процессы]]></category>

		<guid isPermaLink="false">http://otorvald.com/?p=64</guid>
		<description><![CDATA[Операционная система Unix позволяет процессу опрашивать ядро на предмет идентификатора процесса родителя или состояния любого своего потомка. Например, процесс может создать процесс &#8211; потомок для решения конкретной задачи, а затем вызвать какую-нибудь wait ()подобную библиотечную функцию, чтобы проверить, завершилось ли выполнение потомка. Если завершилось, код завершения позволит родителю судить об успешности решения задачи. Чтобы не [...]]]></description>
			<content:encoded><![CDATA[<p>Операционная система Unix позволяет процессу опрашивать ядро на предмет идентификатора процесса родителя или состояния любого своего потомка. Например, процесс может создать процесс &#8211; потомок для решения конкретной задачи, а затем вызвать какую-нибудь wait ()подобную библиотечную функцию, чтобы проверить, завершилось ли выполнение потомка. Если завершилось, код завершения позволит родителю судить об успешности решения задачи.</p>
<p>Чтобы не возникло противоречий с такими возможностями, ядрам Unix не разрешается уничтожать данные, содержащиеся в дескрипторе процесса, сразу после завершения процесса. Им можно сделать это только после того, как процесс &#8211; родитель сделает wait ()подобный системный вызов в отношении завершившегося процесса. Именно поэтому было введено состояние exit zombie: хотя в техническом смысле процесса больше нет, его дескриптор хранится, пока процесс &#8211; родитель не будет уведомлен.</p>
<p><span id="more-64"></span>Что происходит, если родитель завершает выполнение раньше своих потомков? В этом случае система может оказаться переполненной процессами &#8211; зомби, дескрипторы которых навсегда остались бы в оперативной памяти. Как было сказано ранее, эта проблема решается принудительным переводом всех осиротевших”процессов в потомки процесса init. Тогда процесс init уничтожит зомби при проверке завершения одного из своих законных потомков с помощью wait ()подобного системного вызова.</p>
<p>Функция reiease tasko отсоединяет последние структуры от дескриптора процесса &#8211; зомби. Она применяется к зомби одним из двух способов: либо с помощью функции do exito, если родитель не заинтересован в получении сигналов от потомка, либо с помощью системных вызовов wait4() или waitpid (), после того как сигнал был отправлен потомку. В последнем случае функция также произведет утилизацию памяти, занятой дескриптором процесса, а в первом —утилизация памяти будет произведена планировщиком. Эта функция выполняет следующие действия:</p>
<p>1. Уменьшает количество процессов, принадлежащих пользователю &#8211; владельцу завершившегося процесса. Это значение хранится в структуре user struct, упомянутой ранее в этой главе (см. шаг 4 функции сору_ process ()).</p>
<p>2. Если процесс отслеживается, функция удаляет его из списка ptrace_ children отладчика и возвращает оригинальному родителю.</p>
<p>3. Вызывает функцию exit signai о, чтобы отменить все ожидающие доставки сигналы и освободить дескриптор signai struct процесса. Если дескриптор больше не используется другими облегченными процессами, функция удаляет эту структуру. Кроме того, она вызывает функцию exit itimers (), чтобы отсоединить от процесса POSIX &#8211; таймер интервалов, если таковой имеется.</p>
<p>4. Вызывает функцию exit sighando, чтобы убрать обработчики сигналов.</p>
<p>5. Вызывает функцию unhash process (), которая:</p>
<p>•уменьшает переменную nr threads на 1;</p>
<p>•дважды вызывает функцию detach pid (), чтобы удалить дескриптор процесса из хеш &#8211; таблиц pidhash типа pidtype_pid и pidtype_tgid;</p>
<p>•если процесс является лидером группы, опять дважды вызывает функцию detach pid (), чтобы удалить дескриптор процесса из хеш &#8211; таблиц</p>
<p>PIDTYPE_PGID И PIDTYPE_SID;</p>
<p>•вызывает макрос remove links, чтобы удалить дескриптор процесса из списка процессов.</p>
<p>6. Если процесс не является лидером группы, значит, лидер уже стал процессом &#8211; зомби, и данный процесс является последним членом группы потоков.</p>
<p>Функция отправляет сигнал родителю лидера, чтобы уведомить его о &#8220;гибели”процесса.</p>
<p>7. Вызывает функцию sched exitо, чтобы отрегулировать отрезок времени процесса &#8211; родителя (этот шаг логически дополняет шаг 17 в описании функции copy_process ()).</p>
<p>8. Вызывает функцию put task structо, чтобы изменить счетчик обращений у дескриптора процесса. Если этот счетчик сравняется с нулем, функция отменяет все оставшиеся ссылки на процесс:</p>
<p>•уменьшает счетчик обращений (поле count) структуры user struct,</p>
<p>принадлежащей пользователю &#8211; владельцу процесса и освобождает эту структуру, если счетчик обращений становится равным 0;</p>
<p>•освобождает дескриптор процесса и область памяти, занимаемую дескриптором thread infо и стеком режима ядра.</p>
]]></content:encoded>
			<wfw:commentRss>http://otorvald.com/udalenie-protsessov/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Функция do_exit()</title>
		<link>http://otorvald.com/funktsiya-do_exit</link>
		<comments>http://otorvald.com/funktsiya-do_exit#comments</comments>
		<pubDate>Sun, 25 Mar 2012 23:02:26 +0000</pubDate>
		<dc:creator>root</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[функции]]></category>

		<guid isPermaLink="false">http://otorvald.com/?p=62</guid>
		<description><![CDATA[Любое завершение процесса выполняется функцией do exit (), которая удаляет большинство ссылок на завершающийся процесс из структур данных ядра. Функция do exit () принимает в качестве параметра код завершения процесса и выполняет следующие действия: 1. Устанавливает флаг pf exiting в поле flag дескриптора процесса, чтобы отметить тот факт, что процесс в данный момент удаляется. 2. [...]]]></description>
			<content:encoded><![CDATA[<p>Любое завершение процесса выполняется функцией do exit (), которая удаляет большинство ссылок на завершающийся процесс из структур данных ядра.</p>
<p>Функция do exit () принимает в качестве параметра код завершения процесса и выполняет следующие действия:</p>
<p><span id="more-62"></span>1. Устанавливает флаг pf exiting в поле flag дескриптора процесса, чтобы отметить тот факт, что процесс в данный момент удаляется.</p>
<p>2. Удаляет, если необходимо, дескриптор процесса из очереди динамического таймера, для чего вызывает функцию dei timer sync ().</p>
<p>3. Отсоединяет от дескриптора процесса структуры, имеющие отношение к выделению страниц, семафорам, файловой системе, дескрипторам открытых файлов, пространствам имен и битовой карте разрешений ввода/вывода, вызывая С ЭТОЙ целью функции exit_mm(), exit_sem(), exit_f iles (), exit_f s (), exit_namespace () и exit_thread () соответственно. Эти же функции удаляют перечисленные структуры, если другие процессы не пользуются ими совместно с уничтожаемым процессом.</p>
<p>4. Если функции ядра, реализующие область выполнения и поддержку исполняемого формата уничтожаемого процесса, входят в состав модулей ядра, функция уменьшает их счетчики обращений.</p>
<p>5. Записывает код завершения процесса в поле exit code дескриптора процесса. Это значение представляет собой либо параметр системного вызова</p>
<p>exit о или exit groupO (нормальное завершение), либо код ошибки, предоставленный ядром (аварийное завершение).</p>
<p>6. Вызывает функцию exit notifyO, которая выполняет следующие действия:</p>
<p>•обновляет отношения родитель &#8211; потомок”между соответствующими процессами. Все потомки, созданные уничтожаемым процессом, становятся потомками другого процесса в той же группе потоков, если таковой в данный момент выполняется. В противном случае они становятся потомками процесса init;</p>
<p>•убеждается, что поле exit signai дескриптора уничтожаемого процесса отлично от &#8211; 1, и что этот процесс является последним членом своей группы потоков (заметим, что эти условия всегда соблюдены у любого нормального процесса; см. шаг 16 в описании функции copy process о ранее в этой главе). В этом случае функция посылает сигнал (обычно sigchld) родителю уничтожаемого процесса, чтобы известить его о гибели”потомка;</p>
<p>•в противном случае, т. е. если поле exit signai содержит &#8211; 1, или группа потоков включает в себя другие процессы, функция отправляет сигнал sigchld родителю только в том случае, когда процесс отслеживается (в этой ситуации родителем является отладчик, который информируется о гибели”облегченного процесса);</p>
<p>•если поле exit signai дескриптора процесса равно &#8211; 1, а процесс не отслеживается, функция записывает в поле exit state дескриптора процесса значение exit dead и вызывает функцию reiease tasko для утилизации памяти, занимаемой оставшимися структурами данных процесса, и уменьшения счетчика обращений дескриптора процесса (см. следующий раздел). Счетчик обращений становится равным, так что сам дескриптор процесса пока не удаляется;</p>
<p>•если же поле exit signai дескриптора процесса не равно &#8211; 1, а процесс отслеживается, функция записывает в поле exit state значение exit zombie. В следующем разделе мы увидим, что происходит с про &#8211; цессами &#8211; зомби;</p>
<p>•устанавливает флаг pf dead в поле flags дескриптора процесса.</p>
<p>7. Вызывает функцию schedule о, чтобы выбрать, какой процесс будет выполняться следующим. Поскольку процесс в состоянии exit zombie игнорируется планировщиком, выполнение процесса прекращается сразу после вызова макроса switchto в функции schedule о. Как планировщик проверит флаг pf dead и уменьшит счетчик обращений в дескрипторе процесса &#8211; зомби, замещаемого другим процессом, чтобы отметить тот факт, что процесс больше не существует.</p>
]]></content:encoded>
			<wfw:commentRss>http://otorvald.com/funktsiya-do_exit/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Функция do_group_exit()</title>
		<link>http://otorvald.com/funktsiya-do_group_exit</link>
		<comments>http://otorvald.com/funktsiya-do_group_exit#comments</comments>
		<pubDate>Sat, 24 Mar 2012 23:01:14 +0000</pubDate>
		<dc:creator>root</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[функции]]></category>

		<guid isPermaLink="false">http://otorvald.com/?p=60</guid>
		<description><![CDATA[Функция do group exit о уничтожает все процессы, принадлежащие к группе потоков процесса current. Она принимает в качестве параметра код завершения процесса, который представляет собой либо значение, указанное в системном вызове exit group () (нормальное завершение), либо код ошибки, переданный ядром (аварийное завершение). Функция выполняет следующие действия: 1. Проверяет, установлен ли флаг signal group exit [...]]]></description>
			<content:encoded><![CDATA[<p>Функция do group exit о уничтожает все процессы, принадлежащие к группе потоков процесса current. Она принимает в качестве параметра код завершения процесса, который представляет собой либо значение, указанное в системном вызове exit group () (нормальное завершение), либо код ошибки, переданный ядром (аварийное завершение). Функция выполняет следующие действия:</p>
<p><span id="more-60"></span>1. Проверяет, установлен ли флаг signal group exit у завершающегося процесса, т. е. приступило ли уже ядро к процедуре завершения данной группы потоков. В этом случае функция считает кодом завершения значение, хранящееся в поле current -&gt;signal -&gt;group_exit_code.<br />
2. В противном случае функция устанавливает у процесса флаг signal_<br />
group exit и сохраняет код завершения в поле current -&gt;signai -&gt; g гoup_ex i t_c ode.<br />
3. Вызывает функцию zap other threads (), чтобы уничтожить остальные процессы в группе процесса current, если таковые имеются. С этой целью функция перебирает список идентификаторов процессов в хеш &#8211; таблице PIDTYPE TGID, определяемой полем current -&gt;tgid. Каждому процессу в списке, отличному от current, она посылает сигнал sigkill. В результате, все эти процессы, в конце концов, выполнят функцию do exit () И будут уничтожены.<br />
4. Вызывает функцию do exit (), передавая ей код завершения процесса. Как мы скоро увидим, функция do exit () уничтожает процесс и не возвращает управление.</p>
]]></content:encoded>
			<wfw:commentRss>http://otorvald.com/funktsiya-do_group_exit/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Другие потоки ядра</title>
		<link>http://otorvald.com/drugie-potoki-yadra</link>
		<comments>http://otorvald.com/drugie-potoki-yadra#comments</comments>
		<pubDate>Fri, 23 Mar 2012 22:59:39 +0000</pubDate>
		<dc:creator>root</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[kernel]]></category>
		<category><![CDATA[thread]]></category>

		<guid isPermaLink="false">http://otorvald.com/?p=58</guid>
		<description><![CDATA[Linux использует много других потоков ядра. Другие создаются &#8220;по требованию”, когда ядро должно выполнить задачу, для которой оптимальным является собственный контекст ядра. Примерами потоков ядра (помимо процесса 0 и процесса 1) являются: keventd (также называемый events) —выполняет функции из рабочей очереди keventd_wq (см. главу 4)\ kapmd —обрабатывает события, имеющие отношение к усовершенствованному управлению питанием (АРМ, [...]]]></description>
			<content:encoded><![CDATA[<p>Linux использует много других потоков ядра. Другие создаются &#8220;по требованию”, когда ядро должно выполнить задачу, для которой оптимальным является собственный контекст ядра.</p>
<p>Примерами потоков ядра (помимо процесса 0 и процесса 1) являются:</p>
<p>keventd (также называемый events) —выполняет функции из рабочей очереди keventd_wq (см. главу 4)\</p>
<p>kapmd —обрабатывает события, имеющие отношение к усовершенствованному управлению питанием (АРМ, Advanced Power Management);</p>
<p>pdflush —сбрасывает грязные”буферы на диск, чтобы утилизировать память;</p>
<p>kblockd —выполняет функции из рабочей очереди kblockd_workqueue. На практике этот поток ядра периодически активизирует драйверы блочных устройств, как описано в разд. ksoftirqd —выполняет тасклеты. У каждого процессора в системе есть один такой поток ядра.</p>
<p><span id="more-58"></span><strong>Уничтожение процессов</strong></p>
<p>Большинство процессов умирает”в том смысле, что они прекращают выполнять свой код. Память, открытые файлы и прочие вещи, упоминаемые в этой книге, например, семафоры.</p>
<p>Обычно, чтобы закончить свою работу, процесс вызывает библиотечную функцию exit (), которая освобождает ресурсы, выделенные библиотекой С, выполняет все функции, зарегистрированные программистом, и под конец делает системный вызов, удаляющий процесс из системы. Библиотечная функция exit о может быть вызвана программистом явно. Кроме того, компиляторе всегда ставит вызов exit о сразу после последнего оператора функции main ().</p>
<p>Завершение процесса</p>
<p>В Linux 2.6 существуют два системных вызова, которые прекращают выполнение приложения, работающего в пользовательском режиме:</p>
<p>системный вызов exit group, который завершает выполнение всей группы потоков, т. е. целое многопоточное приложение. Основная функция</p>
<p>ядра, реализующая этот системный вызов, называется do group exit(). Этот системный вызов должен быть сделан библиотечной функцией</p>
<p>exit ();</p>
<p>системный вызов exit (), который завершает выполнение одного процесса, независимо от других процессов, входящих в ту же группу, что и жертва. Основная функция ядра, реализующая этот системный вызов, называется do exito. Этот системный вызов делается, например, функцией pthread exit () из библиотеки LinuxThreads.</p>
]]></content:encoded>
			<wfw:commentRss>http://otorvald.com/drugie-potoki-yadra/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Потоки ядра</title>
		<link>http://otorvald.com/potoki-yadra</link>
		<comments>http://otorvald.com/potoki-yadra#comments</comments>
		<pubDate>Thu, 22 Mar 2012 22:58:39 +0000</pubDate>
		<dc:creator>root</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[kernel]]></category>
		<category><![CDATA[thread]]></category>

		<guid isPermaLink="false">http://otorvald.com/?p=56</guid>
		<description><![CDATA[В традиционных Unix &#8211; системах некоторые критические задачи делегируются перемежающимся процессам. Сюда входят сброс дисковых кэшей, выгрузка неиспользуемых страниц, обслуживание сетевых соединений и другие задачи. В самом деле, выполнять эти задачи в строго линейной последовательности неэффективно; их функции и процессы конечного пользователя будут иметь лучшее время отклика, если такие действия выполняются в фоновом режиме. Поскольку [...]]]></description>
			<content:encoded><![CDATA[<p>В традиционных Unix &#8211; системах некоторые критические задачи делегируются перемежающимся процессам. Сюда входят сброс дисковых кэшей, выгрузка неиспользуемых страниц, обслуживание сетевых соединений и другие задачи. В самом деле, выполнять эти задачи в строго линейной последовательности неэффективно; их функции и процессы конечного пользователя будут иметь лучшее время отклика, если такие действия выполняются в фоновом режиме. Поскольку некоторые системные процессы работают только в режиме ядра, современные операционные системы делегируют их функции потокам ядра, не обремененным ненужным контекстом пользовательского режима. В Linux потоки ядра отличаются от обычных процессов по следующим пунктам:</p>
<p>? потоки ядра работают только в режиме ядра, а обычные процессы выполняются попеременно то в режиме ядра, то в пользовательском режиме;</p>
<p>? поскольку потоки ядра работают только в режиме ядра, они обращаются только к линейным адресам, большим чем page offset; обычные же процессы пользуются всеми четырьмя гигабайтами линейных адресов, будь то в режиме пользователя или в режиме ядра.</p>
<p><span id="more-56"></span><strong>Создание потока ядра</strong></p>
<p>Функция kernei thread () создает новый поток ядра. Она принимает в качестве параметров адрес функции ядра, подлежащей выполнению (fn), аргумент, передаваемый этот функции (arg), и набор флагов клона (flags). Фактически она вызывает функцию do_fork () со следующими аргументами.</p>
<p>Флаг clone vm позволяет избежать копирования таблиц страниц вызвавшего процесса. Такое копирование было бы непроизводительной тратой времени и памяти, потому что новый поток ядра все равно не станет обращаться к адресному пространству пользовательского режима. Флаг clone untraced гарантирует, что никакой процесс не сможет отслеживать выполнение нового потока ядра, даже если производилось отслеживание вызвавшего процесса.</p>
<p>Параметр pregs, передаваемый функции do forkO, соответствует адресу в стеке режима ядра, по которому функция copy thread () сможет найти начальные значения регистров процессора для нового потока. Функция kernei thread () строит эту стековую область так, чтобы выполнялось следующее:</p>
<p>? регистры ebx и edx получили от функции copy thread () значения параметров fn и arg соответственно;</p>
<p>? регистр eip получил адрес следующего фрагмента ассемблерного кода:</p>
<p>movl %edx,%eax pushl %edx call %ebx pushl %eax call do_exit</p>
<p>Таким образом, новый поток ядра начнется с выполнения функции fn(arg). Если эта функция завершится, поток ядра сделает системный вызов exit (), передав ему значение, возвращенное функцией fn ().</p>
<p><strong>Процесс О</strong></p>
<p>Предок всех процессов, называемый процессом 0, или холостым процессом, или (по историческим причинам) процессом swapper, представляет собой поток ядра, созданный &#8220;с нуля”на этапе инициализации Linux. Этот всеобщий предок использует следующие статически выделяемые структуры (структуры для всех остальных процессов выделяются динамически):</p>
<p>? дескриптор процесса, хранящийся в переменной init task, которая инициализируется макросом init task;</p>
<p>? дескриптор thread info и стек режима ядра, хранящиеся в переменной</p>
<p>init_thread_union И инициализируемые МакрОСОМ INIT_THREAD_INFO;</p>
<p>? таблицы, на которые указывает дескриптор процесса.</p>
<p>Эти таблицы инициализируются, соответственно, следующими макросами.</p>
<p>? главный глобальный каталог страниц ядра, хранящийся в переменной</p>
<p>swapper_pg_dir (СМ. главу 2).</p>
<p>Функция start kernei о инициализирует все структуры данных, необходимые ядру, включает прерывания и создает еще один поток ядра, процесс 1, чаще называемый процессом init.</p>
<p>Этот новый поток ядра имеет идентификатор процесса, равный 1, и использует все соответствующие структуры данных ядра совместно с процессом 0. Будучи выбранным планировщиком, процесс init запускает функцию init ().</p>
<p>Создав процесс init, процесс 0 вызывает функцию cpu idie (), которая, в сущности, сводится к многократному выполнению ассемблерной инструкции hit при включенных прерываниях. Процесс 0 выбирается планировщиком, только если нет других процессов в состоянии task running.</p>
<p>В многопроцессорных системах процесс 0 имеется у каждого процессора. Сразу после включения питания система BIOS включает один процессор, оставив остальные выключенными. Процесс swapper, работающий на процессоре 0, инициализирует структуры данных ядра, а затем включает остальные процессоры и создает дополнительные процессы swapper с помощью функции copy process о, которой он передает ноль в качестве идентификатора нового процесса. Кроме того, ядро записывает в поле сри дескриптора thread info каждого ответвленного процесса соответствующий индекс процессора.</p>
<p><strong>Процесс 1</strong></p>
<p>Поток ядра, созданный процессом 0, вызывает функцию init (), которая завершает инициализацию ядра. Затем функция init () делает системный вызов execve (), чтобы загрузить исполняемую программу init. В результате поток ядра init становится обычным процессом, имеющим собственные структуры данных ядра. Процессор init продолжает существовать вплоть до выключения питания, поскольку он создает и отслеживает все процессы, реализующие внешние слои операционной системы.</p>
]]></content:encoded>
			<wfw:commentRss>http://otorvald.com/potoki-yadra/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Системные вызовы clone, fork и vfork</title>
		<link>http://otorvald.com/sistemnyie-vyizovyi-clone-fork-i-vfork</link>
		<comments>http://otorvald.com/sistemnyie-vyizovyi-clone-fork-i-vfork#comments</comments>
		<pubDate>Wed, 21 Mar 2012 22:56:22 +0000</pubDate>
		<dc:creator>root</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[процессы]]></category>

		<guid isPermaLink="false">http://otorvald.com/?p=54</guid>
		<description><![CDATA[Облегченные процессы создаются в Linux с помощью функции clone (), которая принимает следующие параметры: fn —задает функцию, которую должен выполнить новый процесс; когда эта функция возвратит управление, потомок завершит свою работу. Функция возвращает целое число, представляющее собой код возврата процес &#8211; са &#8211; потомка; arg —указывает на данные, необходимые для функции fn (); flags —разнообразная [...]]]></description>
			<content:encoded><![CDATA[<p>Облегченные процессы создаются в Linux с помощью функции clone (), которая принимает следующие параметры:</p>
<p>fn —задает функцию, которую должен выполнить новый процесс; когда эта функция возвратит управление, потомок завершит свою работу. Функция возвращает целое число, представляющее собой код возврата процес &#8211; са &#8211; потомка;</p>
<p>arg —указывает на данные, необходимые для функции fn ();</p>
<p>flags —разнообразная информация. Младший байт задает номер сигнала, посылаемого родителю, когда потомок завершит работу (как правило, выбирается сигнал sigchld). Остальные три байта задают группу флагов клона, перечисленных в табл. 3.8;</p>
<p>chiid stack —задает указатель стека в режиме пользователя, который должен быть записан в регистр esp процесса &#8211; потомка. Вызывающий процесс (родитель) должен всегда выделять стек для нового потомка;</p>
<p>tis —задает адрес структуры, определяющей сегмент локальной памяти потока для нового облегченного процесса (см. главу 2). Имеет смысл только при установленном флаге clone settls;</p>
<p>ptid —задает адрес переменной процесса &#8211; родителя в режиме пользователя, где будет храниться идентификатор нового облегченного процесса. Имеет смысл только при установленном флаге clone parent settid;</p>
<p>ctid —задает адрес переменной нового облегченного процесса в режиме пользователя, где будет храниться идентификатор этого процесса. Имеет смысл только при установленном флаге clone child settid.</p>
<p><span id="more-54"></span>Функция clone () фактически является интерфейсной функцией, определенной в библиотеке С (см. разд. &#8220;API &#8211; интерфейсы стандарта POSIX и системные вызовы&#8221; в главе 10). Она устанавливает стек для нового облегченного</p>
<p>процесса и делает системный вызов clone о, скрытый от программиста. У служебной процедуры sys cione (), реализующей системный вызов clone (), нет параметров fn и а г д. Интерфейсная функция сохраняет указатель fn в стеке потомка в позиции, соответствующей адресу возврата самой интерфейсной функции, а указатель arg сохраняется в стеке потомка сразу под fn. Когда интерфейсная функция завершается, процессор извлекает адрес возврата из стека и выполняет функцию fn (arg).</p>
<p>Традиционный системный вызов fork о реализован в Linux в виде системного вызова clone о, у которого параметр flags задает сигнал sigchld и определяет, что все флаги клона сброшены, а параметр chiid stack является указателем на текущий стек процесса &#8211; родителя. Таким образом, родитель и потомок временно совместно используются одним стеком режима пользователя.</p>
<p>Системный вызов vforko, упомянутый в предыдущем разделе, реализован в Linux в виде системного вызова clone о, у которого параметр flags задает сигнал sigchld и флаги clone_vm и clone_vfork, а параметр chiid_stack является указателем на текущий стек процесса &#8211; родителя.</p>
<p>Функция do_fork()</p>
<p>Функция do_fork(), обрабатывающая системные вызовы clone о, fork о и vfork (), принимает следующие параметры:</p>
<p>clone flags —ТО же, ЧТО параметр flags функции clone ();</p>
<p>stack start —ТО же, ЧТО параметр chiid stack функции clone ();</p>
<p>regs —указатель на содержимое регистров общего назначения, сохраненное в стеке режима ядра при переключении из пользовательского режима в режим ядра.</p>
<p>stack size —не используется (всегда равен 0);</p>
<p>parent tidptr, child tidptr —ТО же, ЧТО параметры ptid И ctid фуНКЦИИ clone().</p>
<p>Функция do fork () вызывает служебную функцию copy process (), чтобы установить дескриптор процесса и прочие структуры данных ядра, необходимые для работы потомка. Приведем основные действия, выполняемые функцией do_fork():</p>
<p>1. Выделяет идентификатор процесса для потомка, просматривая битовую карту pidinap_array.</p>
<p>2. Проверяет поле ptrace процесса &#8211; родителя (то есть поле current -&gt;ptrace). Если оно не равно нулю, значит, родитель отслеживается другим процессом, и тогда функция do_f or к () проверяет, хочет ли отладчик отслеживать процесс &#8211; потомок по своей инициативе (то есть независимо от флага clone ptrace, задаваемого родителем). В этом случае, если потомок не является потоком ядра (флаг clone untraced сброшен), функция устанавливает флаг CLONE_PTRACE.</p>
<p>3. Вызывает функцию copy process о, чтобы создать копию дескриптора процесса. Если все необходимые ресурсы доступны, функция возвращает адрес только что созданного дескриптора task struct. Эта функция —&#8221;рабочая лошадка&#8221; процедуры ветвления процесса, и мы обсудим ее сразу ПОСЛе фуНКЦИИ do_fork ().</p>
<p>4. Если либо установлен флаг clone stopped, либо необходим мониторинг процесса &#8211; потомка (то есть флаг рт ptraced в поле p -&gt;ptrace установлен), функция переводит процесс &#8211; потомок в состояние task stopped и добавляет к нему сигнал sigstop, подлежащий обработке. Потомок остается в состоянии task stopped, пока какой-то другой процесс (предположительно, отслеживающий выполнение или родитель) не переведет его в состояние task running с помощью сигнала SIGCONT.</p>
<p>5. Если флаг clone stopped не установлен, функция вызывает функцию wake up new task (), которая выполняет следующие действия:</p>
<p>•настраивает параметры планирования родителя и потомка (см. разд. &#8220;Алгоритм планирования&#8221; главы 7);</p>
<p>•если потомок будет выполняться на том же процессоре, что и родитель8, и родитель с потомком не обращаются к одному набору таблиц страниц (флаг clone vm сброшен), функция заставляет процесс &#8211; потомок выполняться до родителя, занося процесс &#8211; потомок в очередь на выполнение, в которой стоит родитель, непосредственно перед родителем.</p>
<p>•в противном случае, если потомку не предстоит работать на одном процессоре с родителем, или если родитель с потомком совместно используют один набор таблиц страниц (флаг clone vm установлен),</p>
<p>функция ставит процесс &#8211; потомок на последнее месте родительской очереди на выполнение.</p>
<p>6. Если флаг clone stopped установлен, функция переводит процесс &#8211; потомок в состояние task_stopped.</p>
<p>7. Если выполнение процесса &#8211; родителя отслеживается, функция сохраняет идентификатор процесса &#8211; потомка в поле ptrace message текущего процесса и вызывает функцию ptrace notify о, которая фактически останавливает текущий процесс и отправляет сигнал sigchld его родителю. &#8220;Дедушкой”процесса &#8211; потомка здесь является отладчик, отслеживающий выполнение родителя. Сигнал sigchld уведомляет отладчик, что текущий процесс породил процесс, чей идентификатор может быть получен из поля current -&gt;ptrace_message.</p>
<p>8. Если установлен clone vfork, функция ставит процесс &#8211; родитель в очередь ожидания и приостанавливает его выполнение до тех пор, пока потомок не освободит адресное пространство (то есть пока он не завершит работу или не запустит новую программу).</p>
<p>9. Завершает работу и возвращает идентификатор процесса &#8211; потомка. Функция copy_process()</p>
<p>Функция copy process () устанавливает дескриптор процесса и любые другие структуры данных ядра, которые могут потребоваться для выполнения потомка. Ее параметры такие же, как у функции do fork (), плюс идентификатор процесса &#8211; потомка. Она выполняет следующие действия:</p>
<p>1. Проверяет, совместимы ли флаги, переданные в параметре cione fiags. В частности, она возвращает код ошибки в следующих случаях:</p>
<p>•установлены флаги clone_newns и clone_fs;</p>
<p>•флаг clone_thread установлен, но флаг clone_sighand сброшен (облегченные процессы в одной группе потоков должны иметь общие сигналы);</p>
<p>•флаг clone sighand установлен, но флаг clone vm сброшен (облегченные процессы, имеющие общие обработчики сигналов, должны иметь и общий дескриптор памяти).</p>
<p>2. Выполняет дальнейшие защитные проверки, для чего вызывает функцию security_task_create() И, чуть ПОЗЖе, функцию security_task_alloc (). Ядро Linux 2.6 предлагает перехватчики для расширений безопасности, чтобы установить модель безопасности более строгую, чем та, что принята в традиционных Unix &#8211; подобных системах (см. главу 20).</p>
<p>3. Вызывает функцию dup task structо, чтобы получить дескриптор процесса для потомка. Эта функция выполняет следующие действия:</p>
<p>•вызывает функцию uniazy_fpu() для текущего процесса, чтобы сохранить, если необходимо, содержимое регистров FPU, ММХ и SSE/SSE2 в структуре thread info процесса &#8211; родителя. Впоследствии функция dup task struct о скопирует эти значения в структуру thread info процесса &#8211; потомка;</p>
<p>•выполняет макрос aiioc task structо, чтобы получить дескриптор (структуру task struct) для нового процесса, и сохраняет его адрес в локальной переменной tsk;</p>
<p>•выполняет макрос aiioc thread info, чтобы получить свободную область памяти для хранения структуры thread info и стека режима ядра для нового процесса, а затем сохраняет ее адрес в локальной переменной ti. Как было сказано в разд. &#8220;Идентификация процесса”ранее в этой главе, размер этой области памяти равен либо 8 Кбайт, либо 4 Кбайт;</p>
<p>•копирует содержимое дескриптора текущего процесса в структуру task struct, на которую указывает локальная переменная tsk, затем записывает Значение ti В ПОЛе tsk -&gt;thread_info;</p>
<p>•копирует содержимое дескриптора thread info текущего процесса в структуру, на которую указывает локальная переменная ti, а затем записывает значение tsk в поле ti -&gt;task;</p>
<p>•записывает 2 в счетчик обращений дескриптора нового процесса (tsk -&gt;usage), чтобы показать, что дескриптор процесса используется и что соответствующий процесс &#8220;жив&#8221; (его состояние не exit zombie и не exit_dead);</p>
<p>•возвращает указатель на дескриптор нового процесса (tsk).</p>
<p>4. Проверяет значение, хранящееся В поле current -&gt;signal -&gt;rlim[RLIMIT_ nproc] .riim cur. Если оно меньше или равно текущему количеству процессов, принадлежащих пользователю, возвращается код ошибки, если у процесса нет привилегий root. Функция получает текущее количество процессов, принадлежащих пользователю, из пользовательской структуры по имени user struct. Эту структуру можно найти по указателю в поле user дескриптора процесса.</p>
<p>5. Увеличивает счетчик обращений структуры user struct (поле tsk -&gt; user -&gt; count) и счетчик процессов, принадлежащих пользователю (поле tsk -&gt;user -&gt;processes).</p>
<p>6. Убеждается, что количество процессов в системе (хранящееся в переменной nr threads) не превышает значение переменной max threads. Значение этой переменной, принимаемое по умолчанию, зависит от объема оперативной памяти в системе. Общее правило гласит, что память, занимаемая всеми дескрипторами thread inf о и стеками режима ядра, не может превышать 1/8 объема физической памяти. Впрочем, системный администратор может изменить это значение, записав новое в файл /proc/sys/kernel/threads &#8211; max.</p>
<p>7. Если функции ядра, реализующие область выполнения и поддержку исполняемого формата процесса, входят в состав модулей ядра, описываемая функция увеличивает их счетчики обращений.</p>
<p>8. Устанавливает несколько важных полей, имеющих отношение к состоянию процесса:</p>
<p>•инициализирует счетчик глобальной блокировки ядра tsk -&gt;iock_depth значением 1.</p>
<p>•инициализирует поле tsk -&gt;did_exec значением 0. Это счетчик системных вызовов execve (), сделанных процессом;</p>
<p>•обновляет некоторые флаги в поле tsk -&gt;fiags, скопированные у родителя: вначале сбрасывает флаг pf superpriv, показывающий, воспользовался ли процесс какими-либо привилегиями суперпользователя, а затем устанавливает флаг pf forknoexec, показывающий, что потомок еще не сделал системный вызов execve ().</p>
<p>9. Сохраняет идентификатор нового процесса в поле tsk -&gt;pid.</p>
<p>10. Если флаг clone_parent_settid в параметре cione_fiags установлен, функция копирует идентификатор процесса &#8211; потомка в переменную пользовательского режима, на которую указывает параметр parent tidptr.</p>
<p>11. Инициализирует структуры list head и спин &#8211; блокировки, входящие в состав дескриптора процесса &#8211; потомка, и устанавливает некоторые другие поля, имеющие отношение к ожидающим доставки сигналам, таймерам и хронометрической статистики.</p>
<p>12. Вызывает функции copy_semundo (), copy_files (), copy_fs(), copy_ sighand (), copy_signal (), copy_mm () И copy_namespace (), чтобы создать НОВЬЮ структуры и занести в них значения соответствующих структур родителя, если противоположное не указано с помощью параметра</p>
<p>clone_flags.</p>
<p>13. Вызывает функцию copy thread (), чтобы инициализировать стек режима ядра для процесса &#8211; потомка значениями, содержавшимися в регистрах</p>
<p>процессора в момент выдачи системного вызова clone о (эти значения были сохранены в родительском стеке режима ядра; см. главу 10). При этом, однако, функция принудительно записывает 0 в поле, соответствующее регистру еах (это код возврата системного вызова fork о или clone о в процессе &#8211; потомке). Поле thread, esp в дескрипторе потомка инициализируется базовым адресом стека режима ядра процесса &#8211; потомка, а адрес ассемблерной функции ret_from_fork() сохраняется в поле thread, eip. Если родитель пользуется битовой картой разрешений ввода/вывода, потомок получает копию этой карты. Наконец, если установлен флаг clone settls, потомок получает TLS &#8211; сегмент, определяемый структурой данных режима пользователя, на которую указывает параметр tIs системного вызова clone () 9.</p>
<p>14. Если установлен флаг clone_child_settid или clone_child_cleartid в параметре cione fiags, функция копирует значение параметра chiid tidptr В ПОЛе tsk -&gt;set_chid_tid ИЛИ tsk -&gt;clear_child_tid соответственно. Эти флаги говорят о том, что значение переменной, на которую указывает параметр chiid tidptr, в адресном пространстве потомка в режиме пользователя должно быть изменено, хотя физически операции записи будут выполнены позже.</p>
<p>15. Сбрасывает флаг tif syscall trace в структуре thread info процесса &#8211; потомка, чтобы функция ret_from_fork () не уведомляла процесс &#8211; отладчик о завершении системного вызова. Заметим, что при этом системный вызов на мониторинг потомка не отменяется, потому что этим управляет флаг PTRACE_SYSCALL В ПОЛе tsk -&gt;ptrace.</p>
<p>16. Инициализирует поле tsk -&gt;exit_signai номером сигнала, указанным в младших битах параметра cione fiags, если флаг clone thread не установлен. В последнем случае инициализирует это поле значением &#8211; 1. Как мы увидим в разд. &#8220;Завершение процесса&#8221; далее в этой главе, только завершение работы последнего члена группы потоков (как правило, это лидер группы) приводит к отправке уведомляющего сигнала родителю лидера группы.</p>
<p>17. Вызывает функцию sched forko, чтобы завершить инициализацию структуры данных для планировщика, принадлежащей новому процессу. Функция также переводит новый процесс в состояние task running и записывает 1 В поле preempt count Структуры thread info, отключая тем самым вытеснение в ядре. Кроме того, чтобы планирование процессов имело справедливый характер, функция делит оставшийся отрезок времени родителя между родителем и потомком.</p>
<p>18. Записывает в поле сри структуры thread info нового процесса номер локального процессора, полученный ОТ функции smp_processor_id().</p>
<p>19. Инициализирует поля, определяющие отношения между родителем и потомком. В частности, если флаг clone_parent или clone_thread установлен, функция инициализирует поля tsk -&gt;reai_parent и tsk -&gt;parent значением, хранящимся в поле current -&gt;reai_parent, и в результате родитель потомка выступает в качестве родителя текущего процесса. В противном случае функция записывает в эти поля значение current.</p>
<p>20. Если мониторинг потомка не требуется (флаг clone ptrace не установлен), функция записывает 0 в поле tsk -&gt;ptrace. Это поле содержит несколько флагов, используемых при мониторинге одного процесса другим. Таким образом, даже если текущий процесс отслеживается, потомок отслеживаться не будет.</p>
<p>21. Выполняет макрос set links, чтобы занести дескриптор нового процесса в список процессов.</p>
<p>22. Если мониторинг потомка требуется (флаг pt ptraced в поле tsk -&gt;ptrace установлен), функция записывает в поле current -&gt;parent значение поля tsk -&gt;parent и заносит процесс &#8211; потомок в список отслеживаемых процессов, принадлежащий отладчику.</p>
<p>23. Вызывает функцию attach pid (), чтобы занести идентификатор нового процесса в хеш &#8211; таблицу pidhash [pidtype_pid] .</p>
<p>24. Если потомок является лидером группы потоков (флаг clone thread сброшен), функция:</p>
<p>•инициализирует поле tsk -&gt;tgid значением из поля tsk -&gt;pid;</p>
<p>•инициализирует поле tsk -&gt;group_ieader значением из поля tsk;</p>
<p>•трижды вызывает функцию attach pido, чтобы занести идентификатор процесса &#8211; потомка в хеш &#8211; таблицы pidtype tgid, pidtype pgid и</p>
<p>PIDTYPE SID.</p>
<p>25. В противном случае, т. е. если потомок принадлежит к группе потоков своего родителя (флаг clone thread установлен), функция:</p>
<p>•инициализирует ПОЛе tsk -&gt;tgid Значением ИЗ ПОЛЯ tsk -&gt;current -&gt;tgid;</p>
<p>•инициализирует поле tsk -&gt;group_leader Значением из ПОЛЯ current -&gt; group_leader;</p>
<p>•вызывает функцию attach pid (), чтобы занести процесс &#8211; потомок в хеш &#8211; таблицу pidtype tgid (точнее, в список, соответствующий идентификатору процесса и принадлежащий процессу current -&gt;group_ leader).</p>
<p>26. Итак, новый процесс добавлен к множеству существующих процессов. Функция увеличивает значение переменной nr threads.</p>
<p>27. Устанавливает значение переменной totai forks, чтобы отразить в ней количество ответвленных процессов.</p>
<p>28. Завершает выполнение, возвращая указатель на дескриптор процесса &#8211; потомка (tsk).</p>
<p>В будущем, при очередном переключении процессов, планировщик окажет нашему процессу такую любезность, загрузив в соответствующие регистры процессора значения из поля thread дескриптора этого процесса. В частности, в регистр esp будет загружено значение thread.esp (то есть адрес стека процесса &#8211; потомка в режиме ядра), а в регистр eip —адрес функции ret from fork (). Эта функция, написанная на ассемблере, вызывает функцию scheduie taii о, загружает в остальные регистры значения, хранящиеся в стеке, и переводит процессор в режим пользователя, выполнение нового процесса начнется сразу по окончании работы системного вызова fork о, vfork () или clone (). Значение, возвращенное системным вызовом, содержится в регистре еах: оно равно 0 для потомка и идентификатору процесса для родителя. Чтобы понять, как это получается, вернитесь к описанию действий функции copy thread () над регистром еах для процесса &#8211; потомка. Потомок выполняет тот же код, что и родитель, с той разницей, что системный вызов fork() возвращает 0 (шаг 13 в описании функции copy process о). Разработчик приложения может воспользоваться этим фактом, применив прием, знакомый всем, кто программирует в Unix.</p>
]]></content:encoded>
			<wfw:commentRss>http://otorvald.com/sistemnyie-vyizovyi-clone-fork-i-vfork/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Создание процессов</title>
		<link>http://otorvald.com/sozdanie-protsessov</link>
		<comments>http://otorvald.com/sozdanie-protsessov#comments</comments>
		<pubDate>Tue, 20 Mar 2012 22:54:27 +0000</pubDate>
		<dc:creator>root</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[процессы]]></category>

		<guid isPermaLink="false">http://otorvald.com/?p=52</guid>
		<description><![CDATA[При удовлетворении запросов пользователей Unix &#8211; подобные операционные системы активно создают новые процессы. Например, всякий раз, когда пользователь вводит команду, оболочка создает новый процесс, который выполняет еще одну копию оболочки. Традиционные Unix &#8211; системы обращаются со всеми процессами одинаково: ресурсы, которыми обладает процесс &#8211; родитель, дублируются в процессах &#8211; потомках. Такой подход делает процедуру создания [...]]]></description>
			<content:encoded><![CDATA[<p>При удовлетворении запросов пользователей Unix &#8211; подобные операционные системы активно создают новые процессы. Например, всякий раз, когда пользователь вводит команду, оболочка создает новый процесс, который выполняет еще одну копию оболочки.</p>
<p>Традиционные Unix &#8211; системы обращаются со всеми процессами одинаково: ресурсы, которыми обладает процесс &#8211; родитель, дублируются в процессах &#8211; потомках. Такой подход делает процедуру создания процессов очень медленной и неэффективной, потому что включает в себя копирование всего адресного пространства процесса &#8211; родителя. Процессу &#8211; потомку редко нужно читать или модифицировать все ресурсы, унаследованные от родителя; в большинстве случаев он тут же делает системный вызов execve () и очищает столь заботливо скопированное адресное пространство.</p>
<p>Когда один из этих процессов попытается сделать запись в физическую страницу, ядро копирует ее содержимое в новую физическую страницу, которая присваивается пишущему процессу;<br />
облегченные процессы позволяют как родителю, так и потомку совместно использовать многие структуры данных ядра, назначаемые процессам. Сюда входят таблицы страниц (и, следовательно, все адресное пространство режима пользователя, таблицы открытых файлов и диспозиций сигналов);<br />
системный вызов vforko создает процесс, который использует адресное пространство своего родителя. Чтобы не позволить родителю переписать данные, необходимые потомку, выполнение родителя задерживается до тех пор, пока потомок не закончит свою работу или не запустит новую программу.</p>
]]></content:encoded>
			<wfw:commentRss>http://otorvald.com/sozdanie-protsessov/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Загрузка регистров FPU</title>
		<link>http://otorvald.com/zagruzka-registrov-fpu</link>
		<comments>http://otorvald.com/zagruzka-registrov-fpu#comments</comments>
		<pubDate>Mon, 19 Mar 2012 22:53:15 +0000</pubDate>
		<dc:creator>root</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[thread]]></category>
		<category><![CDATA[процессы]]></category>

		<guid isPermaLink="false">http://otorvald.com/?p=50</guid>
		<description><![CDATA[Содержимое регистров с плавающей точкой не восстанавливается сразу после того, как возобновляется выполнение процесса next. Однако флаг ts в регистре его был установлен макросом uniazy_fpu (), поэтому, как только процесс next попытается выполнить инструкцию ESCAPE, MMX или SSE/SSE2, управляющий блок возбудит исключение &#8220;Устройство недоступно&#8221;, а ядро (точнее, обработчик этого исключения) вызовет функцию math state restore [...]]]></description>
			<content:encoded><![CDATA[<p>Содержимое регистров с плавающей точкой не восстанавливается сразу после того, как возобновляется выполнение процесса next. Однако флаг ts в</p>
<p>регистре его был установлен макросом uniazy_fpu (), поэтому, как только процесс next попытается выполнить инструкцию ESCAPE, MMX или SSE/SSE2, управляющий блок возбудит исключение &#8220;Устройство недоступно&#8221;, а ядро (точнее, обработчик этого исключения) вызовет функцию math state restore (). В этом обработчике процесс next идентифицируется как current.</p>
<p><span id="more-50"></span>void math_state_restore()</p>
<p>(</p>
<p>asm volatile (&#8220;cits&#8221;); / сбросить флаг TS в регистре crO / if (!(current -&gt;flags &amp; PF_USED_MATH)) init_fpu(current);</p>
<p>restore_fpu(current); current -&gt;thread.status |= TS_USEDFPU;</p>
<p>)</p>
<p>Функция сбрасывает флаг ts в регистре его, чтобы последующие инструкции FPU, ММХ или SSE/SSE2, выполняемые процессом, не привели к возникновению исключения Устройство недоступно”. Если содержимое субполя thread.i387 смысла не имеет, т. е. флаг pf used math равен 0, вызывается функция init fpuo, которая сбрасывает субполе thread.i387 и устанавливает флаг pf used math процесса current. Затем вызывается функция restore fpu (), загружающая в регистры FPU соответствующие значения, хранящиеся в субполе thread.i387. Для этого применяется ассемблерная инструкция fxrstor или frstor, в зависимости от того, поддерживает ли процессор расширения SSE/SSE2. Наконец, функция math_state_restore() устанавливает флаг TS_USEDFPU.</p>
<p>Использование блоков FPU, ММХ и SSE/SSE2 в режиме ядра</p>
<p>Ядро само может использовать блоки FPU, ММХ и SSE/SSE2. Конечно, при этом оно не должно мешать вычислениям, которые выполняет текущий процесс в пользовательском режиме. Поэтому</p>
<p>? перед использованием сопроцессора ядро должно вызвать функцию kernel fpu begin (), КОТОрая вызывает функцию save init fpu (), Чтобы сохранить содержимое регистров, если процесс в пользовательском режиме выполнял инструкции FPU (флаг ts usedfpu), а затем сбрасывает флаг ts в регистре его;</p>
<p>? после использования сопроцессора ядро должно вызвать функцию kernei fpu end (), которая устанавливает флаг ts в регистре его.</p>
<p>Впоследствии, когда процесс пользовательского режима выполнит инструкцию сопроцессора, функция math state restore о восстановит содержимое регистров, аналогично тому, как это происходит при переключении процессов.</p>
<p>Следует отметить, ЧТО время работы функции kernel fpu begin () довольно велико, если текущий процесс пользовательского режима обращается к сопроцессору. Настолько велико, что этого достаточно для аннулирования выигрыша, полученного от применения блоков FPU, ММХ или SSE/SSE2. На практике ядро пользуется ими лишь в некоторых ситуациях, как правило, при переносе или очистке больших областей памяти или при вычислении контрольных сумм.</p>
]]></content:encoded>
			<wfw:commentRss>http://otorvald.com/zagruzka-registrov-fpu/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Сохранение и загрузка регистров FPU, ММХ и ХММ</title>
		<link>http://otorvald.com/sohranenie-i-zagruzka-registrov-fpu-mmh-i-hmm</link>
		<comments>http://otorvald.com/sohranenie-i-zagruzka-registrov-fpu-mmh-i-hmm#comments</comments>
		<pubDate>Sun, 18 Mar 2012 22:51:57 +0000</pubDate>
		<dc:creator>root</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[thread]]></category>
		<category><![CDATA[процессы]]></category>

		<guid isPermaLink="false">http://otorvald.com/?p=48</guid>
		<description><![CDATA[Начиная с Intel 80486DX, блок операций с плавающей точкой (Floating Point Unit, FPU) интегрирован в процессор. Тем не менее название математический сопроцессор используется по-прежнему и напоминает о тех днях, когда вычисления с плавающей точкой выполнялись дорогим специализированным чипом. Для поддержки совместимости со старыми моделями арифметические функции выполняются при помощи ESCAPE &#8211; инструкций, т. е. инструкций [...]]]></description>
			<content:encoded><![CDATA[<p>Начиная с Intel 80486DX, блок операций с плавающей точкой (Floating Point Unit, FPU) интегрирован в процессор. Тем не менее название математический сопроцессор используется по-прежнему и напоминает о тех днях, когда вычисления с плавающей точкой выполнялись дорогим специализированным чипом. Для поддержки совместимости со старыми моделями арифметические функции выполняются при помощи ESCAPE &#8211; инструкций, т. е. инструкций с префиксным байтом, имеющим значение от 0xd8 до Oxdf. Эти инструкции работают с набором процессорных регистров для операций с плавающей точкой. Очевидно, что если процесс использует ESCAPE &#8211; инструкции, то содержимое регистров с плавающей точкой является частью его аппаратного контекста и должно быть сохранено. Впоследствии компания Intel встроила в свои микропроцессоры Pentium новый набор ассемблерных инструкций. Они получили название MMX &#8211; инструкций и были задуманы для ускорения работы мультимедийных приложений. MMX &#8211; инструкции используют регистры блока операций с плавающей точкой (блока FPU).</p>
<p><span id="more-48"></span>Очевидным недостатком этого архитектурного решения является невозможность для программистов применять инструкции с плавающей точкой вместе с MMX &#8211; инструкциями. Достоинство же заключается в том, что разработчики операционной системы могут игнорировать новый набор инструкций, потому что фрагмент кода, переключающего процессы, отвечающий за сохранение состояния блока FPU, может быть использован и для сохранения состояния блока ММХ.<br />
MMX &#8211; инструкции ускоряют работу мультимедийных приложений, потому что реализуют внутри процессора конвейер SIMD (Single &#8211; Instruction Multiple &#8211; Data, одна инструкция -множественные данные&#8221;). В модели Pentium III возможности SIMD &#8211; конвейера расширяются за счет введения расширений SSE (Streaming SIMD Extensions, потоковые расширения SIMD), которые позволяют обрабатывать значения с плавающей точкой, хранящиеся в 128 &#8211; битовых регистрах, называемых ХММ &#8211; регистроми. Такие регистры не перекрываются регистрами FPU и ММХ, так что инструкции SSE можно смело смешивать с инструкциями FPU/ММХ. В модели Pentium 4 появилась еще одна функциональная возможность -расширения SSE2. В принципе, это SSE &#8211; расширения с поддержкой значений с плавающей точкой с повышенной точностью. Расширения SSE2 используют тот же набор ХММ &#8211; регистров, что и расширения SSE.</p>
<p>Микропроцессоры 80&#215;86 не сохраняют автоматически регистры FPU, ММХ и ХММ в сегменте состояния задачи. Однако они предоставляют некоторую аппаратную поддержку, позволяющую ядру сохранять эти регистры только тогда, когда это действительно необходимо. Поддержка сводится к флагу ts в регистре его, причем соблюдаются следующие правила:<br />
? каждый раз, когда производится переключение аппаратного контекста, флаг ts устанавливается;<br />
? каждый раз, когда инструкция ESCAPE, ММХ, SSE или SSE2 выполняется при установленном флаге ts, управляющий блок возбуждает исключение &#8220;Устройство недоступно&#8221;.</p>
<p>Флаг ts позволяет ядру сохранять и восстанавливать регистры FPU, ММХ и ХММ, только когда это действительно необходимо. В качестве иллюстрации предположим, что процесс А пользуется математическим сопроцессором. Когда происходит переключение контекста с А на В, ядро устанавливает флаг ts и сохраняет регистры с плавающей точкой в сегменте состояния задачи, которым пользуется процесс А. Если новый процесс В не работает с математическим сопроцессором, ядру не нужно будет восстанавливать содержимое регистров с плавающей точкой. Но как только процесс В попытается выполнить инструкцию ESCAPE или ММХ, процессор возбудит исключение &#8220;Устройство недоступно&#8221;, и соответствующий обработчик загрузит в регистры с плавающей точкой значения, сохраненные в сегменте состояния задачи процесса В.</p>
<p>Теперь мы опишем структуры данных, применяемые при избирательной загрузке регистров FPU, ММХ и ХММ. Они хранятся в дескрипторе процесса, в субполе thread. i387, формат которого описывается объединением<br />
i387_union:<br />
union i387_union (<br />
struct i387_fsave_struct fsave; struct i387_fxsave_struct fxsave; struct i387_soft_struct soft;<br />
);<br />
Из описания видно, что поле может хранить структуры только одного из трех типов. Тип i387_soft_struct применяется моделями процессоров без математического сопроцессора, и ядро Linux до сих пор поддерживает эти старые чипы, эмулируя сопроцессор программным образом. Впрочем, мы больше не будем обсуждать этот морально устаревший случай. Тип i387_fsave_struct используется в процессорах, имеющих математический сопроцессор и, возможно, блок ММХ. Наконец, тип i387_fxsave_struct используется процессорами, поддерживающими расширениями SSE и SSE2.<br />
Дескриптор процесса содержит два дополнительных флага:<br />
? флаг TS USEDFPU, ВХОДЯЩИЙ В состав ПОЛЯ status дескриптора thread_info. Он показывает, использовал ли процесс регистры FPU, ММХ и ХММ в текущем выполнении;<br />
? флаг PF USED MATH, ВХОДЯЩИЙ В состав ПОЛЯ flags дескриптора task_struct. Этот флаг показывает, имеет ли смысл содержимое субполя thread.i387. Флаг сбрасывается (содержимое смысла не имеет) в следующих двух случаях:<br />
oкогда процесс запускает новую программу, делая системный вызов execve () (см. главу 20). Поскольку управление не будет возвращено прежней программе, данные в поле thread.i387 никогда не понадобятся;<br />
oкогда процесс, выполнявший программу в пользовательском режиме, запускает процедуру обработчика сигнала (см. главу 11). Поскольку обработчики сигналов асинхронны по отношению к программе, содержимое регистров с плавающей точкой может быть бессмысленным для обработчика сигнала. Тем не менее ядро сохраняет регистры с плавающей точкой в поле thread.i387 до начала выполнения обработчика и восстанавливает их после его окончания. Поэтому обработчику сигнала разрешается использовать математический сопроцессор.<br />
Сохранение регистров FPU<br />
Как было сказано ранее, функция switch to () выполняет макрос uniazy<br />
fpu, передавая ему в качестве аргумента дескриптор замещаемого процесса prev. Макрос проверяет значение флага ts usedfpu у процесса prev. Если флаг установлен, значит, процесс prev пользовался инструкциями FPU, ММХ, SSE или SSE2, и ядро должно сохранить соответствующий аппаратный контекст:<br />
if (prev -&gt;thread_info -&gt;status &amp; TS_USEDFPU) save_init_fpu(prev);<br />
Функция save init fpu () выполняет следующие действия:<br />
1. Сохраняет содержимое регистров FPU в дескрипторе процесса prev, после чего заново инициализирует блок FPU. Если процессор использует расширения SSE/SSE2, функция также сохраняет содержимое регистров ХММ и заново инициализирует блок SSE/SSE2. Для реализации всего этого достаточно пары строчек на мощном расширенном встроенном ассемблере; либо<br />
asm volatile( &#8220;fxsave %0 ; fnclex&#8221;<br />
: M=mM (prev -&gt;thread.i387.fxsave) );,<br />
если процессор использует расширения SSE/SSE2, либо<br />
asm volatile( &#8220;fnsave %0 ; fwait&#8221;<br />
: &#8220;=m&#8221; (prev -&gt;thread.i387.fsave) );<br />
в противном случае.<br />
2. Сбрасывает флаг ts usedfpu процесса prev: prev -&gt;thread_info -&gt;status &amp;= ~TS_USEDFPU;<br />
3. Устанавливает бит ts в регистре его при помощи макроса stts (), который на практике возвращает следующие ассемблерные инструкции:<br />
movl %сгО, %еах orl $8,%еах movl %еах, %сгО.</p>
]]></content:encoded>
			<wfw:commentRss>http://otorvald.com/sohranenie-i-zagruzka-registrov-fpu-mmh-i-hmm/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Макрос switch__to</title>
		<link>http://otorvald.com/makros-switch__to</link>
		<comments>http://otorvald.com/makros-switch__to#comments</comments>
		<pubDate>Sat, 17 Mar 2012 22:49:06 +0000</pubDate>
		<dc:creator>root</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[kernel]]></category>
		<category><![CDATA[процессы]]></category>

		<guid isPermaLink="false">http://otorvald.com/?p=46</guid>
		<description><![CDATA[Второй шаг переключения процессов выполняется макросом switch to. Это одна из наиболее аппаратно &#8211; зависимых процедур ядра, и для понимания ее действий требуются некоторые усилия. Во-первых, макрос принимает три параметра, prev, next и last. Нетрудно догадаться о назначении параметров prev и next: они играют ту же роль, что и локальные переменные prev и next, т. [...]]]></description>
			<content:encoded><![CDATA[<p>Второй шаг переключения процессов выполняется макросом switch to. Это одна из наиболее аппаратно &#8211; зависимых процедур ядра, и для понимания ее действий требуются некоторые усилия.</p>
<p>Во-первых, макрос принимает три параметра, prev, next и last. Нетрудно догадаться о назначении параметров prev и next: они играют ту же роль, что и локальные переменные prev и next, т. е. являются входными параметрами, задающими ячейки памяти, хранящие адреса дескрипторов замещаемого процесса и замещающего процесса соответственно.</p>
<p><span id="more-46"></span>А как насчет третьего параметра, last? Дело в том, что в каждое переключение процессов вовлечены не два, а три процесса. Предположим, ядро приняло решение переключиться с процесса А на процесс В. В функции schedule о переменная prev указывает на дескриптор процесса A, a next —на дескриптор процесса В. Как только макрос switch to деактивирует процесс А, его выполнение прекращается.</p>
<p>Впоследствии, когда ядро решит возобновить выполнение процесса А, оно должно будет приостановить некоторый процесс С (вообще говоря, отличный от В), для чего вызовет макрос switch to с параметром prev, указывающим на С, и параметром next, указывающим на А. Когда процесс А продолжит работу, он найдет свой старый стек режима ядра, где локальная переменная prev указывает на дескриптор процесса А, а переменная next указывает на дескриптор процесса В. Планировщик, который теперь выполняется от имени процесса А, не имеет ссылки на процесс С. Однако оказывается, что эта ссылка нужна для завершения процедуры переключения процессов.</p>
<p>Последний параметр макроса switch to является выходным и задает ячейку памяти, в которую макрос запишет адрес дескриптора процесса С (естественно, это делается после того, как процесс А возобновит выполнение). Перед переключением процессов макрос сохраняет в процессорном регистре еах содержимое переменной, идентифицируемой первым входным параметром prev, т. е. содержимое локальной переменной prev, хранящейся в стеке режима ядра, принадлежащем процессу А. У процессов, в эту ячейку записывается адрес дескриптора процесса С. В текущей реализации функции schedule о последний параметр идентифицирует локальную переменную prev процесса А, так что prev переписывается адресом процесса С.</p>
<p>Содержимое стеков режима ядра процессов А, В и С, а также значения регистрах. Необходимо учитывать, что на рисунке показано значение локальной переменной prev до того, как оно будет переписано содержимым регистра еах.</p>
<p>Макрос switch to написан на расширенном встроенном ассемблере, который довольно трудно читать. В частности, ссылки на регистры делаются с помощью специальной позиционной нотации, которая позволяет компилятору самостоятельно решать, какими регистрами общего назначения он будет пользоваться. Вместо того чтобы приводить здесь громоздкий код на расширенном встроенном ассемблере, мы опишем работу макроса switch to в архитектуре 80&#215;86, пользуясь стандартным ассемблером. Итак, макрос switch to выполняет следующие действия:</p>
<p>1. Сохраняет значения параметров prev и next в регистрах еах и edx соответственно:</p>
<p>movl prev, %еах movl next, %edx</p>
<p>2. Сохраняет содержимое регистров efiags и еЬр в стеке режима ядра процесса prev. Эти регистры нужно сохранить, потому что компилятор предполагает их неизменность вплоть до окончания макроса switchto:</p>
<p>pushfl pushl %ebp</p>
<p>3. Сохраняет содержимое регистра esp В поле prev -&gt;thread.esp, чтобы это поле указывало на верхушку стека режима ядра процесса prev:</p>
<p>movl %esp,484(%еах)</p>
<p>Операнд 484(%еах) идентифицирует ячейку памяти, адрес которой равен содержимому регистра еах, увеличенному на 484.</p>
<p>4. Загружает значение next -&gt;thread.esp в регистр esp. С этого момента ядро работает со стеком режима ядра процесса next, т. е. эта инструкция фактически выполняет переключение от процесса prev к процессу next. Поскольку адрес дескриптора процесса тесно связан с адресом стека режима ядра, смена стека ядра равносильна смене текущего процесса.</p>
<p>movl 484(%edx), %esp</p>
<p>5. Сохраняет адрес с меткой 1 (о нем далее в этом разделе) в поле prev -&gt;thread. eip. Когда замещаемый процесс возобновит свое выполнение, он выполнит инструкцию с меткой 1:</p>
<p>movl $lf, 480(%еах)</p>
<p>6. Заносит значение поля next -&gt;thread. eip в стек режима ядра процесса next. В большинстве случаев это адрес с меткой 1:</p>
<p>pushl 480(%edx)</p>
<p>7. Переходит на выполнение функции switch to (), написанной на языке С</p>
<p>(см. следующий раздел):</p>
<p>jmp switch_to</p>
<p>8. Здесь процесс А, который был замещен процессом В, снова получает в свое распоряжение процессор. Выполняется ряд инструкций, восстанавливающих содержимое регистров efiags. Первая инструкция имеет метку 1:</p>
<p>1:</p>
<p>popl %ebp popf 1</p>
<p>Обратите внимание, что эти инструкции pop работают со стеком ядра, принадлежащим процессу prev. Они будут выполнены, когда планировщик выберет prev в качестве нового процесса, подлежащего выполнению, и вызовет макрос switch to, передав ему prev в качестве второго параметра. Таким образом, регистр esp указывает на стек режима ядра процесса</p>
<p>prev.</p>
<p>9. Копирует содержимое регистра еах (загруженное на шаге 1) в ячейку памяти, идентифицируемую параметром last макроса switch to:</p>
<p>movl %еах, last</p>
<p>Как было сказано ранее, регистр еах указывает на дескриптор только что замещенного процесса6.</p>
<p>Функция switch_to()</p>
<p>Функция switch to о выполняет основную часть работы по переключению процессов, начатой макросом switch to (). Она принимает параметры prev p и next p, обозначающие старый и новый процессы соответственно. Вызов этой функции отличается от вызова среднестатистической”функции, поскольку функция switch to о берет значения параметров prev p и next p из регистров еах и edx (в которых, как мы видели, эти значения сохраняются), а не из стека, как большинство других функций. Чтобы заставить функцию брать параметры из регистров, ядро применяет ключевые слова attribute и regparm, являющиеся нестандартными расширениями языка С, реализованными в компиляторе gcc. Функция switch toO объявлена в заголовочном</p>
<p>файле include/asm &#8211; i386/system.h следующим образом:</p>
<p>switch_to(struct task_struct prev_p,</p>
<p>struct task_struct next_p)</p>
<p>attribute (гедрагш(З) ) ;</p>
<p>Она выполняет следующие действия:</p>
<p>1. Выполняет код, возвращенный макросом uniazy_fpu() (см. разд. &#8220;Сохранение и загрузка регистров FPU, ММХ и ХММ”далее в этой главе), чтобы сохранить содержимое регистров FPU, ММХ и ХММ процесса prev p, если необходимо.</p>
<p>unlazy_fpu (prev_p) ;</p>
<p>2. Выполняет макрос smp_processor_id(), чтобы получить индекс локального процессора, т. е. процессора, выполняющего код. Макрос извлекает искомый индекс из поля сри структуры thread info текущего процесса и сохраняет его в локальной переменной.</p>
<p>3. Загружает значение поля next_p -&gt;thread.espO в поле espO сегмента состояния задачи, ассоциированного с локальным процессором. Как мы узнаем из разд. &#8220;Выдача системного вызова с помощью инструкции sysenter”, любое последующее изменение уровня привилегий от режима пользователя до режима ядра, вызванное ассемблерной инструкцией sysenter, приведет к копированию этого адреса в регистр esp:</p>
<p>init_tss[cpu].espO = next_p -&gt;thread.espO;</p>
<p>4. Загружает в глобальную таблицу дескрипторов локального процесса сегменты TLS (Thread &#8211; Local Storage), используемые процессом next p. Три селектора сегментов хранятся в массиве tis array в дескрипторе процесса (см. главу 2).</p>
<p>cpu_gdt_table[сри][б] = next_p -&gt;thread.tls_array[0]; cpu_gdt_table[сри][7] = next_p -&gt;thread.tls_array[1]; cpu_gdt_table[cpu][8] = next_p -&gt;thread.tls_array[2];</p>
<p>5. Сохраняет содержимое сегментных регистров fs и gs в полях prev_p -&gt; thread.fs и prev_p -&gt;thread.gs, соответственно, выполняя следующие ассемблерные инструкции:</p>
<p>movl %fs, 40 (%esi) movl %gs, 44(%esi)</p>
<p>Регистр esi указывает на структуру prev_p -&gt;thread.</p>
<p>6. Если сегментный регистр fs или gs был уже использован процессом prev p или next p (то есть если они содержат ненулевое значение), функция загружает в эти регистры значения, хранящиеся в дескрипторе thread struct процесса next p. Этот шаг логически дополняет действия, выполненные в предыдущем. Главными ассемблерными инструкциями при этом являются:</p>
<p>movl 40(%ebx),%fs movl 44(%ebx),%gs</p>
<p>Регистр ebx указывает на структуру next_p -&gt;thread. На самом деле, код намного сложнее, потому что процессор может возбудить исключение, если он обнаружит недопустимое значение в сегментном регистре. Код учитывает эту возможность, прибегая к приему &#8220;обработка исключения”. Поскольку процессы редко модифицируют битовую карту разрешений ввода/вывода, она обрабатывается в ленивом”режиме. Реальная битовая карта копируется в сегмент состояния задачи локального процессора, только если процесс действительно обращается к порту ввода/вывода в текущем интервале времени. Битовая карта разрешений ввода/вывода, принадлежащая какому-то процессу, хранится в буфере, на который указывает поле io bitmap ptr структуры thread info этого процесса. Функция handie io bitmap () устанавливает значение в поле io bitmap сегмента состояния задачи локального процессора для процесса next p следующим образом:</p>
<p>•если процесс next p не имеет собственной битовой карты разрешений ввода/вывода, в поле io bitmap сегмента состояния задачи записывается значение 0&#215;8000;</p>
<p>•если у процесса next p есть собственная битовая карта разрешений ввода/вывода, в поле io bitmap сегмента состояния задачи записывается значение 0&#215;9000.</p>
<p>Поле io bitmap сегмента состояния задачи должно содержать смещение внутри этого сегмента, определяющее место хранения реальной битовой карты. Значения 0&#215;8000 и 0&#215;9000 указывают за пределы сегмента TSS, что послужит причиной возбуждения исключения Общий сбой защиты”, как только процесс режима пользователя попытается обратиться к порту ввода/вывода (см. разд. &#8220;Исключения&#8221; главы 4). Обработчик исключения do_general_protection() проверит Значение В ПОЛе io bitmap. ЕСЛИ ОНО равно 0&#215;8000, функция отправит сигнал sigsegv процессу в пользовательском режиме. Если же оно равно 0&#215;9000, функция скопирует битовую карту процесса (на которую указывает поле io bitmap ptr структуры thread info) в сегмент состояния задачи локального процессора, запишет в поле io bitmap фактическое смещение битовой карты и форсирует новое выполнение ассемблерной инструкции, приведшей к возбуждению исключения.</p>
<p>9. Завершает работу. Функция switch to о (написанная на языке С) заканчивается оператором:</p>
<p>return prev_p;</p>
<p>Соответствующие ассемблерные инструкции, сгенерированные компилятором, выглядят так:</p>
<p>movl %edi,%eax ret</p>
<p>Параметр prev р (сейчас хранящийся в регистре edi) копируется в регистр еах, потому что значение, возвращаемое любой функцией на языке С, передается через регистр еах. Обратите внимание, что таким образом значение регистра еах оказывается одинаковым до и после вызова функции</p>
<p>switch to (). Это очень важно, потому что при вызове макроса switch to</p>
<p>подразумевается, что регистр еах всегда хранит адрес дескриптора замещаемого процесса.</p>
<p>Ассемблерная инструкция ret загружает в счетчик команд eip адрес возврата, хранящийся на верхушке стека. Однако функция switch to () была вызвана простой инструкцией перехода. Поэтому инструкция ret находит на верхушке стека адрес инструкции с меткой 1, который был положен в стек макросом switch to. Если процесс next p до этого не приостанавливался, потому что выполняется впервые, функция находит начальный адрес функции ret f rom fork ().</p>
]]></content:encoded>
			<wfw:commentRss>http://otorvald.com/makros-switch__to/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

