kernel/services/synchronization/mutexes.html · kernel/services/synchronization/mutexes.rst 공식 원문 ↗

Zephyr 3.7.0 · Kernel · Synchronization

Mutexes

Ownership과 lock count로 resource를 배타 보호하고 priority inversion을 inheritance로 줄입니다.

Official pathkernel/services/synchronization/mutexes.html
Source filekernel/services/synchronization/mutexes.rst
Source versionZephyr 3.7.0
TranslationFull · reviewed

본문 기준: Zephyr 3.7.0 LTS3 · 이후 유지보수 패치는 별도입니다.

Part 1

요약·해설

Reentrant unlock 균형, priority ceiling과 multi-mutex 제약, futex·sys_mutex 차이를 설명합니다.

Ownership

owner · lock count

Priority

inheritance · ceiling

Variants

k_mutex · futex · sys_mutex

Part 2

접을 수 있는 영어 원문 전체

영어 원문 전체 펼치기
원문 SHA-256 C33DA75BB8BD6301803CA1C2046D0544774CE12C29DBD442C198114281C103E9
.. _mutexes_v2:

Mutexes
#######

A :dfn:`mutex` is a kernel object that implements a traditional
reentrant mutex. A mutex allows multiple threads to safely share
an associated hardware or software resource by ensuring mutually exclusive
access to the resource.

.. contents::
    :local:
    :depth: 2

Concepts
********

Any number of mutexes can be defined (limited only by available RAM). Each mutex
is referenced by its memory address.

A mutex has the following key properties:

* A **lock count** that indicates the number of times the mutex has been locked
  by the thread that has locked it. A count of zero indicates that the mutex
  is unlocked.

* An **owning thread** that identifies the thread that has locked the mutex,
  when it is locked.

A mutex must be initialized before it can be used. This sets its lock count
to zero.

A thread that needs to use a shared resource must first gain exclusive rights
to access it by **locking** the associated mutex. If the mutex is already locked
by another thread, the requesting thread may choose to wait for the mutex
to be unlocked.

After locking a mutex, the thread may safely use the associated resource
for as long as needed; however, it is considered good practice to hold the lock
for as short a time as possible to avoid negatively impacting other threads
that want to use the resource. When the thread no longer needs the resource
it must **unlock** the mutex to allow other threads to use the resource.

Any number of threads may wait on a locked mutex simultaneously.
When the mutex becomes unlocked it is then locked by the highest-priority
thread that has waited the longest.

.. note::
    Mutex objects are *not* designed for use by ISRs.

Reentrant Locking
=================

A thread is permitted to lock a mutex it has already locked.
This allows the thread to access the associated resource at a point
in its execution when the mutex may or may not already be locked.

A mutex that is repeatedly locked by a thread must be unlocked an equal number
of times before the mutex becomes fully unlocked so it can be claimed
by another thread.

Priority Inheritance
====================

The thread that has locked a mutex is eligible for :dfn:`priority inheritance`.
This means the kernel will *temporarily* elevate the thread's priority
if a higher priority thread begins waiting on the mutex. This allows the owning
thread to complete its work and release the mutex more rapidly by executing
at the same priority as the waiting thread. Once the mutex has been unlocked,
the unlocking thread resets its priority to the level it had before locking
that mutex.

.. note::
    The :kconfig:option:`CONFIG_PRIORITY_CEILING` configuration option limits
    how high the kernel can raise a thread's priority due to priority
    inheritance. The default value of 0 permits unlimited elevation.

The owning thread's base priority is saved in the mutex when it obtains the
lock. Each time a higher priority thread waits on a mutex, the kernel adjusts
the owning thread's priority. When the owning thread releases the lock (or if
the high priority waiting thread times out), the kernel restores the thread's
base priority from the value saved in the mutex.

This works well for priority inheritance as long as only one locked mutex is
involved. However, if multiple mutexes are involved, sub-optimal behavior will
be observed if the mutexes are not unlocked in the reverse order to which the
owning thread's priority was previously raised. Consequently it is recommended
that a thread lock only a single mutex at a time when multiple mutexes are
shared between threads of different priorities.

Implementation
**************

Defining a Mutex
================

A mutex is defined using a variable of type :c:struct:`k_mutex`.
It must then be initialized by calling :c:func:`k_mutex_init`.

The following code defines and initializes a mutex.

.. code-block:: c

    struct k_mutex my_mutex;

    k_mutex_init(&my_mutex);

Alternatively, a mutex can be defined and initialized at compile time
by calling :c:macro:`K_MUTEX_DEFINE`.

The following code has the same effect as the code segment above.

.. code-block:: c

    K_MUTEX_DEFINE(my_mutex);

Locking a Mutex
===============

A mutex is locked by calling :c:func:`k_mutex_lock`.

The following code builds on the example above, and waits indefinitely
for the mutex to become available if it is already locked by another thread.

.. code-block:: c

    k_mutex_lock(&my_mutex, K_FOREVER);

The following code waits up to 100 milliseconds for the mutex to become
available, and gives a warning if the mutex does not become available.

.. code-block:: c

    if (k_mutex_lock(&my_mutex, K_MSEC(100)) == 0) {
        /* mutex successfully locked */
    } else {
        printf("Cannot lock XYZ display\n");
    }

Unlocking a Mutex
=================

A mutex is unlocked by calling :c:func:`k_mutex_unlock`.

The following code builds on the example above,
and unlocks the mutex that was previously locked by the thread.

.. code-block:: c

    k_mutex_unlock(&my_mutex);

Suggested Uses
**************

Use a mutex to provide exclusive access to a resource, such as a physical
device.

Configuration Options
*********************

Related configuration options:

* :kconfig:option:`CONFIG_PRIORITY_CEILING`

API Reference
*************

.. doxygengroup:: mutex_apis

Futex API Reference
*******************

k_futex is a lightweight mutual exclusion primitive designed to minimize
kernel involvement. Uncontended operation relies only on atomic access
to shared memory. k_futex are tracked as kernel objects and can live in
user memory so that any access bypasses the kernel object permission
management mechanism.

.. doxygengroup:: futex_apis

User Mode Mutex API Reference
*****************************

sys_mutex behaves almost exactly like k_mutex, with the added advantage
that a sys_mutex instance can reside in user memory. When user mode isn't
enabled, sys_mutex behaves like k_mutex.

.. doxygengroup:: user_mutex_apis

Part 3

한국어 전문 번역

Mutex는 traditional reentrant mutual exclusion을 구현하는 kernel object입니다. 여러 thread가 hardware 또는 software resource를 공유할 때 한 번에 하나만 접근하게 합니다.

개념

RAM이 허용하는 만큼 정의하고 address로 참조합니다. Lock count는 owner가 잠근 횟수이며 0이면 unlocked입니다. Locked 상태에는 owning thread도 기록합니다. 초기화하면 count가 0이 됩니다.

Shared resource를 쓰기 전에 mutex를 lock하고, 다른 owner가 있으면 unlock까지 기다릴 수 있습니다. 다른 thread 영향이 적도록 hold time을 짧게 유지하고 끝나면 반드시 unlock합니다. 여러 waiter 중 highest-priority이면서 가장 오래 기다린 thread가 다음 owner가 됩니다.

Reentrant locking

Owner thread는 자신이 이미 잠근 mutex를 다시 잠글 수 있습니다. 반복 lock한 횟수와 같은 횟수로 unlock해야 완전히 available이 됩니다.

Priority inheritance

Higher-priority waiter가 생기면 kernel은 owner priority를 일시적으로 waiter와 같은 수준까지 올려 work와 release를 빨리 끝내게 합니다. Unlock 뒤 lock 전 priority로 복구합니다. CONFIG_PRIORITY_CEILING default 0은 unlimited elevation이고 다른 값은 상한을 둡니다.

Owner base priority는 mutex에 저장됩니다. Higher-priority waiter가 추가될 때 조정하고 owner가 release하거나 waiter가 timeout되면 저장 값으로 복구합니다. Mutex 하나에는 잘 동작하지만 여러 mutex를 priority 상승 순서의 역순으로 unlock하지 않으면 sub-optimal할 수 있으므로 priority가 다른 thread가 공유할 때 한 번에 하나만 lock하는 것을 권장합니다.

구현

Mutex 정의

k_mutex variable을 선언하고 init합니다.

struct k_mutex my_mutex;

k_mutex_init(&my_mutex);

Compile-time 정의입니다.

K_MUTEX_DEFINE(my_mutex);

Mutex lock

무기한 기다리는 예제입니다.

k_mutex_lock(&my_mutex, K_FOREVER);

최대 100ms 기다리고 실패하면 warning을 출력합니다.

if (k_mutex_lock(&my_mutex, K_MSEC(100)) == 0) {
    /* mutex successfully locked */
} else {
    printf("Cannot lock XYZ display\n");
}

Mutex unlock

앞서 caller가 잠근 mutex를 해제합니다.

k_mutex_unlock(&my_mutex);

권장 용도와 설정

Physical device 같은 resource의 exclusive access에 mutex를 사용합니다. 관련 option은 CONFIG_PRIORITY_CEILING입니다.

API 참고: mutex_apis

다른 module의 mutex declaration입니다.

extern struct k_mutex <name>;
K_MUTEX_DEFINE(name)

Mutex를 static하게 정의하고 초기화합니다. 다른 module에서는 extern struct k_mutex 으로 접근합니다.

매개변수

  • name: mutex 이름
k_mutex_init(mutex)

첫 사용 전에 mutex를 owner가 없고 available인 상태로 초기화합니다.

매개변수

  • mutex: mutex 주소

반환값

  • 0: mutex object 생성
k_mutex_lock(mutex, timeout)

Mutex를 잠급니다. 다른 thread가 보유하면 available 또는 timeout까지 기다립니다. Owner가 다시 잠그면 즉시 성공하고 lock count를 1 올립니다. ISR에서는 사용할 수 없습니다.

매개변수

  • mutex: mutex 주소
  • timeout: 대기 시간 또는 K_NO_WAIT/K_FOREVER

반환값

  • 0: 잠금 성공
  • -EBUSY: 기다리지 않고 반환
  • -EAGAIN: 대기 시간 만료
k_mutex_unlock(mutex)

Caller가 보유한 mutex를 한 단계 해제합니다. 획득 횟수만큼 해제해야 다른 thread가 claim할 수 있으며 ownership·priority inheritance 때문에 ISR에서는 사용할 수 없습니다.

매개변수

  • mutex: mutex 주소

반환값

  • 0: 해제 성공
  • -EPERM: current thread가 owner가 아님
  • -EINVAL: mutex가 locked 상태가 아님
k_mutex

Mutex Structure이며 include 경로는 kernel.h입니다.

wait_q

Mutex에서 기다리는 thread를 보관하는 wait queue입니다.

owner

Mutex owner를 가리키는 k_thread pointer입니다.

lock_count

Current reentrant lock count입니다.

owner_orig_prio

Priority inheritance 전 owner의 original priority입니다.

Futex API 참고: futex_apis

k_futex는 uncontended path에서 shared memory atomic access만 사용해 kernel 관여를 줄이는 lightweight primitive입니다. Kernel object로 추적되지만 user memory에 둘 수 있어 일반 object permission management를 우회해 접근합니다.

k_futex_wait(futex, expected, timeout)

Futex 값이 expected와 같으면 다른 thread가 k_futex_wake()를 호출할 때까지 current thread를 sleep시킵니다. Wakeup 뒤 값이 바뀌었는지 caller가 다시 검사해야 합니다.

매개변수

  • futex: futex 주소
  • expected: 기다리기 전 일치해야 하는 값
  • timeout: 대기 시간 또는 K_NO_WAIT/K_FOREVER

반환값

  • 0: 실제로 sleep했다가 wakeup됨
  • -EACCES: 주소 read 권한 없음
  • -EAGAIN: 값이 expected와 다름
  • -EINVAL: kernel이 주소를 futex로 인식하지 못함
  • -ETIMEDOUT: futex wakeup이 아니라 timeout으로 깨어남
k_futex_wake(futex, wake_all)

Futex에서 기다리는 highest-priority thread 하나 또는 모든 thread를 깨웁니다.

매개변수

  • futex: 대기 thread를 깨울 futex
  • wake_all: true면 전체, false면 highest-priority 하나

반환값

  • non-negative: wakeup한 thread 수
  • -EACCES: futex 주소 접근 권한 없음
  • -EINVAL: kernel이 주소를 futex로 인식하지 못함

User mode mutex API 참고: user_mutex_apis

sys_mutex는 user memory에 둘 수 있다는 점을 제외하면 k_mutex와 거의 같고 userspace가 꺼지면 k_mutex처럼 동작합니다.

다른 module의 sys_mutex declaration입니다.

extern struct sys_mutex <name>;
SYS_MUTEX_DEFINE(name)

User memory에 둘 수 있는 sys_mutex를 static하게 정의·초기화합니다. 다른 module의 extern struct sys_mutex 선언과 K_APP_DMEM() memory domain routing을 사용할 수 있습니다.

매개변수

  • name: mutex 이름
sys_mutex_init(mutex)

Userspace가 꺼져 있고 SYS_MUTEX_DEFINE()을 쓰지 않은 sys_mutex를 available·no-owner 상태로 초기화합니다.

매개변수

  • mutex: mutex 주소
sys_mutex_lock(mutex, timeout)

User memory에도 위치할 수 있는 mutex를 잠그며 reentrant acquire 시 lock count가 증가합니다.

매개변수

  • mutex: user memory일 수 있는 mutex 주소
  • timeout: 대기 시간 또는 K_NO_WAIT/K_FOREVER

반환값

  • 0: 성공
  • -EBUSY: no-wait 실패
  • -EAGAIN: timeout
  • -EACCES: 주소 접근 권한 없음
  • -EINVAL: kernel이 mutex를 인식하지 못함
sys_mutex_unlock(mutex)

Caller가 보유한 sys_mutex를 한 단계 해제합니다.

매개변수

  • mutex: user memory일 수 있는 mutex 주소

반환값

  • 0: 성공
  • -EACCES: 주소 접근 권한 없음
  • -EINVAL: 인식할 수 없거나 locked 상태가 아님
  • -EPERM: caller가 owner가 아님

Source

출처

영어 원문에는 문단, 문서 지시문, 표, 코드, 코드 심벌, 경로를 그대로 보존했습니다.