What is mutex in linux

Generic Mutex SubsystemВ¶

What are mutexes?В¶

In the Linux kernel, mutexes refer to a particular locking primitive that enforces serialization on shared memory systems, and not only to the generic term referring to ‘mutual exclusion’ found in academia or similar theoretical text books. Mutexes are sleeping locks which behave similarly to binary semaphores, and were introduced in 2006[1] as an alternative to these. This new data structure provided a number of advantages, including simpler interfaces, and at that time smaller code (see Disadvantages).

ImplementationВ¶

Mutexes are represented by ‘struct mutex’, defined in include/linux/mutex.h and implemented in kernel/locking/mutex.c. These locks use an atomic variable (->owner) to keep track of the lock state during its lifetime. Field owner actually contains struct task_struct * to the current lock owner and it is therefore NULL if not currently owned. Since task_struct pointers are aligned to at least L1_CACHE_BYTES, low bits (3) are used to store extra state (e.g., if waiter list is non-empty). In its most basic form it also includes a wait-queue and a spinlock that serializes access to it. Furthermore, CONFIG_MUTEX_SPIN_ON_OWNER=y systems use a spinner MCS lock (->osq), described below in (ii).

When acquiring a mutex, there are three possible paths that can be taken, depending on the state of the lock:

fastpath: tries to atomically acquire the lock by cmpxchg()ing the owner with the current task. This only works in the uncontended case (cmpxchg() checks against 0UL, so all 3 state bits above have to be 0). If the lock is contended it goes to the next possible path.

midpath: aka optimistic spinning, tries to spin for acquisition while the lock owner is running and there are no other tasks ready to run that have higher priority (need_resched). The rationale is that if the lock owner is running, it is likely to release the lock soon. The mutex spinners are queued up using MCS lock so that only one spinner can compete for the mutex.

The MCS lock (proposed by Mellor-Crummey and Scott) is a simple spinlock with the desirable properties of being fair and with each cpu trying to acquire the lock spinning on a local variable. It avoids expensive cacheline bouncing that common test-and-set spinlock implementations incur. An MCS-like lock is specially tailored for optimistic spinning for sleeping lock implementation. An important feature of the customized MCS lock is that it has the extra property that spinners are able to exit the MCS spinlock queue when they need to reschedule. This further helps avoid situations where MCS spinners that need to reschedule would continue waiting to spin on mutex owner, only to go directly to slowpath upon obtaining the MCS lock.

Читайте также:  Body editor для windows

slowpath: last resort, if the lock is still unable to be acquired, the task is added to the wait-queue and sleeps until woken up by the unlock path. Under normal circumstances it blocks as TASK_UNINTERRUPTIBLE.

While formally kernel mutexes are sleepable locks, it is path (ii) that makes them more practically a hybrid type. By simply not interrupting a task and busy-waiting for a few cycles instead of immediately sleeping, the performance of this lock has been seen to significantly improve a number of workloads. Note that this technique is also used for rw-semaphores.

SemanticsВ¶

The mutex subsystem checks and enforces the following rules:

Only one task can hold the mutex at a time.

Only the owner can unlock the mutex.

Multiple unlocks are not permitted.

Recursive locking/unlocking is not permitted.

A mutex must only be initialized via the API (see below).

A task may not exit with a mutex held.

Memory areas where held locks reside must not be freed.

Held mutexes must not be reinitialized.

Mutexes may not be used in hardware or software interrupt contexts such as tasklets and timers.

These semantics are fully enforced when CONFIG DEBUG_MUTEXES is enabled. In addition, the mutex debugging code also implements a number of other features that make lock debugging easier and faster:

Uses symbolic names of mutexes, whenever they are printed in debug output.

Point-of-acquire tracking, symbolic lookup of function names, list of all locks held in the system, printout of them.

Detects self-recursing locks and prints out all relevant info.

Detects multi-task circular deadlocks and prints out all affected locks and tasks (and only those tasks).

InterfacesВ¶

Statically define the mutex:

Dynamically initialize the mutex:

Acquire the mutex, uninterruptible:

Acquire the mutex, interruptible:

Acquire the mutex, interruptible, if dec to 0:

Unlock the mutex:

Test if the mutex is taken:

DisadvantagesВ¶

Unlike its original design and purpose, ‘struct mutex’ is among the largest locks in the kernel. E.g: on x86-64 it is 32 bytes, where ‘struct semaphore’ is 24 bytes and rw_semaphore is 40 bytes. Larger structure sizes mean more CPU cache and memory footprint.

When to use mutexesВ¶

Unless the strict semantics of mutexes are unsuitable and/or the critical region prevents the lock from being shared, always prefer them to any other locking primitive.

© Copyright The kernel development community.

Источник

Национальная библиотека им. Н. Э. Баумана
Bauman National Library

Персональные инструменты

Мьютекс

Мью́текс (англ. mutex , от англ. mutual exclusion — «взаимное исключение») — является аналогом одноместного семафора, в программировании необходим для сопоставления синхронно выполняющихся потоков. [1] . Мью́текс представляет собой концепцию программирования, которая используется для решения вопросов многопоточности. Мьютекс отличается от семафора тем, что допускает только один поток в контролируемом участке, заставляя другие потоки, которые пытаются получить доступ к этому разделу ждать, пока первый поток не вышел из этого раздела.

Принимает два значенения:

  • открыт — поток может войти в свою критическую секцию;
  • закрыт — поток не может войти в критическую секцию.
Читайте также:  Windows 10 папка начального экрана

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

Цель использования мьютексов — защита данных от повреждения в результате асинхронных изменений (состояние гонки), однако могут порождаться другие проблемы — такие, как взаимная блокировка (клинч). Для описания мьютекса требуется всего один бит, хотя чаще используется целая переменная, у которой 0 означает не блокированное состояние, а все остальные значения соответствуют блокированному состоянию. Значение мьютекса устанавливается двумя процедурами. Если поток собирается войти в критическую область, он вызывает процедуру mutex_lock. Если мьютекс не заблокирован, запрос выполняется и вызывающий поток может попасть в критическую область [2] . Если mutex закрыт, то поток пытающийся войти в критическую секцию блокируется.

Содержание

Мьютекс в операционных системах

Использование mutex в Windows

  • Создание mutex CreateMutex (параметры_безопасности, собственность_потока, имя) Поименный mutex может использоваться для синхронизации процессов
  • Открытие mutex OpenMutex (параметры_безопасности, собственность_потока, имя)
  • Ожидание и захват mutex WaitForSingleObject (дескриптор_mutex, время_ожидания)
  • Освобождение mutex ReleaseMutex (дескриптор_mutex)

Использование mutex в Linux

Mutex в Linux — это объект для синхронизации потоков в рамках одного процесса

  • Описание mutex pthread_mutex_t_mutex ;
  • Инициализация mutex pthread_mutex_init (&mutex, NULL) ;
  • Перед входом в критическую секцию поток должен попытаться захватить mutex. Если это не удается, то поток блокируется до освобождения mutex pthread_mutex_lock (&mutex) ;
  • При выходе из критической секции поток должен освободить mutex pthread_mutex_unlock (&mutex) ;
Методы [3]
Имя Описание
Close() Освобождает все ресурсы, удерживаемые текущим объектом WaitHandle.(Наследуется от WaitHandle.)
CreateObjRef(Type) Создает объект, который содержит всю необходимую информацию для создания прокси-сервера, используемого для взаимодействия с удаленным объектом.(Наследуется от MarshalByRefObject.)
Dispose() Освобождает все ресурсы, используемые текущим экземпляром класса WaitHandle.(Наследуется от WaitHandle.)
Equals(Object) Определяет, равен ли заданный объект текущему объекту.(Наследуется от Object.)
GetAccessControl() Получает объект MutexSecurity, представляющий безопасность управления доступом для именованного мьютекса.
GetHashCode() Играет роль хэш-функции для определённого типа.

(Наследуется от Object.)

GetLifetimeService() Извлекает объект обслуживания во время существования, который управляет политикой времени существования данного экземпляра.(Наследуется от MarshalByRefObject.)
GetType() Возвращает объект класса Type для текущего экземпляра. (Наследуется от Object.)
InitializeLifetimeService() Возвращает объект обслуживания во время существования для управления политикой времени существования данного экземпляра.(Наследуется от MarshalByRefObject.)
OpenExisting(String) Открывает указанный именованный мьютекс, если он уже существует.
OpenExisting(String, MutexRights) Открывает указанный именованный мьютекс, если он уже существует, с требуемыми правами доступа.
ReleaseMutex() Освобождает объект Mutex один раз.
SetAccessControl(MutexSecurity) Задает безопасность управления доступом для именованного системного мьютекса.
ToString() Возвращает строковое представление текущего объекта.

(Наследуется от Object.)

TryOpenExisting(String, Mutex) Открывает указанный именованный мьютекс, если он уже существует, и возвращает значение, указывающее, успешно ли выполнена операция.
TryOpenExisting(String, MutexRights, Mutex) Открывает указанный именованный мьютекс, если он уже существует, с требуемыми правами доступа, и возвращает значение, указывающее, успешно ли выполнена операция.
WaitOne() Блокирует текущий поток до получения сигнала объектом WaitHandle.(Наследуется от WaitHandle.)
WaitOne(Int32) Блокирует текущий поток до получения текущим дескриптором WaitHandle сигнала, используя 32-разрядное целое число со знаком для указания интервала времени в миллисекундах.(Наследуется от WaitHandle.)
WaitOne(Int32, Boolean) Блокирует текущий поток до получения сигнала текущим объектом WaitHandle, используя 32-разрядное целое число со знаком для задания периода времени и указывая, следует ли выйти из домена синхронизации до начала ожидания.(Наследуется от WaitHandle.)
WaitOne(TimeSpan) Блокирует текущий поток до получения сигнала текущим экземпляром, используя значение типа TimeSpan для указания интервала времени.(Наследуется от WaitHandle.)
WaitOne(TimeSpan, Boolean) Блокирует текущий поток до получения сигнала текущим экземпляром, используя значение типа TimeSpan для задания интервала времени и указывая, следует ли выйти из домена синхронизации до начала ожидания.(Наследуется от WaitHandle.)

Свойства

  • Запоминание владельца — освободить мьютекс может только поток, который его захватил;
  • Повторяющийся характер — поток может неоднократно захватить мьютекс (вызвать aquire() ); для освобождения мьютекса поток должен соответствующее число раз захватить release() ;
  • Наследование приоритета — поток, который захватил мьютекс, временно получает максимальный из приоритетных потоков, которые ждут освобождения этого мьютекса.

Структура управления мьютексом

Каждый мьютекс ассоциируется со структурой управления:

Элементы структуры мьютекса
wait_queue Очередь задач, ожидающих освобождение мьютекса
mutex_queue Элемент списка заблокированных задачей мьютексов
ock_mutex_queue Системная очередь заблокированных мьютексов
attr Атрибут (тип обхода инверсии приоритетов) мьютекса
holder Указатель на TCB(Task Control Block) задачи, блокирующей мьютекс
ceil_priority Максимальный приоритет из задач, которые могут использовать ресурс (требуется для протокола увеличения приоритета)
cnt Зарезервировано
id_mutex Поле идентификации объекта как мьютекса

Структура мьютекса доступна только при определении TN_DEBUG . Тем не менее, прямой доступ к элементам структуры мьютекса крайне не рекомендуется, так как это является вмешательством в работу планировщика и других сервисов RTOS. [4]

Синхронизация файловых операций Mutex

Задача сводится к тому как заставить две программы работать с одним файлом, когда одна программа может писать, а вторая должна читать. Задача как избежать конфликтов при данной ситуации. Создадим два проекта, как Win32 console, один с именем WriteData, а другой с именем ReadData в каталоге TestMutex. Так будет и в прилагаемом проекте.

Основа программы функция CreateMutex :

Теперь весь этот механизм запускается с помощью ReleaseMutex после использования указатель HANDLE нужно закрыть CloseHandle .

Для доступа к Mutex теперь его нужно открыть с тем же именем. И поставить флаг MUTEX_ALL_ACCESS . Функция WaitForSingleObject будет стоять пока доступ не будет получен.

Запустите одновременно две программы. Программа чтения будет ждать пока программа записи не разрешит доступ. Вот примерно так как на экране.

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

Разница между мьютексом и Семафор (Операционные Системы)

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

Источник

Читайте также:  Проблемы при использовании linux
Оцените статью