关键词:wfe、FIFO ticket-based、spin_lock/spin_trylock/spin_unlock、spin_lock_irq/spin_lock_bh/spin_lock_irqsave。

Linux并发与同步专题 (1)原子操作和内存屏障

Linux并发与同步专题 (2)spinlock

Linux并发与同步专题 (3) 信号量

Linux并发与同步专题 (4) Mutex互斥量

Linux并发与同步专题 (5) 读写锁

Linux并发与同步专题 (6) RCU

Linux并发与同步专题 (7) 内存管理中的锁

Linux并发与同步专题 (8) 最新更新与展望

spinlock同一时刻只能被一个内核代码路径持有,如果有另外一个内核代码路径试图获取一个已经被持有的spinlock,那么该内核代码路径需要一直自旋忙等待,直到锁持有者释放了该锁。

spinlock锁的特性如下:

  • spinlock属于忙等待机制,当无法获取spinlock锁时会不断尝试,直到获取锁为止。
  • 同一时刻只能有一个内核代码路径可以获得所。
  • 要求spinlock锁持有者尽快完成临界区的执行任务。如果临界区执行时间过长,在锁外面忙等待的CPU比较浪费,特别是spinlock临界区里不能睡眠。
  • spinlock锁可以在中断上下文中使用。

1. spinlock实现

使用spinlock锁的重要原则是:拥有spinlock锁的临界区代码必须是原子执行,不能休眠和主动调度。

1.1 spinlock数据结构

spinlock的数据结构spinlock_t考虑了不同体系结构和实时性内核的要求。如果打上了RT-patch,就变成了使用rt_mutex。

如果没有使用RT-patch,那么arch_spinlock_t在ARM32体系结构下就对应如下。

typedef struct spinlock {
union {
structraw_spinlockrlock;
};
} spinlock_t; typedef struct raw_spinlock {
arch_spinlock_t raw_lock;
} raw_spinlock_t; #define TICKET_SHIFT 16 typedef struct {
union {
u32 slock;----------------------------在Linux 2.6.25之前,spinlock数据结构是一个简单无符号类型变量,slock为1表示锁未被持有,为0或者负数表示锁被持有。
struct __raw_tickets {
#ifdef __ARMEB__
u16 next;
u16 owner;
#else
u16 owner;
u16 next;
#endif
} tickets;
};
} arch_spinlock_t;

在Linux  2.6.25内核之后,spinlock实现了一套名为“FIFO ticket-based”算法的spinlock机制。

ticket-based算法的shipinlock仍然使用原来数据结构,但slocl被拆分为两部分。

owner表示锁持有者的等号牌,next表示外面排队队列中末尾者的等号牌。

1.2 spin_lock()/spin_unlock()/spin_trylock()

spin_lock()函数最终调用__raw_spin_lock()函数来实现。

首先关闭内核抢占,然后调用架构相关的arch_spin_lock()。

static inline void spin_lock(spinlock_t *lock)
{
raw_spin_lock(&lock->rlock);
} #define raw_spin_lock(lock) _raw_spin_lock(lock) void __lockfunc _raw_spin_lock(raw_spinlock_t *lock)
{
__raw_spin_lock(lock);
} static inline void __raw_spin_lock(raw_spinlock_t *lock)
{
preempt_disable();--------------------------------------------禁止抢占,这里受CONFIG_PREEMPT_COUNT和CONFIG_PREEMPT开关而不同。
spin_acquire(&lock->dep_map, , , _RET_IP_);-----------------如果相关调试选项没有打开,是一个空函数。
LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);--在没有定义CONFIG_LOCK_STAT的情况下,就是直接调用do_raw_spin_lock()。
} static inline void do_raw_spin_lock(raw_spinlock_t *lock) __acquires(lock)
{
__acquire(lock);
arch_spin_lock(&lock->raw_lock);------------------------------架构相关的0spin lock处理。

spin_unlock()和spin_lock()对应,最终调用__raw_spin_unlock()。

分别调用架构相关的arch_spin_unlock(),然后打开抢占。

static inline void spin_unlock(spinlock_t *lock)
{
raw_spin_unlock(&lock->rlock);
} #define raw_spin_unlock(lock) _raw_spin_unlock(lock) void __lockfunc _raw_spin_unlock(raw_spinlock_t *lock)
{
__raw_spin_unlock(lock);
} static inline void __raw_spin_unlock(raw_spinlock_t *lock)
{
spin_release(&lock->dep_map, , _RET_IP_);---------------------和spin_acquire()对应。
do_raw_spin_unlock(lock);
preempt_enable();----------------------------------------------打开抢占。
} static inline void do_raw_spin_unlock(raw_spinlock_t *lock) __releases(lock)
{
arch_spin_unlock(&lock->raw_lock);-----------------------------架构相关的spin unlock处理。
__release(lock);
}

spin_trylock()是spin_lock的变种,spin_trylock()尽力获得自旋锁lock,如果能立即获得锁,则获得锁并返回真;如果不能,则返回假。

spin_trylock()不会等待lock被释放。

static inline int spin_trylock(spinlock_t *lock)
{
return raw_spin_trylock(&lock->rlock);
} #define raw_spin_trylock(lock) __cond_lock(lock, _raw_spin_trylock(lock)) int __lockfunc _raw_spin_trylock(raw_spinlock_t *lock)
{
return __raw_spin_trylock(lock);
} static inline int __raw_spin_trylock(raw_spinlock_t *lock)
{
preempt_disable();
if (do_raw_spin_trylock(lock)) {
spin_acquire(&lock->dep_map, , , _RET_IP_);
return ;
}
preempt_enable();
return ;
} static inline int do_raw_spin_trylock(raw_spinlock_t *lock)
{
return arch_spin_trylock(&(lock)->raw_lock);
}

1.2.1 spinlock临界区为什么不允许发生抢占?

如果spinlock临界区中允许抢占,那么如果临界区内发生中断,中断返回时会去检查抢占调度。

这里有两个问题,一是抢占调度相当于持有锁的进程睡眠,违背了spinlock锁不能睡眠和快速执行完成的设计;

二是抢占调度进程也有可能会去申请spinlock锁,那么会导致发生死锁。

1.3 arch_spin_lock()/arch_spin_unlock()/arch_spin_trylock()

通过上面的分析可知,spin_lock()/spin_unlock()的核心是架构相关的部分。

static inline void arch_spin_lock(arch_spinlock_t *lock)
{
unsigned long tmp;
u32 newval;
arch_spinlock_t lockval; prefetchw(&lock->slock);
__asm__ __volatile__(
"1: ldrex %0, [%3]\n"
" add %1, %0, %4\n"
" strex %2, %1, [%3]\n"
" teq %2, #0\n"
" bne 1b"
: "=&r" (lockval), "=&r" (newval), "=&r" (tmp)
: "r" (&lock->slock), "I" ( << TICKET_SHIFT)-----------------------------TICKET_SHIFT为16,也即lock->tickets.next++。
: "cc"); while (lockval.tickets.next != lockval.tickets.owner) {-------------------next是当前进程对应锁的等号牌,判断owner和next是否相等。如相等表示CPU成功获取了spinlock锁,arch_spin_lock()返回;如不等,则调用wfe让CPU进入等待状态。
wfe();
lockval.tickets.owner = ACCESS_ONCE(lock->tickets.owner);-------------更新整个系统锁持有者的等号牌,lockval.tickets.owner一直在变,而lockval.tickets.next是当前位置持锁等号牌,不变。
} smp_mb();-----------------------------------------------------------------内存屏障
}

arch_spin_unlock()更新owner域的值,并且发送指令唤醒CPU。

static inline void arch_spin_unlock(arch_spinlock_t *lock)
{
smp_mb();
lock->tickets.owner++;---------------------------------------------------更新owner,表示所得持有者转到下一个临界区。
dsb_sev();
}

可以看出arch_spin_trylock()并没有类似arch_spin_lock()一样执行wfe指令,所以会直接返回1或者0.

所以使用spin_trylock()的代码路径不会阻塞,它会导致其他欲持有该锁的代码路径阻塞。

在使用spin_trylock()成功获取锁之后,仍然需要使用spin_unlock()解锁。

static inline int arch_spin_trylock(arch_spinlock_t *lock)
{
unsigned long contended, res;
u32 slock; prefetchw(&lock->slock);
do {
__asm__ __volatile__(
" ldrex %0, [%3]\n"
" mov %2, #0\n"
" subs %1, %0, %0, ror #16\n"
" addeq %0, %0, %4\n"
" strexeq %2, %0, [%3]"
: "=&r" (slock), "=&r" (contended), "=&r" (res)
: "r" (&lock->slock), "I" ( << TICKET_SHIFT)
: "cc");
} while (res); if (!contended) {
smp_mb();
return ;
} else {
return ;
}
}

1.3.1 wfe和wfi对比

wfi和wfe指令都是让ARM核进入standby睡眠模式。wfi是直到有wfi唤醒事件发生才会唤醒CPU,wfe是直到wfe唤醒事件发生,这两类事件大部分相同。

唯一不同之处在于wfr可以被其他CPU上的sev指令唤醒,sec指令用于修改event寄存器的指令。

1.3.2 smp_mb和dsb_sev

smp_mb调用dmb指令来保证把调用函数之前所有的访问内存指令都执行完成。

dsb_sev()包含两个指令dsb和sev。dsb指令保证之前的owner与已经写入内存中;sev指令来唤醒通过wfe指令进入睡眠状态的CPU。

此处arch_spin_lock()的wfe()等待处被唤醒,进行owner更新以及和next比较。

2. spinlock变种

上面是spinlock最常见的形式spin_lock()/spin_trylock()/spin_unlock()。

spinlock还有其他一些变种。

2.1 irq变种

2.1.1 为什么需要irq变种

假设临界区有链表操作需要spinlock来保护。

当处于临界区发生了外部硬件中断,此时系统暂停当前进程的执行转而去处理该中断。

假设中断处理程序恰巧也要操作该链表,链表的操作是一个临界区,所以在操作之前要调用spin_lock()函数来对该链表进行保护。

中断处理函数试图去获取该spinlock,但因为它已经被别人持有了,于是导致中断处理函数进入忙等待状态或者wfe睡眠状态。

在中断上下文出现忙等待或者睡眠状态是致命的,中断处理程序要求“短“和”快“,锁的持有者因为被中断打断而不能尽快释放锁,而中断处理程序一直在忙等待锁,从而导致死锁的发生。

Linux内核spin_lock_irq()函数在获取spinlock时关闭本地CPU中断,可以解决该问题。

2.1.2 spin_lock_irq()/spin_trylock_irq()/spin_unlock_irq()

从下面三个函数的调用关系可以知道,最终跟spin_lock()/spin_trylock()/spin_unlock()相似,只是中间增加了local_irq_disable()/local_irq_enable()。

local_irq_disable()用于关闭本地处理器中断,这样在获取spinlock琐时可以确保不会发生中断,从而避免发生死锁问题。

因此spin_lock_irq()主要防止本地中断处理程序和持有锁者之间存在锁的争用。

spin_lock_irq()代码路径如下:

static inline void spin_lock_irq(spinlock_t *lock)
{
raw_spin_lock_irq(&lock->rlock);
} #define raw_spin_lock_irq(lock) _raw_spin_lock_irq(lock) void __lockfunc _raw_spin_lock_irq(raw_spinlock_t *lock)
{
__raw_spin_lock_irq(lock);
} static inline void __raw_spin_lock_irq(raw_spinlock_t *lock)
{
local_irq_disable();-------------------------------------------------------关本地处理器中断
preempt_disable();---------------------------------------------------------禁止抢占
spin_acquire(&lock->dep_map, , , _RET_IP_);
LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
}

下面是spin_unlock_irq()代码路径,可以看出在spin_lock_irq()和spin_unlock_irq()之间的部分是关本地中断和禁止抢占的。

static inline void spin_unlock_irq(spinlock_t *lock)
{
raw_spin_unlock_irq(&lock->rlock);
} #define raw_spin_unlock_irq(lock) _raw_spin_unlock_irq(lock) void __lockfunc _raw_spin_unlock_irq(raw_spinlock_t *lock)
{
__raw_spin_unlock_irq(lock);
} static inline void __raw_spin_unlock_irq(raw_spinlock_t *lock)
{
spin_release(&lock->dep_map, , _RET_IP_);
do_raw_spin_unlock(lock);
local_irq_enable();---------------------------------------------------------打开本地处理器中断
preempt_enable();-----------------------------------------------------------允许抢占
}

spin_trylock_irq()主要通过raw_spin_trylock(),所以这里和spin_trylock()的区别也仅仅是关中断/开中断。

如果获取锁成功,那么后面会有spin_unlock_irq()来开中断;如果获取失败,直接开中断。

static inline int spin_trylock_irq(spinlock_t *lock)
{
return raw_spin_trylock_irq(&lock->rlock);
} #define raw_spin_trylock_irq(lock) \
({ \
local_irq_disable(); \-----------------------------------------------------增加禁止本地处理器中断
raw_spin_trylock(lock) ? \
: ({ local_irq_enable(); ; }); \---------------------------------------如果获取锁不成功,打开本地处理器中断
})

2.2 bh变种

bh变种可以对照原生的spinlock,区别在于原生spinlock使用preempt_enable()/preempt_disable()来禁止抢占;而bh变种不但表示了当前处于软中断上下文,还可能会禁止抢占,这取决于SOFTIRQ_LOCK_OFFSET的定义。

spin_lock_bh():

static inline void spin_lock_bh(spinlock_t *lock)
{
raw_spin_lock_bh(&lock->rlock);
} #define raw_spin_lock_bh(lock) _raw_spin_lock_bh(lock) void __lockfunc _raw_spin_lock_bh(raw_spinlock_t *lock)
{
__raw_spin_lock_bh(lock);
} static inline void __raw_spin_lock_bh(raw_spinlock_t *lock)
{
__local_bh_disable_ip(_RET_IP_, SOFTIRQ_LOCK_OFFSET);
spin_acquire(&lock->dep_map, , , _RET_IP_);
LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
}

spin_trylock_bh():

static inline int spin_trylock_bh(spinlock_t *lock)
{
return raw_spin_trylock_bh(&lock->rlock);
} #define raw_spin_trylock_bh(lock) \
__cond_lock(lock, _raw_spin_trylock_bh(lock)) int __lockfunc _raw_spin_trylock_bh(raw_spinlock_t *lock)
{
return __raw_spin_trylock_bh(lock);
} static inline int __raw_spin_trylock_bh(raw_spinlock_t *lock)
{
__local_bh_disable_ip(_RET_IP_, SOFTIRQ_LOCK_OFFSET);
if (do_raw_spin_trylock(lock)) {
spin_acquire(&lock->dep_map, , , _RET_IP_);
return ;
}
__local_bh_enable_ip(_RET_IP_, SOFTIRQ_LOCK_OFFSET);
return ;
}

spin_unlock_bh():

static inline void spin_unlock_bh(spinlock_t *lock)
{
raw_spin_unlock_bh(&lock->rlock);
} #define raw_spin_unlock_bh(lock) _raw_spin_unlock_bh(lock) void __lockfunc _raw_spin_unlock_bh(raw_spinlock_t *lock)
{
__raw_spin_unlock_bh(lock);
} static inline void __raw_spin_unlock_bh(raw_spinlock_t *lock)
{
spin_release(&lock->dep_map, , _RET_IP_);
do_raw_spin_unlock(lock);
__local_bh_enable_ip(_RET_IP_, SOFTIRQ_LOCK_OFFSET);
}

2.3 irqsave变种

下面是irqsave的三个变种函数,这些函数和irq变种基本上一致。

只是local_irq_enable()/local_irq_disable()换成了local_irq_save()/local_irq_restore()。

主要是保存和恢复CPSR寄存器内容,这样做的目的是防止破坏掉中断响应的状态。

spin_lock_irqsave()函数:

#define spin_lock_irqsave(lock, flags)                \
do { \
raw_spin_lock_irqsave(spinlock_check(lock), flags); \
} while () #define raw_spin_lock_irqsave(lock, flags) \
do { \
typecheck(unsigned long, flags); \
flags = _raw_spin_lock_irqsave(lock); \
} while () unsigned long __lockfunc _raw_spin_lock_irqsave(raw_spinlock_t *lock)
{
return __raw_spin_lock_irqsave(lock);
} static inline unsigned long __raw_spin_lock_irqsave(raw_spinlock_t *lock)
{
unsigned long flags; local_irq_save(flags);------------------------------------------------保存中断转改CPSR寄存器到flags,然后关闭本地CPU中断。
preempt_disable();----------------------------------------------------关闭抢占。
spin_acquire(&lock->dep_map, , , _RET_IP_);
/*
* On lockdep we dont want the hand-coded irq-enable of
* do_raw_spin_lock_flags() code, because lockdep assumes
* that interrupts are not re-enabled during lock-acquire:
*/
do_raw_spin_lock_flags(lock, &flags);
return flags;
} static inline void
do_raw_spin_lock_flags(raw_spinlock_t *lock, unsigned long *flags) __acquires(lock)
{
__acquire(lock);
arch_spin_lock_flags(&lock->raw_lock, *flags);
} #define arch_spin_lock_flags(lock, flags) arch_spin_lock(lock)

spin_trylock_irqsave()函数:

#define spin_trylock_irqsave(lock, flags)            \
({ \
raw_spin_trylock_irqsave(spinlock_check(lock), flags); \
}) #define raw_spin_trylock_irqsave(lock, flags) \
({ \
local_irq_save(flags); \
raw_spin_trylock(lock) ? \
: ({ local_irq_restore(flags); ; }); \
})

spin_unlock_irqrestore()函数:

static inline void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags)
{
raw_spin_unlock_irqrestore(&lock->rlock, flags);
} #define raw_spin_unlock_irqrestore(lock, flags) \
do { \
typecheck(unsigned long, flags); \
_raw_spin_unlock_irqrestore(lock, flags); \
} while () void __lockfunc _raw_spin_unlock_irqrestore(raw_spinlock_t *lock, unsigned long flags)
{
__raw_spin_unlock_irqrestore(lock, flags);
} static inline void __raw_spin_unlock_irqrestore(raw_spinlock_t *lock,
unsigned long flags)
{
spin_release(&lock->dep_map, , _RET_IP_);
do_raw_spin_unlock(lock);
local_irq_restore(flags);-----------------------------------------恢复flags到寄存器中,并打开处理器中断相应。
preempt_enable();-------------------------------------------------打开抢占。
}

2.4 preempt_enable()/preempt_disable()和__local_bh_enable_ip()/__local_bh_disable_ip()

这四个函数都是改变进程preempt_count计数的,其中preempt_enable()/preempt_disable()只修改preempt域,表示是否允许抢占。

#define preempt_enable() \
do { \
barrier(); \
preempt_count_dec(); \
} while () #define preempt_count_dec() preempt_count_sub() #define preempt_count_sub(val) __preempt_count_sub(val) #define preempt_disable() \
do { \
preempt_count_inc(); \
barrier(); \
} while () #define preempt_count_inc() preempt_count_add() #define preempt_count_add(val) __preempt_count_add(val)

而__local_bh_enable_ip()/__local_bh_disable_ip()修改的内容则是可变的,具体到bh变种中对应的是SOFTIRQ_LOCK_OFFSET。

#define SOFTIRQ_LOCK_OFFSET (SOFTIRQ_DISABLE_OFFSET + PREEMPT_CHECK_OFFSET)

由上面的定义可知,这里可能会禁止软中断和抢占。

void __local_bh_enable_ip(unsigned long ip, unsigned int cnt)
{
...
preempt_count_sub(cnt - ); if (unlikely(!in_interrupt() && local_softirq_pending())) {
/*
* Run softirq if any pending. And do it in its own stack
* as we may be calling this deep in a task call stack already.
*/
do_softirq();
} preempt_count_dec();
...
} static __always_inline void __local_bh_disable_ip(unsigned long ip, unsigned int cnt)
{
preempt_count_add(cnt);
barrier();
}

2.5 local_irq_enable()/local_irq_disable()、local_irq_save()/local_irq_restore()

这四个函数最终都通过架构相关的函数实现。

#define local_irq_enable()    do { raw_local_irq_enable(); } while (0)
#define local_irq_disable() do { raw_local_irq_disable(); } while (0)
#define local_irq_save(flags) \
do { \
raw_local_irq_save(flags); \
} while ()
#define local_irq_restore(flags) do { raw_local_irq_restore(flags); } while (0) #define raw_local_irq_disable() arch_local_irq_disable()
#define raw_local_irq_enable() arch_local_irq_enable()
#define raw_local_irq_save(flags) \
do { \
typecheck(unsigned long, flags); \
flags = arch_local_irq_save(); \
} while ()
#define raw_local_irq_restore(flags) \
do { \
typecheck(unsigned long, flags); \
arch_local_irq_restore(flags); \
} while () /*
* Disable IRQs
*/
static inline void arch_local_irq_disable(void)
{
unsigned long temp;
asm volatile(
" mrs %0, cpsr @ arch_local_irq_disable\n"
" orr %0, %0, #128\n"
" msr cpsr_c, %0"
: "=r" (temp)
:
: "memory", "cc");
} /*
* Enable IRQs
*/
static inline void arch_local_irq_enable(void)
{
unsigned long temp;
asm volatile(
" mrs %0, cpsr @ arch_local_irq_enable\n"
" bic %0, %0, #128\n"
" msr cpsr_c, %0"
: "=r" (temp)
:
: "memory", "cc");
} /*
* Save the current interrupt enable state & disable IRQs
*/
static inline unsigned long arch_local_irq_save(void)
{
unsigned long flags, temp; asm volatile(
" mrs %0, cpsr @ arch_local_irq_save\n"
" orr %1, %0, #128\n"
" msr cpsr_c, %1"
: "=r" (flags), "=r" (temp)
:
: "memory", "cc");
return flags;
} static inline void arch_local_irq_restore(unsigned long flags)
{
asm volatile(
" msr " IRQMASK_REG_NAME_W ", %0 @ local_irq_restore"
:
: "r" (flags)
: "memory", "cc");
}

3. spin_lock()和raw_spin_lock()

对于没有打RT-patch的平台,spin_lock()直接调用raw_spin_lock()。

在传统的include/linux/spinlock.h中定义了spin_lock()。

和RT-pathc后的内核差异在spin_lock()的定义转到spinlock_rt.h中去了。

#ifdef CONFIG_PREEMPT_RT_FULL
# include <linux/spinlock_rt.h>
#else /* PREEMPT_RT_FULL */
...static __always_inline void spin_lock(spinlock_t *lock)
{
raw_spin_lock(&lock->rlock);
}
...
#endif /* !PREEMPT_RT_FULL */

Linux的RT-patch旨在提升Linux内核的实时性,它允许在spinlock锁的临界区内被强占,且临界区内允许进程睡眠等到,这样会导致spinlock语义被修改。

当打开CONFIG_PREEMPT_RT_FULL选项后,spin_lock()的工作就从raw_spin_lock()变成了rt_spin_lock()。

rt_spin_lock()依赖于rt_mutex,其中允许睡眠和抢占。

#define spin_lock(lock)            rt_spin_lock(lock)

void __lockfunc rt_spin_lock(spinlock_t *lock)
{
rt_spin_lock_fastlock(&lock->lock, rt_spin_lock_slowlock, true);
spin_acquire(&lock->dep_map, , , _RET_IP_);
} static inline void rt_spin_lock_fastlock(struct rt_mutex *lock,
void (*slowfn)(struct rt_mutex *lock,
bool mg_off),
bool do_mig_dis)
{
might_sleep_no_state_check(); if (do_mig_dis)
migrate_disable(); if (likely(rt_mutex_cmpxchg_acquire(lock, NULL, current)))
rt_mutex_deadlock_account_lock(lock, current);
else
slowfn(lock, do_mig_dis);
} static void noinline __sched rt_spin_lock_slowlock(struct rt_mutex *lock,
bool mg_off)
{
...
}

内核中真正不允许睡眠的地方修改为使用raw_spin_lock(),其它地方使用spin_lock()。

spin_lock()和raw_spin_lock()的区别在于:在绝对不允许被强占和睡眠的临界区,应该使用raw_spin_lock(),否则使用spin_lock()。

Linux并发与同步专题 (2)spinlock的更多相关文章

  1. Linux并发与同步专题 (4) Mutex互斥量

    关键词:mutex.MCS.OSQ. <Linux并发与同步专题 (1)原子操作和内存屏障> <Linux并发与同步专题 (2)spinlock> <Linux并发与同步 ...

  2. Linux并发与同步专题 (3) 信号量

    关键词:Semaphore.down()/up(). <Linux并发与同步专题 (1)原子操作和内存屏障> <Linux并发与同步专题 (2)spinlock> <Li ...

  3. Linux并发与同步专题 (1)原子操作和内存屏障

    关键词:. <Linux并发与同步专题 (1)原子操作和内存屏障> <Linux并发与同步专题 (2)spinlock> <Linux并发与同步专题 (3) 信号量> ...

  4. Linux并发与同步专题

    并发访问:多个内核路径同时访问和操作数据,就有可能发生相互覆盖共享数据的情况,造成被访问数据的不一致. 临界区:访问和操作共享数据的代码段. 并发源:访问临界区的执行线程或代码路径. 在内核中产生并发 ...

  5. Linux海量数据高并发实时同步架构方案杂谈

    不论是Redhat还是CentOS系统,除去从CDN缓存或者数据库优化.动静分离等方面来说,在架构层面上,实 现海量数据高并发实时同步访问概括起来大概可以从以下几个方面去入手,当然NFS的存储也可以是 ...

  6. 漫画|Linux 并发、竞态、互斥锁、自旋锁、信号量都是什么鬼?(转)

    知乎链接:https://zhuanlan.zhihu.com/p/57354304 1. 锁的由来? 学习linux的时候,肯定会遇到各种和锁相关的知识,有时候自己学好了一点,感觉半桶水的自己已经可 ...

  7. linux和windows同步数据 cwrsync client to rsync server

    linux和windows同步数据,rsync server  cwrsync client linux server一般系统都自带rsync,如果没有就挂载系统盘自己安装一下,安装挺简单的不用我再多 ...

  8. linux下的同步与互斥

    linux下的同步与互斥 谈到linux的并发,必然涉及到线程之间的同步和互斥,linux主要为我们提供了几种实现线程间同步互斥的 机制,本文主要介绍互斥锁,条件变量和信号量.互斥锁和条件变量包含在p ...

  9. 【Linux】多线程同步的四种方式

    背景问题:在特定的应用场景下,多线程不进行同步会造成什么问题? 通过多线程模拟多窗口售票为例: #include <iostream> #include<pthread.h> ...

随机推荐

  1. Python入门基础之迭代和列表生成式

    什么是迭代 在Python中,如果给定一个list或tuple,我们可以通过for循环来遍历这个list或tuple,这种遍历我们成为迭代(Iteration). 在Python中,迭代是通过 for ...

  2. Redis常用命令【字符串】

    1.启动Redis客户端 进入src目录下,执行:redis-cli启动Redis客户端 2.help 帮助 帮助命令,用来查看redis命令的使用方式 3.set 设置 3.1设置 3.2不存在才设 ...

  3. 自动化测试基础篇--Selenium多窗口、句柄问题

    摘自https://www.cnblogs.com/sanzangTst/p/7680402.html 有时我们在打开浏览器浏览网页时,当点击网页上某些链接时,它不是直接在当前页面上跳转,而是重新打开 ...

  4. 内核线程的进程描述符task_struct中的mm和active_mm

    task_struct进程描述符中包含两个跟进程地址空间相关的字段mm, active_mm, struct task_struct { // ... struct mm_struct *mm; st ...

  5. c/c++柔性数组成员

    柔性数组成员 定义和声明分离 #include <stdio.h> //只是告诉编译器,当编译到使用到这个函数的的代码时,虽然还没有找到函数定义的实体,但是也让它编译不出错误. exter ...

  6. python3+xlwt 读取txt信息并写入到excel中

    # coding = utf-8 import os import xlwt import re def readTxt_toExcel(valueList, Pathlist): workbook ...

  7. HBase数据库相关基本知识

    HBase数据库相关知识 1. HBase相关概念模型 l  表(table),与关系型数据库一样就是有行和列的表 l  行(row),在表里数据按行存储.行由行键(rowkey)唯一标识,没有数据类 ...

  8. 聚类——K-means

    聚类——认识K-means算法 作者:凯鲁嘎吉 - 博客园 http://www.cnblogs.com/kailugaji/ 一.聚类与分类 聚类: 无监督学习.聚类是在预先不知道欲划分类的情况下, ...

  9. Python虚拟环境笔记

    虚拟环境 为什么需要虚拟环境: 到目前位置,我们所有的第三方包安装都是直接通过pip install xx的方式进行安装的,这样安装会将那个包安装到你的系统级的Python环境中.但是这样有一个问题, ...

  10. Nginx的configure各项中文说明

    –prefix=<path> – Nginx安装路径.如果没有指定,默认为 /usr/local/nginx. –sbin-path=<path> – Nginx可执行文件安装 ...