信号量semaphore

信号量是一种允许进程进入睡眠的同步机制,信号量是一个计数器,支持两种原语即P 和V操作,也就是down 和up 操作,

/* Please don't access any members of this structure directly */
struct semaphore {
raw_spinlock_t lock;//用于对count以及wait_list成员的保护
unsigned int count;//表示允许进入临界区的控制路径
struct list_head wait_list;//用于管理所有在该信号上睡眠的进程
};
#define __SEMAPHORE_INITIALIZER(name, n) \
{ \
.lock = __RAW_SPIN_LOCK_UNLOCKED((name).lock), \
.count = n, \
.wait_list = LIST_HEAD_INIT((name).wait_list), \
} #define DEFINE_SEMAPHORE(name) \
struct semaphore name = __SEMAPHORE_INITIALIZER(name, 1) static inline void sema_init(struct semaphore *sem, int val)
{
static struct lock_class_key __key;
*sem = (struct semaphore) __SEMAPHORE_INITIALIZER(*sem, val);
lockdep_init_map(&sem->lock.dep_map, "semaphore->lock", &__key, 0);
}

信号量的操作:

 void down(struct semaphore *sem);
int __must_check down_interruptible(struct semaphore *sem);
int __must_check down_killable(struct semaphore *sem);
int __must_check down_trylock(struct semaphore *sem);
int __must_check down_timeout(struct semaphore *sem, long jiffies);
void up(struct semaphore *sem); 1 /** 2 * down_interruptible - acquire the semaphore unless interrupted 3 * @sem: the semaphore to be acquired
 4  *
5 * Attempts to acquire the semaphore. If no more tasks are allowed to
6 * acquire the semaphore, calling this function will put the task to sleep.
7 * If the sleep is interrupted by a signal, this function will return -EINTR.
8 * If the semaphore is successfully acquired, this function returns 0.
9 */
10 int down_interruptible(struct semaphore *sem)
11 {
12 unsigned long flags;
13 int result = 0;
14
15 raw_spin_lock_irqsave(&sem->lock, flags);
16 if (likely(sem->count > 0))
17 sem->count--;
18 else
19 result = __down_interruptible(sem);
20 raw_spin_unlock_irqrestore(&sem->lock, flags);
21
22 return result;
23 }
24 static noinline int __sched __down_interruptible(struct semaphore *sem)
25 {
26 return __down_common(sem, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
27 }
28 /*
29 * Because this function is inlined, the 'state' parameter will be
30 * constant, and thus optimised away by the compiler. Likewise the
31 * 'timeout' parameter for the cases without timeouts.
32 */
33 static inline int __sched __down_common(struct semaphore *sem, long state,
34 long timeout)
35 {
36 struct task_struct *task = current;
37 struct semaphore_waiter waiter;
38
39 list_add_tail(&waiter.list, &sem->wait_list);//添加到末尾 fifo 先进先出
40 waiter.task = task;
41 waiter.up = false;
42
43 for (;;) {
44 if (signal_pending_state(state, task))//收到信号且不是sigkill
45 goto interrupted;
46 if (unlikely(timeout <= 0))//超时唤醒
47 goto timed_out;
48 __set_task_state(task, state);
49 raw_spin_unlock_irq(&sem->lock);
50 timeout = schedule_timeout(timeout);
51 raw_spin_lock_irq(&sem->lock);
52 if (waiter.up)//up 为true 表示被唤醒切获取到自旋锁

53 return 0;
54 }
55
56 timed_out:
57 list_del(&waiter.list);
58 return -ETIME;
59
60 interrupted:
61 list_del(&waiter.list);
62 return -EINTR;
63 }

可知16-19行为临界区,涉及到count 的计数 会使用spinlock 来保护,由于这些信号量可能在中断函数里面使用,所以需要关闭本地cpu 中断,这里采用raw_spin_lock_irqsave,如果count值小于0 表示当前进程无法获取该信号量使用down_interrupt来执行睡眠等待操作,

49-50行:主动让出cpu之前需要释放该锁,

 1 /**
2 * up - release the semaphore
3 * @sem: the semaphore to release
4 *
5 * Release the semaphore. Unlike mutexes, up() may be called from any
6 * context and even by tasks which have never called down().
7 */
8 void up(struct semaphore *sem)
9 {
10 unsigned long flags;
11
12 raw_spin_lock_irqsave(&sem->lock, flags);
13 if (likely(list_empty(&sem->wait_list)))
14 sem->count++;
15 else
16 __up(sem);
17 raw_spin_unlock_irqrestore(&sem->lock, flags);
18 }
19 static noinline void __sched __up(struct semaphore *sem)
20 {
21 struct semaphore_waiter *waiter = list_first_entry(&sem->wait_list,
22 struct semaphore_waiter, list);
23 list_del(&waiter->list);
24 waiter->up = true;
25 wake_up_process(waiter->task);
26 }
27 /**
28 * wake_up_process - Wake up a specific process
29 * @p: The process to be woken up.
30 *
31 * Attempt to wake up the nominated process and move it to the set of runnable
32 * processes.
33 *
34 * Return: 1 if the process was woken up, 0 if it was already running.
35 *
36 * It may be assumed that this function implies a write memory barrier before
37 * changing the task state if and only if any tasks are woken up.
38 */
39 int wake_up_process(struct task_struct *p)
40 {
41 return try_to_wake_up(p, TASK_NORMAL, 0);
42 }

可知:如果信号量上的等待队列为空,则说明没有进程等待该变量,那么只需要将count++,如果不为空则有进程在等待队列休眠,需要up叫醒他们。up函数中唤醒进程是先进先出。

MUTEX 互斥体

mutex是阻塞锁,当某线程无法获取互斥量时,该线程会被直接挂起,该线程不再消耗CPU时间,当其他线程释放互斥量后,操作系统会激活那个被挂起的线程,让其投入运行.

问题:mutex 互斥体解决了什么问题?,mutex 和信号量的区别?

信号量可以根据初始化的count值大小分为计数信号量和互斥信号量。mutex 相当于count计数等于1的信号量,为什么还有mutex呢?

猜测原因:应该是在锁竞争激烈的场景下,mutex 执行更快吧,

作者:匿名用户
链接:https://www.zhihu.com/question/47704079/answer/216430116
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

A mutex
is essentially the same thing as a binary semaphore and sometimes uses
the same basic implementation. The differences between them are in how
they are used. While a binary semaphore may be used as a mutex, a mutex
is a more specific use-case, in that only the thread that locked the
mutex is supposed to unlock it. This constraint makes it possible to
implement some additional features in mutexes:

  1. Since only the thread that locked the mutex is supposed to unlock
    it, a mutex may store the id of thread that locked it and verify the
    same thread unlocks it.
  2. Mutexes may provide priority inversion
    safety. If the mutex knows who locked it and is supposed to unlock it,
    it is possible to promote the priority of that thread whenever a
    higher-priority task starts waiting on the mutex.
  3. Mutexes may also provide deletion safety, where the thread holding the mutex cannot be accidentally deleted.
  4. Alternately, if the thread holding the mutex is deleted (perhaps due
    to an unrecoverable error), the mutex can be automatically released.
  5. A mutex may be recursive: a thread is allowed to lock it multiple times without causing a deadlock.
/*
* 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;//原子计数 1表示没有人持有锁,0 表示被持有
spinlock_t wait_lock; //用于保护wait_list睡眠等待队列
struct list_head wait_list;
#if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_MUTEX_SPIN_ON_OWNER)
struct task_struct *owner;
#endif
#ifdef CONFIG_MUTEX_SPIN_ON_OWNER
struct optimistic_spin_queue osq; /* Spinner MCS lock */
#endif
#ifdef CONFIG_DEBUG_MUTEXES
void *magic;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
struct lockdep_map dep_map;
#endif
};
Spinner MCS lock : mcs 一种spinlock的优化方案

mutex 实现:
 1 /**
2 * mutex_lock - acquire the mutex
3 * @lock: the mutex to be acquired
4 *
5 * Lock the mutex exclusively for this task. If the mutex is not
6 * available right now, it will sleep until it can get it.
7 *
8 * The mutex must later on be released by the same task that
9 * acquired it. Recursive locking is not allowed. The task
10 * may not exit without first unlocking the mutex. Also, kernel
11 * memory where the mutex resides must not be freed with
12 * the mutex still locked. The mutex must first be initialized
13 * (or statically defined) before it can be locked. memset()-ing
14 * the mutex to 0 is not allowed.
15 *
16 * ( The CONFIG_DEBUG_MUTEXES .config option turns on debugging
17 * checks that will enforce the restrictions and will also do
18 * deadlock debugging. )
19 *
20 * This function is similar to (but not equivalent to) down().
21 */
22 void __sched mutex_lock(struct mutex *lock)
23 {
24 might_sleep();
25 /*
26 * The locking fastpath is the 1->0 transition from
27 * 'unlocked' into 'locked' state.
28 */
29 __mutex_fastpath_lock(&lock->count, __mutex_lock_slowpath);
30 mutex_set_owner(lock);//成功持有锁后 lock->owner 只想当前进程的task_struct 当前线程的thread_info可以通过sp低13位清零获取到
31 }

eg:如果count计数减1后小于0,表示该锁已经被人持有,这进入slowpath,  1 __mutex_lock_slowpath(atomic_t *lock_count)

  2 {
3 struct mutex *lock = container_of(lock_count, struct mutex, count);
4
5 __mutex_lock_common(lock, TASK_UNINTERRUPTIBLE, 0,
6 NULL, _RET_IP_, NULL, 0);
7 }
8
9 /*
10 * Lock a mutex (possibly interruptible), slowpath:
11 */
12 static __always_inline int __sched
13 __mutex_lock_common(struct mutex *lock, long state, unsigned int subclass,
14 struct lockdep_map *nest_lock, unsigned long ip,
15 struct ww_acquire_ctx *ww_ctx, const bool use_ww_ctx)
16 {
17 struct task_struct *task = current;// 通过sp低13位清零获取到thread_info,再通过thread_info获取到task指针

18 struct mutex_waiter waiter;
19 unsigned long flags;
20 int ret;
21
22 if (use_ww_ctx) {
23 struct ww_mutex *ww = container_of(lock, struct ww_mutex, base);
24 if (unlikely(ww_ctx == READ_ONCE(ww->ctx)))
25 return -EALREADY;
26 }
27
28 preempt_disable();
29 mutex_acquire_nest(&lock->dep_map, subclass, 0, nest_lock, ip);
30
31 if (mutex_optimistic_spin(lock, ww_ctx, use_ww_ctx)) {
32 /* got the lock, yay! */
33 preempt_enable();
34 return 0;
35 }
36
37 spin_lock_mutex(&lock->wait_lock, flags);// 获取lock->wait_lock自旋锁,直到获取成功为止,因为接下来的代码要对lock的成员变量进行读写,需要互斥访问
38
39 /*
40 * Once more, try to acquire the lock. Only try-lock the mutex if
41 * it is unlocked to reduce unnecessary xchg() operations.
42 */ // 循环读取互斥锁计数器count的值,直到count为1,表示互斥锁可用,此处读取互斥锁计数器的同时也将互斥锁的值设置为0了
43 if (!mutex_is_locked(lock) &&
44 (atomic_xchg_acquire(&lock->count, 0) == 1))
45 goto skip_wait;
46
47 debug_mutex_lock_common(lock, &waiter);
48 debug_mutex_add_waiter(lock, &waiter, task_thread_info(task));
49
50 /* add waiting tasks to the end of the waitqueue (FIFO): */
51 list_add_tail(&waiter.list, &lock->wait_list);
52 waiter.task = task;
53
54 lock_contended(&lock->dep_map, ip);
55
56 for (;;) {
57 /*
58 * Lets try to take the lock again - this is needed even if
59 * we get here for the first time (shortly after failing to
60 * acquire the lock), to make sure that we get a wakeup once
61 * it's unlocked. Later on, if we sleep, this is the
62 * operation that gives us the lock. We xchg it to -1, so
63 * that when we release the lock, we properly wake up the
64 * other waiters. We only attempt the xchg if the count is
65 * non-negative in order to avoid unnecessary xchg operations:
66 */
67 if (atomic_read(&lock->count) >= 0 &&
68 (atomic_xchg_acquire(&lock->count, -1) == 1))
69 break;
70
71 /*
72 * got a signal? (This code gets eliminated in the
73 * TASK_UNINTERRUPTIBLE case.)
74 */
75 if (unlikely(signal_pending_state(state, task))) {
76 ret = -EINTR;
77 goto err;
78 }
79
80 if (use_ww_ctx && ww_ctx->acquired > 0) {
81 ret = __ww_mutex_lock_check_stamp(lock, ww_ctx);
82 if (ret)
83 goto err;
84 }
85 // 设置当前任务状态TASK_UNINTERRUPTIBLE

86 __set_task_state(task, state);
87
88 /* didn't get the lock, go to sleep: */
89 spin_unlock_mutex(&lock->wait_lock, flags); // 释放自旋锁(此处对lock的成员变量修改读写都已经完成,其他任务可读写修改
90 schedule_preempt_disabled();/ 执行一次调度,主动切换到其他任务,等其他任务释放互斥锁是会唤醒当前任务,继续执行下面的函数
91 spin_lock_mutex(&lock->wait_lock, flags);// 任务被唤醒,重新获取自旋锁
92 }
93 __set_task_state(task, TASK_RUNNING);
94
95 mutex_remove_waiter(lock, &waiter, current_thread_info());
96 /* set it to 0 if there are no waiters left: */
97 if (likely(list_empty(&lock->wait_list)))
98 atomic_set(&lock->count, 0);
99 debug_mutex_free_waiter(&waiter);
100
101 skip_wait:
102 /* got the lock - cleanup and rejoice! */
103 lock_acquired(&lock->dep_map, ip);
104 mutex_set_owner(lock);
105
106 if (use_ww_ctx) {
107 struct ww_mutex *ww = container_of(lock, struct ww_mutex, base);
108 ww_mutex_set_context_slowpath(ww, ww_ctx);
109 }
110
111 spin_unlock_mutex(&lock->wait_lock, flags);
112 preempt_enable();
113 return 0;
114
115 err:
116 mutex_remove_waiter(lock, &waiter, task_thread_info(task));
117 spin_unlock_mutex(&lock->wait_lock, flags);
118 debug_mutex_free_waiter(&waiter);
119 mutex_release(&lock->dep_map, 1, ip);
120 preempt_enable();
121 return ret;
122 }

就不详细看了 ;

主要是mutex 和semaphore使用区别:一个是互斥一个 计数信号量

以生活现象为例:超市最多允许同时进100个人,一开始100人顺利进入,后面出一个进一个

// 通过sp低13位清零获取到thread_info,再通过thread_info获取到task指针

linux 内核并发同步 2的更多相关文章

  1. Linux内核的同步机制

    本文详细的介绍了Linux内核中的同步机制:原子操作.信号量.读写信号量和自旋锁的API,使用要求以及一些典型示例 一.引言 在现代操作系统里,同一时间可能有多个内核执行流在执行,因此内核其实象多进程 ...

  2. linux内核级同步机制--futex

    在面试中关于多线程同步,你必须要思考的问题 一文中,我们知道glibc的pthread_cond_timedwait底层是用linux futex机制实现的. 理想的同步机制应该是没有锁冲突时在用户态 ...

  3. Linux内核--并发【转】

    本文转自自:http://www.jianshu.com/p/035550ae05d2 为什么会产生并发 1.多个用户同时登陆的时候,他们有可能在任何时刻以任意的组合调用内核代码. 2.smp系统可能 ...

  4. Linux内核的同步机制---自旋锁

    自旋锁的思考:http://bbs.chinaunix.net/thread-2333160-1-1.html 近期在看宋宝华的<设备驱动开发具体解释>第二版.看到自旋锁的部分,有些疑惑. ...

  5. Linux内核同步机制

    http://blog.csdn.net/bullbat/article/details/7376424 Linux内核同步控制方法有很多,信号量.锁.原子量.RCU等等,不同的实现方法应用于不同的环 ...

  6. Linux内核同步:自旋锁

    linux内核--自旋锁的理解 自旋锁:如果内核配置为SMP系统,自旋锁就按SMP系统上的要求来实现真正的自旋等待,但是对于UP系统,自旋锁仅做抢占和中断操作,没有实现真正的“自旋”.如果配置了CON ...

  7. [内核同步]浅析Linux内核同步机制

    转自:http://blog.csdn.net/fzubbsc/article/details/37736683?utm_source=tuicool&utm_medium=referral ...

  8. Linux内核同步机制--转发自蜗窝科技

    Linux内核同步机制之(一):原子操作 http://www.wowotech.net/linux_kenrel/atomic.html 一.源由 我们的程序逻辑经常遇到这样的操作序列: 1.读一个 ...

  9. Linux 内核同步机制

        本文将就自己对内核同步机制的一些简要理解,做出一份自己的总结文档.     Linux内部,为了提供对共享资源的互斥访问,提供了一系列的方法,下面简要的一一介绍. Technorati 标签: ...

随机推荐

  1. 多测师讲解自动化测试 _RF关键字001_( 中)_高级讲师肖sir

    1.关键字如下 1.1Get Text 1.2Get Value 2.#上下滑动(滚动条) Open Browser http://www.jd.com gc Maximize Browser Win ...

  2. day14 Pyhton学习

    一.迭代器-概念 可迭代协议:内部含有__iter__方法的值/变量都是可迭代的 如何得到一个迭代器:可迭代变量.__iter__()返回一个迭代器 迭代器协议:内部含有__iter__方法和__ne ...

  3. 关于umi-request GET请求参数携带数组

    在使用umi-request时候发现GET传递数组,后台在接收时只能接受到最后一个,对此记录一下. 问题: // 发送数据: request(url, { params: { select: [1,2 ...

  4. 【C语言教程】“双向循环链表”学习总结和C语言代码实现!

    双向循环链表 定义 双向循环链表和它名字的表意一样,就是把双向链表的两头连接,使其成为了一个环状链表.只需要将表中最后一个节点的next指针指向头节点,头节点的prior指针指向尾节点,链表就能成环儿 ...

  5. 第二十一章 PHP编译安装(centos7)

    一.环境准备 主机 IP 身份 web01 10.0.0.7 编译安装PHP 二.准备安装 1.新建目录 [root@jindada ~]# mkdir /php 2.上传源码包并解压 [root@j ...

  6. 【Azure 环境】连接到微软云Azure中国区 By VS 2019, VS Code, Powershell

    问题情形 最近,在使用最新的VS Code插件连接到中国区的Azure时候,出现了依旧是global版的登录连接.这个问题是当前Azure Account插件最新版的问题,可以使用V0.8.11版本登 ...

  7. 类型转化 - js中的骚操作

    Number Number() 把字符串数字转化成数字类型,布尔类型也可以转化 parseInt parseInt() 字符串数字转化成数字类型,当布尔类型不可以(NaN),但该函数可以把数字开头的数 ...

  8. RxJava用法

    首先导入依赖: implementation 'io.reactivex.rxjava2:rxjava:2.2.9'implementation 'io.reactivex.rxjava2:rxand ...

  9. 撤销rebase与git原理

    git对象 git是面向对象的,对象存储在.git/objects文件夹中.此文件夹中,一个对象就是一个文件,文件名就是对象的id 提交commit的时候,每个文件都是一个数据对象,一个树对象会用来维 ...

  10. EXCEL计数时间差--分钟数