Средства синхронизации в ядре

Средства синхронизации в ядре, интерфейсы к ним, особенности работы. Порядок выполнения атомарных операций. Функции работы со спин-блокировками. Создание и инициализация семафоров. Условные переменные и секвентные блокировки. Барьеры компилятора и памяти.

Рубрика Программирование, компьютеры и кибернетика
Вид лекция
Язык русский
Дата добавления 22.12.2011
Размер файла 45,9 K

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.

up_write(&mr_rwsem);

Для семафоров есть реализации функций down_read_trylock() и down_write_ trylock (). Каждая из них принимает один параметр-- указатель на семафор чтения-записи. Обе функции возвращают ненулевое значение, если блокировка захвачена успешно, и нуль, если блокировка находится в состоянии конфликта. Следует быть внимательными -- поведение этих функций противоположно поведению аналогичных функций для обычных семафоров, причем без всякой на то причины!

Семафоры чтения-записи имеют уникальную функцию, аналога которой нет для спин-блокировок чтения-записи. Это функция downgrade_writer (), которая автоматически превращает блокировку, захваченную на запись, в блокировку, захваченную на чтение.

Семафоры чтения-записи, так же как и спин-блокировки аналогичного типа, должны использоваться, только если есть четкое разделение между участками кода, которые осуществляют чтение, и участками кода, которые осуществляют запись. Использование механизмов блокировок чтения-записи приводит к дополнительным затратам, поэтому их стоит использовать, только если код можно четко разделить на участки чтения и записи.

Сравнение спин-блокировок и семафоров

Понимание того, когда использовать спин-блокировки, а когда семафоры является важным для написания оптимального кода. Однако во многих случаях выбирать очень просто. В контексте прерывания могут использоваться только спин-блокировки, и только семафор может удерживаться процессом, который находится в состоянии ожидания. В табл. 9.6 показан обзор требований того, какой тип блокировок использовать.

Таблица 9.6. Что следует использовать: семафоры или спин-блокировки

Требование

Рекомендуемый тип блокировки

Блокировка с малыми накладными затратами (low overhead)

Малое время удержания блокировки

Длительное время удержания блокировки

Необходимо использовать блокировку в контексте прерывания

Необходимо переходить в состояние ожидания (sleep) при захваченной блокировке

Спин-блокировки более предпочтительны

Спин-блокировки более предпочтительны

Семафоры более предпочтительны

Необходима спин-блокировка

Необходимо использовать семафоры

Условные переменные

Условные переменные (conditional variable, completion variable) -- простое средство синхронизации между двумя заданиями, которые работают в режиме ядра, когда необходимо, чтобы одно задание послало сигнал другому о том, что произошло некоторое событие. При этом одно задание ожидает на условной переменной, пока другое задание не выполнит некоторую работу. Когда другое задание завершит выполнение своей работы, оно использует условную переменную для того, чтобы возвратить к выполнению все ожидающие на ней задания. Если это кажется похожим на работу семафора, то именно так оно и есть, идея та же. В действительности, условные переменные просто обеспечивают простое решение проблемы, для которой в других ситуациях используются семафоры. Например, в системном вызове vfork () условная переменная используется для возврата к выполнению родительского процесса при завершении порожденного.

Условные переменные представляются с помощью структуры struct completion, которая определена в файле <linux/completion.h>.

Статически условная переменная может быть создана с помощью макроса

DECLARE_COMPLETION(mr_comp);

Динамически созданная условная переменная может быть инициализирована с помощью функции init_completion ().

Задание, которое должно ожидать на условной переменной, вызывает функцию wait_for_completion (). После того как наступило ожидаемое событие, вызов функции complete () посылает сигнал заданию, которое ожидает на условной переменной, и это задание возвращается к выполнению. В табл. 9.7 приведены методы работы с условными переменными.

Таблица. 9.7. Методы работы с условными переменными

Метод

Описание

init_completion (struct completion *)

wait_for_completion (struct completion *)

complete (struct completion *)

Инициализация динамически созданной условной переменной в заданной области памяти

Ожидание сигнала на указанной условной переменной

Отправка сигнала всем ожидающим заданиям и возвращение их к выполнению

Для примеров использования условных переменных смотрите файлы kernel/ sched.c и kernel/fork. с. Наиболее часто используются условные переменные, которые создаются динамически, как часть структур данных. Код ядра, который ожидает на инициализацию структуры данных, вызывает функцию wait_for_ completion (). Когда инициализация закончена, ожидающие задания возвращаются к выполнению с помощью вызова функции complete ().

BLK: Большая блокировка ядра

Добро пожаловать к "рыжему пасынку" ядра. Большая блокировка ядра (Big Kernel Lock, BKL) -- это глобальная спин-блокировка, которая была создана специально для того, чтобы облегчить переход от первоначальной реализации SMP в операционной системе Linux к мелкоструктурным блокировкам. Блокировка BKL имеет следующие интересные свойства.

· Во время удержания BKL можно переходить в состояние ожидания. Блокировка автоматически освобождается, когда задание переходит в состояние ожидания, и снова захватывается, когда задание планируется на выполнение. Конечно, это не означает, что безопасно переходить в состояние ожидания при удержании BKL, просто это можно делать и это не приведет к взаимоблокировке.

· Блокировка BKL рекурсивна. Один процесс может захватывать эту блокировку несколько раз подряд, и это не приведет к самоблокировке, как в случае обычных спин-блокировок.

· Блокировка BKL может использоваться только в контексте процесса.

· Блокировка BKL -- это от лукавого.

Рассмотренные свойства дали возможность упростить переход от ядер серии 2.0 к серии 2.2. Когда в ядро 2.0 была введена поддержка SMP, только одно задание могло выполняться в режиме ядра в любой момент времени (конечно, сейчас ядро распараллелено очень хорошо -- пройден огромный путь). Целью создания ядра серии 2.2 было обеспечение возможности параллельного выполнения кода ядра на нескольких процессорах. Блокировка BKL была введена для того, чтобы упростить переход к мелкоструктурным блокировкам. В те времена она оказала большую помощь, а сегодня она приводит к ухудшению масштабируемости.

Использовать блокировку BKL не рекомендуется. На самом деле, новый код никогда не должен использовать BKL. Однако эта блокировка все еще достаточно интенсивно используется в некоторых частях ядра. Поэтому важно понимать особенности большой блокировки ядра и интерфейса к ней. Блокировка BKL ведет себя, как обычная спин-блокировка, за исключением тех особенностей, которые были рассмотрены выше. Функция lock_kernel () позволяет захватить блокировку, а функция unlockjcernel () - освободить блокировку. Каждый поток выполнения может рекурсивно захватывать эту блокировку, но после этого необходимо столько же раз вызвать функцию unlockkernel (). При последнем вызове функции освобождения блокировки блокировка будет освобождена. Функция kernel_locked() возвращает ненулевое значение, если блокировка в данный момент захвачена, в противном случае возвращается нуль. Эти интерфейсы определены в файле <linux/smp_lock. h>. Рассмотрим простой пример использования этой блокировки.

lock_kernel();

/*

Критический раздел, который синхронизирован со всеми пользователями блокировки BKL...

Заметим, что здесь можно безопасно переходить в состояние ожидания и блокировка будет прозрачным образом освобождаться. После перепланирования блокировка будет прозрачным образом снова захватываться. Это гарантирует, что не возникнет состояния взаимоблокировки, но все-таки лучше не переходить в состояние ожидания, если необходимо гарантировать защиту данных! */ unlockjcernel();

Когда эта блокировка захвачена, происходит запрещение преемптивности. Для ядер, скомпилированных под однопроцессорную машину, код BKL на самом деле не выполняет никаких блокировок. В табл. 9.8 приведен полный список функций работы с BKL.

Таблица 9,8. Функции работы с большой блокировкой ядра

Функция

Описание

lock_kernel ()

Unlock_kernel()

kernel_locked ()

Захватить блокировку BKL

Освободить блокировку BKL

Возвратить ненулевое значение, если блокировка захвачена, и нуль -- в противном случае

Одна из самых главных проблем, связанных с большой блокировкой ядра, -- как определить, что защищается с помощью данной блокировки. Часто блокировка BKL ассоциируется с кодом (например, она "синхронизирует вызовы функции f оо () "), а не с данными ("защита структуры foo "). Это приводит к тому, что заменить BKL обычными спин-блокировками бывает сложно, потому что нелегко определить, что же все-таки необходимо блокировать. На самом деле, подобная замена еще более сложна, так как необходимо учитывать все взаимоотношения между всеми участками кода, которые используют эту блокировку.

синхронизация ядро атомарный блокировка

Секвентные блокировки

Секвентная блокировка (seq lock) -- это новый тип блокировки, который появился в ядрах серии 2.6. Эти блокировки предоставляют очень простой механизм чтения и записи совместно используемых данных. Работа таких блокировок основана на счетчике последовательности событий. Перед записью рассматриваемых данных захватывается спин-блокировка, и значение счетчика увеличивается на единицу. После записи данных значение счетчика снова увеличивается на единицу, и спин-блокировка освобождается, давая возможность записи другим потокам. Перед чтением и после чтения данных проверяется значение счетчика. Если два полученных значения одинаковы, то во время чтения данных новый акт записи не начинался. Если к тому же оба эти значения четные, то к моменту начала чтения акт записи был закончен (при захвате блокировки на запись значение счетчика становится нечетным, а перед освобождением -- снова четным, так как изначальное значение счетчика равно нулю).

Определение секвентной блокировки можно записать следующим образом.

seqlock_t mr_seq_lock = SEQLOCK_UNLOCKED;

Участок кода, который осуществляет запись, может выглядеть следующим образом.

write_seqlock(&mr_seq_lock);

/* блокировка захвачена на запись... */

write_sequnlock(&mr_seq_lock);

Это выглядит, как работа с обычной спин-блокировкой. Необычность появляется в коде чтения, который несколько отличается от ранее рассмотренных.

unsigned long seq;

do {

seq = read_seqbegin(&mr_seq_lock);

/* здесь нужно читать данные... * I

} while (read_seqretry(&mr_seq_lock, seq));

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

Средства запрещения преемптивности

Так как ядро является вытесняемым, процесс, работающий в режиме ядра, может прекратить выполнение в любой момент, чтобы позволить выполняться более высокоприоритетному процессу. Это означает, что новое задание может начать выполняться в том же критическом участке, в котором выполнялось вытесненное задание. Для того чтобы предотвратить такую возможность, код, который отвечает за преемптивность ядра, использует спин-блокировки в качестве маркеров, чтобы отмечать участки "непреемптивности". Если спин-блокировка захвачена, то ядро является невытесняемым. Так как проблемы, связанные с параллелизмом, в случае SMP и преемптивного ядра одинаковы, то, если ядро уже является безопасным для SMP-обработки, такое простое дополнение позволяет также сделать ядро безопасным и при вытеснении.

Будем надеяться, что это действительно так. На самом деле возникают некоторые ситуации, в которых нет необходимости использовать спин-блокировки, но нужно запрещать преемптивность ядра. Наиболее часто ситуация такого рода возникает из-за данных, привязанных к определенным процессорам (per-processor data). Если используются данные, уникальные для каждого процессора, то может быть необязательным защищать их с помощью спин-блокировок, потому что только один процессор может получать доступ к этим данным. Если Никакая спин-блокировка не захвачена и ядро является преемптивным, то появляется возможность доступа к тем же переменным для вновь запланированного задания, как показано в следующем примере.

задание А манипулирует переменной f оо

задание А вытесняется

задание В планируется на выполнение

задание В манипулирует переменной foo

задание В завершается

задание А планируется на выполнение

задание А манипулирует переменной foo

Следовательно, даже для однопроцессорного компьютера к некоторой переменной может псевдопараллельно обращаться несколько процессов. В обычной ситуации для такой переменной требуется спин-блокировка (для защиты при истинном параллелизме на многопроцессорной машине). Если эта переменная связана с одним процессором, то для нее не требуется блокировка.

Для решения указанной проблемы преемптивность ядра можно запретить с помощью функции preempt_disable (). Этот вызов может быть вложенным, т.е. функцию можно вызывать много раз подряд. Для каждого такого вызова требуется соответствующий вызов функции preempt_enable (). Последний вызов функции preempt_enable () разрешает преемптивность, как показано в следующем примере.

preempt_disable();

/* преемптивнэсть запрещена... */

preempt_enable ();

Счетчик преемптивности текущего процесса содержит значение, равное количеству захваченных этим процессом блокировок плюс количество вызовов функции preemptdi sable (). Если значение этого счетчика равно нулю, то ядро является вытесняемым. Если значение этого счетчика больше или равно единице, то ядро не вытесняемое. Данный счетчик невероятно полезен для отладки атомарных операций совместно с переходами в состояние ожидания. Функция preempt_count () возвращает значение данного счетчика. В табл. 9.9 показан полный список функций управления преемптивностью.

Таблица 9.9. Функции управления преемптивностью ядра

Функция

Описание

preempt_disable()

preerapt_enable()

preempt_enable_no_resched()

preempt_count()

Запретить вытеснение кода ядра

Разрешить вытеснение кода ядра

Разрешить вытеснение кода ядра, но не перепланировать выполнение процесса

Возвратить значение счетчика преемптивности

Более полное решение задачи работы с данными, связанными с определенным процессором, -- это получение номера процессора (который используется в качестве индекса для доступа к данным, связанным с определенным процессором) с помощью функции get_cpu (). Эта функция запрещает преемптивность ядра перед тем, как возвратить номер текущего процессора.

int cpu = get_cpu();

/* работаем с данными, связанными с текущим процессором... */

/* работа закончена, снова разрешаем вытеснение кода ядра */ put_cpu();

Барьеры и порядок выполнения

В случае, когда необходимо иметь дело с синхронизацией между разными процессорами или разными аппаратными устройствами, иногда возникает требование, чтобы чтение памяти (load) или запись в память (save) выполнялись в том же порядке, как это указано в исходном программном коде. При работе с аппаратными устройствами часто необходимо, чтобы некоторая указанная операция чтения была выполнена перед другими операциями чтения или записи. В дополнение к этому, на симметричной многопроцессорной системе может оказаться необходимым, чтобы операции записи выполнялись строго в том порядке, как это указано в исходном программном коде (обычно для того, чтобы гарантировать, что последовательные операции чтения получают данные в том же порядке). Эти проблемы усложняются тем, что как компилятор, так и процессор могут менять порядок операций чтения и записи6 для повышения производительности. К счастью, все процессоры, которые переопределяют порядок операций чтения или записи предоставляют машинные инструкции, которые требуют выполнения операций чтения-записи памяти в указанном порядке. Также существует возможность дать инструкцию компилятору, что нельзя изменять порядок выполнения операций при переходе через определенную точку программы. Эти инструкции называются барьерами (barrier).

Рассмотрим следующий код.

а = 1;

b = 2;

На некоторых процессорах запись нового значения в область памяти, занимаемую переменной Ь, может выполниться до того, как будет записано новое значение в область памяти переменной а. Компилятор может выполнить такую перестановку статически и внести в файл объектного кода, что значение переменной b должно быть установлено перед переменной а. Процессор может изменить порядок выполнения динамически путем предварительной выборки и планирования выполнения внешне вроде бы независимых инструкций для повышения производительности. В большинстве случаев такая перестановка операций будет оптимальной, так как между переменными а и b нет никакой зависимости. Тем не менее иногда программисту все-таки виднее.

Хотя в предыдущем примере и может быть изменен порядок выполнения, ни процессор, ни компилятор никогда не будут менять порядок выполнения следующего кода, где переменные а и b являются глобальными.

а = 1;

b = a;

Это происходит потому, что в последнем случае четко видно зависимость между переменными а и Ь. Однако ни компилятор, ни процессор не имеют никакой информации о коде, который выполняется в других контекстах. Часто важно, чтобы результаты записи в память "виделись" в нужном порядке другим кодом, который выполняется за пределами нашей досягаемости. Такая ситуация часто имеет место при работе с аппаратными устройствами, а также возникает на многопроцессорных машинах.

Функция rmb () позволяет установить барьер чтения памяти (read memory barrier). Она гарантирует, что никакие операции чтения памяти, которые выполняются перед вызовом функции rmb (), не будут переставлены местами с операциями, которые выполняются после этого вызова. Иными словами, все операции чтения, которые указаны до этого вызова, будут выполнены перед этим вызовом, а все операции чтения, которые указаны после этого вызова никогда не будут выполняться перед ним.

Функция wmb () позволяет установить барьер записи памяти (write barrier).Она работает так же, как и функция rmb (), но не с операциями чтения, а с операциями записи -- гарантируется, что операции записи, которые находятся по разные стороны барьера, никогда не будут переставлены местами друг с другом.

Функция mb () позволяет создать барьер на чтение и запись. Никакие операции чтения и записи, которые указаны по разные стороны вызова функции mb (), не будут переставлены местами друг с другом. Эта функция предоставляется пользователю, так как существует машинная инструкция (часто та же инструкция, что используется вызовом rmb ()), которая позволяет установить барьер на чтение и запись.

Вариант функции rmb() -- read_barrier_depends () -- обеспечивает создание барьера чтения, но только для тех операций чтения, от которых зависят следующие за ними операции чтения. Гарантируется, что все операции чтения, которые указаны перед барьером выполнятся перед теми операциями чтения, которые находятся после барьера и зависят от операций чтения, идущих перед барьером. Все понятно? В общем, эта функция позволяет создать барьер чтения, так же как и функция rmb (), но этот барьер будет установлен только для некоторых операций чтения -- тех, которые зависят друг от друга.

Для некоторых аппаратных платформ функция readbarrierdepends () выполняется значительно быстрее, чем функция rmb (), так как для этих платформ функция readbarrierdepends () просто не нужна и вместо нее выполняется инструкция поор (нет операции).

Рассмотрим пример использования функций mb () и rmb (). Первоначальное значение переменной а равно 1, а переменной b равно 2.

Поток 1

а=3;

mb<);

b=4;

Поток 2

-

-

c=b;

rmb();

d=a;

Без использования барьеров памяти для некоторых процессоров возможна ситуация, в которой после выполнения этих фрагментов кода переменной с присвоится новое значение переменной b, в то время как переменной d присвоится старое значение переменной а. Например, переменная с может стать равной 4 (что мы и хотим), а переменная d может остаться равной 1 (чего мы не хотим). Использование функции mb () позволяет гарантировать, что переменные а и b записываются в указанном порядке, а функция rmb () гарантирует, что чтение переменных b и а будет выполнено в указанном порядке.

Такое изменение порядка выполнения операций может возникнуть из-за того, что современные процессоры обрабатывают и передают на выполнение инструкции в измененном порядке для того, чтобы оптимизировать использование конвейеров. Это может привести к тому, что инструкции чтения переменных b и а выполнятся не в том порядке. Функции rmb () и wmb () соответствуют инструкциям, которые заставляют процессор выполнить все незаконченные операции чтения и записи перед тем, как продолжить работу далее.

Рассмотрим простой пример случая, когда можно использовать функцию read_ barrier_depends () вместо функции rmb (). В этом примере изначально переменная а равна 1, b -- 2, а р -- &b.

Поток 1

а=3;

mb ();

p=&a;

-

-

Поток 2

-

-

рр=р;

read__barrier_depends ()

b=*pp;

Снова без использования барьеров памяти появляется возможность того, что переменной b будет присвоено значение *рр до того, как переменной рр будет присвоено значение переменной р. Функция read_barrier_depends () обеспечивает достаточный барьер, так как считывание значения *рр зависит от считывания переменной р. Здесь также будет достаточно использовать функцию rmb (), но поскольку операции чтения зависимы между собой, то можно использовать потенциально более быструю функцию read_barrier_depends (). Заметим, что в обоих случаях требуется использовать функцию mb () для того, чтобы гарантировать необходимый порядок выполнения операций чтения-записи в потоке 1.

Макросы smp_rmb (), smp_rmb (), smp_mb () и smpread_barrier_depends () позволяют выполнить полезную оптимизацию. Для SMP-ядра они определены как обычные барьеры памяти, а для ядра, рассчитанного на однопроцессорную машину, -- только как барьер компилятора. Эти SMP-варианты барьеров можно использовать, когда ограничения на порядок выполнения операций являются специфичными для SMP-систем.

Функция barrier () предотвращает возможность оптимизации компилятором операций считывания и записи данных, если эти операции находятся по разные стороны от вызова данной функции (т.е. запрещает изменение порядка операций). Компилятор не изменяет порядок операций записи и считывания в случаях, когда это может повлиять на правильность выполнения кода, написанного на языке С, или на существующие зависимости между данными. Однако у компилятора нет информации о событиях, которые могут произойти вне текущего контекста. Например, компилятор не может иметь информацию о прерываниях, в контексте которых может выполняться считывание данных, которые в данный момент записываются. Например, по этой причине может оказаться необходимым гарантировать, что операция записи выполнится перед операцией считывания. Указанные ранее барьеры памяти работают и как барьеры компилятора, но барьер компилятора значительно быстрее, чем барьер памяти (практически не влияет на производительность). Использование барьера компилятора на практике является опциональным, так как он просто предотвращает возможность того, что компилятор что-либо изменит.

В табл. 9.10 приведен полный список функций установки барьеров памяти и компилятора, которые доступны для разных аппаратных платформ, поддерживаемых ядром Linux.

Следует заметить, что эффекты установки барьеров могут быть разными для разных аппаратных платформ. Например, если машина не изменяет порядок операций записи (как в случае набора микросхем Intel х86), то функция wmb () не выполняет никаких действий. Можно использовать соответствующий барьер памяти для самой плохой ситуации (т.е. для процессора с самым плохим порядком выполнения), и ваш код будет скомпилирован оптимально для вашей аппаратной платформы.

Таблица 9.10. Средства установки барьеров компилятора и памяти

Барьер

Описание

rmb()

read barrier_depends()

wmb ()

mb()

smp_rmb()

smp_read_barrier_depends()

smp_wmb()

smp_mb ()

barrier()

Предотвращает изменение порядка выполнения операций чтения данных из памяти при переходе через барьер

Предотвращает изменение порядка выполнения операций чтения данных из памяти при переходе через барьер, но только для операций чтения, которые зависимы друг от друга

Предотвращает изменение порядка выполнения операций записи данных в память при переходе через барьер

Предотвращает изменение порядка выполнения операций чтения и записи данных при переходе через барьер

Для SMP-ядер эквивалентно функции rmb (), а для ядер, рассчитанных на однопроцессорные машины, эквивалентно функции barrier!)

Для SMP-ядер эквивалентно функции readbarrierdepends (), а для ядер, рассчитанных на однопроцессорные машины, эквивалентно функции barrier ()

Для SMP-ядер эквивалентно функции wmb (), а для ядер, рассчитанных на однопроцессорные машины, эквивалентно функции barrier()

Для SMP-ядер эквивалентно функции mb (), а для ядер, рассчитанных на однопроцессорные машины, эквивалентно функции barrier ()

Предотвращает оптимизации компилятора по чтению и записи данных при переходе через барьер

Резюмирование по синхронизации

В этой главе было рассказано о том, как применять на практике понятия, описанные в предыдущей главе, чтобы лучше разобраться с функциями ядра, которые помогают осуществлять синхронизацию и параллелизм. Вначале были рассмотрены самые простые методы, которые позволяют гарантировать синхронизацию, -- атомарные операции. Далее были описаны спин-блокировки -- наиболее часто используемые типы блокировок в ядре, которые построены на основе периодической проверки в цикле условия освобождения блокировки и позволяют гарантировать, что доступ к ресурсу получит только один поток выполнения. После этого были рассмотрены семафоры -- блокировки, которые переводят вызывающий процесс в состояние ожидания, а также более специализированные типы элементов синхронизации -- условные переменные и секвентные блокировки. Мы получили удовольствие от блокировки BKL, рассмотрели методы запрещения вытеснения кода ядра и коснулись барьеров. Диапазон большой.

Вооруженные арсеналом методов синхронизации из данной главы теперь вы сможете писать код ядра, который защищен от состояний конкуренции за ресурсы и позволяет обеспечить необходимую синхронизацию с помощью самого подходящего для этого инструментария.

Размещено на Allbest.ru


Подобные документы

  • Функции программного интерфейса операционной системы Windows, предназначенные для работы с семафорами. Средства синхронизации Win32 АРI, основанные на использовании объектов исполнительной системы с дескрипторами. Проблемы при использовании семафоров.

    реферат [67,4 K], добавлен 06.10.2010

  • Классификация компьютерной памяти. Использование оперативной, статической и динамической оперативной памяти. Принцип работы DDR SDRAM. Форматирование магнитных дисков. Основная проблема синхронизации. Теория вычислительных процессов. Адресация памяти.

    курсовая работа [1,5 M], добавлен 28.05.2016

  • История разработки многозадачной операционной системы POSIX-стандарта - FreeBSD; описание ее виртуальной памяти, файловой системы, уровня защиты. Описание основных средств синхронизации процессов - сигналов и семафоров. Способы блокировки файлов.

    презентация [584,2 K], добавлен 02.06.2011

  • Разработка приложения, автоматизирующего процесс синхронизации файлов между сменным носителем и каталогом на другом диске. Классы для работы с файловой системой. Интерфейс программы и способы взаимодействия пользователя с ним. Создание новой синхропары.

    курсовая работа [632,0 K], добавлен 21.10.2015

  • Архитектура многопроцессорных систем с общей шиной и с неоднородным доступом к памяти. Структура кэш памяти. Взаимодействие user space с kernel space. Средства синхронизации ядра Linux. Обход каталогов страниц. Инструментация кода средствами Clang.

    дипломная работа [513,7 K], добавлен 14.11.2017

  • Основные ограничения синхронизации, необходимые для корректного функционирования системы. Добавление в код производителя и потребителя операторов синхронизации для обеспечения ее корректной работы. Сигнал конечного буфера производителя-потребителя.

    курсовая работа [167,0 K], добавлен 05.12.2012

  • Диаграмма последовательности работы в интерфейсе программы. Интерфейсы необходимых классов и их взаимодействие. Средства обработки исключений. Начальный экран работы программы. Инструменты работы с персоналом. Основные функции работника регистратуры.

    курсовая работа [3,7 M], добавлен 09.10.2013

  • Модель памяти как набор опций компилятора, ее виды в BC++2.0, размеры и взаимное расположение. Назначение сегментных регистров в различных моделях памяти, порядок просмотра переменных. Основные и дополнительные функции динамических переменных в памяти.

    лабораторная работа [28,4 K], добавлен 06.07.2009

  • Программно-аппаратный комплекс производства компании Nvidia. Код для сложения векторов, представленный в CUDA. Вычислительная схема СPU с несколькими ядрами SMP. Выделение памяти на видеокарте. Проведение синхронизации работы основной и GPU программ.

    презентация [392,5 K], добавлен 14.12.2013

  • Понятие процесса и потока, характеристика их свойств и особенности создания. Требования к алгоритмам синхронизации, суть взаимного исключения на примере монитора и семафора. Методика изучения элективного курса "Процессы в операционной системе Windows".

    дипломная работа [1,7 M], добавлен 03.06.2012

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.