Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to understand kernel mutex in Linux driver

2025-03-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Servers >

Share

Shulou(Shulou.com)06/01 Report--

How to understand the kernel mutex in the Linux driver, I believe many inexperienced people do not know what to do about it. Therefore, this paper summarizes the causes and solutions of the problem. Through this article, I hope you can solve this problem.

Overview of mutexes

Semaphores are mechanisms that protect multiple processors from accessing a common resource in a parallel processing environment, and mutex is used for mutually exclusive operations. The semaphore's count initialization to 1 down () / up () can also achieve a mutex-like effect.

The semantics of mutex is simpler and lighter than that of semaphores. In the test scenario of fierce lock competition, mutex has faster execution speed and better scalability than semaphores. In addition, the definition of mutex data structure is smaller than semaphores.

Advantages of mutex

Mutex is much more efficient than semaphores:

Mutex is the first to implement spin waiting mechanism.

Mutex tries to acquire the lock before sleeping.

Mutex implements MCS to avoid CPU cache jolts caused by multiple CPU contention locks.

Considerations for the use of mutex:

Only one thread can hold the mutex at a time.

Only the lock holder can unlock it. You can no longer hold mutex in one process and release it in another process.

Recursive locking and unlocking are not allowed.

When a process holds a mutex, the process cannot exit.

Mutex must be initialized with the official API.

Mutex can sleep, so it is not allowed to be used in interrupt handlers or the lower half of interrupts, such as tasklet, timers, and so on.

Table of contents:

/ linux/include/linux/mutex.h / * * Simple Straightforward mutexes with strict semantics: * *-only one task can hold the mutex at a time *-only the owner can unlock the mutex *-multiple unlocks are not permitted *-recursive locking is not permitted *-a mutex object must be initialized via the API *-a mutex object must not be initialized via memset or copying *-task may not exit with 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 DEBUG_MUTEXES is * enabled. Furthermore, besides enforcing the above rules, the mutex * debugging code also implements a number of additional 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 *-owner tracking *-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) * / struct mutex {/ * 1: unlocked, 0: locked, negative: locked, possible waiters * / atomic_t count Spinlock_t wait_lock; struct list_head wait_list; # if defined (CONFIG_DEBUG_MUTEXES) | | defined (CONFIG_SMP) struct task_struct * owner; # endif # ifdef CONFIG_MUTEX_SPIN_ON_OWNER void * spin_mlock; / * Spinner MCS lock * / # endif # ifdef CONFIG_DEBUG_MUTEXES const char * name; void * magic; # endif # ifdef CONFIG_DEBUG_LOCK_ALLOC struct lockdep_map dep_map; # endif}

Role and access rules:

Mutex is mainly used to implement the function of mutually exclusive access in the kernel. Kernel mutexes are implemented on top of atomic API, but this is not visible to kernel users.

Access to it must follow some rules: only one task can hold a mutex at a time, and only this task can unlock the mutex. Mutexes cannot be recursively locked or unlocked. A mutex object must be initialized through its API, not with memset or replication. A task cannot be finished while holding a mutex. The area of memory used by mutexes cannot be freed. Mutexes in use cannot be reinitialized. And mutexes cannot be used to interrupt the context.

Mutexes are faster and more compact than the current kernel semaphore option.

The use of mutexes

Initialization

The static definition is as follows:

DEFINE_MUTEX (name)

Initialize mutex dynamically, as follows:

Mutex_init & mutex)

The specific implementation is as follows:

# define mutex_init (mutex)\ do {\ static struct lock_class_key _ key;\\ _ mutex_init ((mutex), # mutex, & _ key);\} while (0) void _ mutex_init (struct mutex * lock, const char * name, struct lock_class_key * key) {atomic_set (& lock- > count, 1); spin_lock_init (& lock- > wait_lock) INIT_LIST_HEAD (& lock- > wait_list); mutex_clear_owner (lock); # ifdef CONFIG_MUTEX_SPIN_ON_OWNER lock- > spin_mlock = NULL; # endif debug_mutex_init (lock, name, key);}

Apply for mutex

The list of mutex operations is as follows:

Method describes that mutex_lock (struct mutex*) is locked for the specified mutex. If not available, sleep mutex_unlock (struct mutex*) unlocks the mutex_trylock (struct mutex*) view for the specified mutex to obtain the specified mutex, and returns 1 if successful; otherwise, the lock is acquired, and the return value is 0mutex_is_lock (struct mutex*). If the lock has been requisitioned, 1 is returned; otherwise, 0 is returned.

The simplicity and efficiency of mutex comes from being more limited than using semaphores. It is different from semaphores because mutex implements only the most basic behavior that Dijkstra was designed to do. Therefore, the usage scenario of mutex is relatively strict.

(1) Code: linux/kernel/mutex.c

Void inline fastcall _ _ sched mutex_lock (struct mutex * lock); / / acquire mutex.

In fact, it is to do a self-subtraction operation for count, and then use its own spin lock to enter the critical section operation. First of all, get the value of count, set count to-1, and judge that if the original count is set to 1, that is, the mutex can be obtained, then get it directly and jump out. Otherwise, enter the loop to test the mutex repeatedly. In the loop, the original state of the mutex lock is also obtained first, and it is judged that if it can be obtained (equal to 1), the loop is exited, otherwise the state of the current process is set to an uninterruptible state, unlock its own spin lock, enter the sleep state, and obtain its own spin lock when it is scheduled to wake up, and enter the cycle of querying its own state (the state of the mutex) for a new time.

(2) for more information, see linux/kernel/mutex.c

Int fastcall _ _ sched mutex_lock_interruptible (struct mutex * lock)

Like mutex_lock (), it also acquires mutexes. 0 is returned after acquiring the mutex or going to sleep until the mutex is acquired. If you go to sleep while waiting for the lock to be acquired and receive a signal (interrupted by the signal), return _ EINIR.

(3) for more information, see linux/kernel/mutex.c

Int fastcall _ _ sched mutex_trylock (struct mutex * lock)

An attempt was made to acquire the mutex, returning 1 if it was successfully acquired, otherwise 0 without waiting.

Release mutex

For more information, see linux/kernel/mutex.c

Void fastcall mutex_unlock (struct mutex * lock)

Releases the mutex acquired by the current process. This function cannot be used in an interrupt context and is not allowed to release an unlocked mutex.

Matters needing attention on trial of mutex

Only one task can hold mutex at any one time, that is, the usage count of mutex is always 1

The person who locks the mutex must be responsible for unlocking it again-you can't lock a MUTEX in one context and unlock it in another. This limitation makes mutex unsuitable for scenarios where the kernel is complex to synchronize with user space. The most commonly used method is to lock and unlock in the same context.

Recursive ground locking and unlocking are not allowed. That is, you cannot hold the same lock recursively, nor can you unlock a mutex that has been unlocked.

When holding a mutex, the process cannot exit

Mutex cannot be used in interrupts or the lower half, even if mutex_trylock () is used

Mutex can only be managed through the official API: it can only be initialized using the methods described in the previous section, and cannot be copied, manually initialized, or repeatedly initialized

Semaphores and mutexes

Mutexes and semaphores are very similar, and the coexistence of the two in the kernel can be confusing. Fortunately, their standard usage has a simple specification: unless a constraint on mutex prevents you from using it, use mutex over semaphores. When you write new code, you only need to use semaphores on special occasions (usually very low-level code). Therefore, mutex is recommended. If you find that the constraint cannot be met and there is no other choice, consider selecting the semaphore.

Spin locks and mutexes are used

Knowing when to use spin locks and when to use mutexes (or semaphores) is important for writing good code, but in most cases, you don't need to think much about it, because you can only use spin locks in an interrupt context. Mutexes can only be used during task sleep.

Here is a summary of the requirements for various locks

Requirements recommended locking method low overhead locking priority use spin lock short-term lock priority spin lock long-term lock priority use mutex interrupt context lock use spin lock holding lock requires sleep to use mutex

Examples of mutex locking and unlocking

The method of use is as follows:

1. Struct mutex mutex; 2. Mutex_init (& mutex); / * definition * / 3. / / lock 4. Mutex_lock (& mutex); 5. 6. / / critical section 7. 8. / / unlock 9. Mutex_unlock (& mutex)

As you can see, the mutex is a simplified version of the semaphore because you no longer need to manage any usage counts.

The following is the driver of the network card DM9000, in which the operation of writing eeprom has tried the mutex mechanism:

Static void dm9000_write_eeprom (board_info_t * db, int offset, U8 * data) {unsigned long flags; if (db- > flags & DM9000_PLATF_NO_EEPROM) return; mutex_lock (& db- > addr_lock); spin_lock_irqsave (& db- > lock, flags); iow (db, DM9000_EPAR, offset); iow (db, DM9000_EPDRH, data [1]); iow (db, DM9000_EPDRL, data [0]) Iow (db, DM9000_EPCR, EPCR_WEP | EPCR_ERPRW); spin_unlock_irqrestore (& db- > lock, flags); dm9000_wait_eeprom (db); mdelay (1); / * wait at least 150uS to clear * / spin_lock_irqsave (& db- > lock, flags); iow (db, DM9000_EPCR, 0); spin_unlock_irqrestore (& db- > lock, flags); mutex_unlock (& db- > addr_lock);}

You can see that every time the driver writes data to the eeprom (accessing the critical resource), the mutex lock db- > addr_lock corresponding to the resource needs to be obtained first, and the lock must be released after use.

After reading the above, have you mastered how to understand kernel mutexes in Linux drivers? If you want to learn more skills or want to know more about it, you are welcome to follow the industry information channel, thank you for reading!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Servers

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report