DPDK 锁:ticketlock和mcslock
DPDK 锁:ticketlock和mcslock
RToax 2021年4月
1. spinlock
/*** The rte_spinlock_t type.*/
typedef struct {volatile int locked; /**< lock status 0 = unlocked, 1 = locked */
} rte_spinlock_t;
其中volatile
标识易失的,表明每次读取locked变量时需要直接从内存中读取,这虽然减少了缓存震荡带来的问题,但一次内存IO的开销也是严重的。
使用__atomic_xxx
实现,此处的宏定义的意思为:
__ATOMIC_RELAXED
//编译器和处理器可以对memory access做任何的reorder__ATOMIC_ACQUIRE
//保证本线程中,所有后续的读操作必须在本条原子操作完成后执行。__ATOMIC_RELEASE
//保证本线程中,所有之前的写操作完成后才能执行本条原子操作。__ATOMIC_ACQ_REL
//同时包含 memory_order_acquire 和 memory_order_release__ATOMIC_CONSUME
//释放消费顺序(release/consume)的规范正在修订中,而且暂时不鼓励使用__ATOMIC_SEQ_CST
//最强的同步模式,同时也是默认的模型,具有强烈的happens-before语义
在下面的
static inline void
rte_spinlock_lock(rte_spinlock_t *sl)
{int exp = 0;while (!__atomic_compare_exchange_n(&sl->locked, &exp, 1, 0,__ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) {while (__atomic_load_n(&sl->locked, __ATOMIC_RELAXED))rte_pause();exp = 0;}
}
static inline void
rte_spinlock_unlock (rte_spinlock_t *sl)
{__atomic_store_n(&sl->locked, 0, __ATOMIC_RELEASE);
}
static inline int
rte_spinlock_trylock (rte_spinlock_t *sl)
{int exp = 0;return __atomic_compare_exchange_n(&sl->locked, &exp, 1,0, /* disallow spurious failure */__ATOMIC_ACQUIRE, __ATOMIC_RELAXED);
}
static inline int rte_spinlock_is_locked (rte_spinlock_t *sl)
{return __atomic_load_n(&sl->locked, __ATOMIC_ACQUIRE);
}
使用__sync_xxx
实现
static inline void
rte_spinlock_lock(rte_spinlock_t *sl)
{while (__sync_lock_test_and_set(&sl->locked, 1))while (sl->locked)rte_pause();
}static inline void
rte_spinlock_unlock(rte_spinlock_t *sl)
{__sync_lock_release(&sl->locked);
}static inline int
rte_spinlock_trylock(rte_spinlock_t *sl)
{return __sync_lock_test_and_set(&sl->locked, 1) == 0;
}
使用asm
实现:
static inline void
rte_spinlock_lock(rte_spinlock_t *sl)
{int lock_val = 1;asm volatile ("1:\n""xchg %[locked], %[lv]\n""test %[lv], %[lv]\n""jz 3f\n""2:\n""pause\n""cmpl $0, %[locked]\n""jnz 2b\n""jmp 1b\n""3:\n": [locked] "=m" (sl->locked), [lv] "=q" (lock_val): "[lv]" (lock_val): "memory");
}static inline void
rte_spinlock_unlock (rte_spinlock_t *sl)
{int unlock_val = 0;asm volatile ("xchg %[locked], %[ulv]\n": [locked] "=m" (sl->locked), [ulv] "=q" (unlock_val): "[ulv]" (unlock_val): "memory");
}static inline int
rte_spinlock_trylock (rte_spinlock_t *sl)
{int lockval = 1;asm volatile ("xchg %[locked], %[lockval]": [locked] "=m" (sl->locked), [lockval] "=q" (lockval): "[lockval]" (lockval): "memory");return lockval == 0;
}
整体思路比较简单,此处不做解释。
2. ticketlock
/*** The rte_ticketlock_t type.*/
typedef union {uint32_t tickets;struct {uint16_t current;uint16_t next;} s;
} rte_ticketlock_t;
结构体中有三个变量:
- tickets:ticket,分为高低十六位;
- current:当前持有锁的cpu;
- 下一个即将持有锁;
ticketlock主要解决的问题是,当各个CPU的性能存在较小的差异,或者在调度过程中不同的调度优先级和不同的时间片,都会影响到原始spinlock获取锁线程的争抢顺序有可能先争抢lock的线程抢不到锁,而后来的线程却先抢到了,ticketlock就解决了这个问题。下面直接看实现(做了一定简化):
static inline void
rte_ticketlock_lock(rte_ticketlock_t *tl)
{uint16_t me = __atomic_fetch_add(&tl->s.next, 1, __ATOMIC_RELAXED);rte_wait_until_equal_16(&tl->s.current, me, __ATOMIC_ACQUIRE);
}static inline void
rte_ticketlock_unlock(rte_ticketlock_t *tl)
{uint16_t i = __atomic_load_n(&tl->s.current, __ATOMIC_RELAXED);__atomic_store_n(&tl->s.current, i + 1, __ATOMIC_RELEASE);
}static inline int
rte_ticketlock_trylock(rte_ticketlock_t *tl)
{rte_ticketlock_t old, new;old.tickets = __atomic_load_n(&tl->tickets, __ATOMIC_RELAXED);new.tickets = old.tickets;new.s.next++;if (old.s.next == old.s.current) {if (__atomic_compare_exchange_n(&tl->tickets, &old.tickets,new.tickets, 0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))return 1;}return 0;
}
3. mcslock
mcslock因发明者而得名,John M. Mellor-Crummey
和 Michael L. Scott
。在官方解释,mcslock主要解决的问题在于:
- 提供可扩展/可伸缩的自旋锁;
- 避免缓存震荡;
- 提供公平锁;
首先看下它的结构:
/*** The rte_mcslock_t type.*/
typedef struct rte_mcslock {struct rte_mcslock *next;int locked; /* 1 if the queue locked, 0 otherwise */
} rte_mcslock_t;
首先还是关注三个方面,lock,unlock,和 trylock
static inline void
rte_mcslock_lock(rte_mcslock_t **msl, rte_mcslock_t *me)
{rte_mcslock_t *prev;/* Init me node */__atomic_store_n(&me->locked, 1, __ATOMIC_RELAXED);__atomic_store_n(&me->next, NULL, __ATOMIC_RELAXED);prev = __atomic_exchange_n(msl, me, __ATOMIC_ACQ_REL);if (likely(prev == NULL)) {return;}__atomic_store_n(&prev->next, me, __ATOMIC_RELAXED);__atomic_thread_fence(__ATOMIC_ACQ_REL);while (__atomic_load_n(&me->locked, __ATOMIC_ACQUIRE))rte_pause();
}static inline void
rte_mcslock_unlock(rte_mcslock_t **msl, rte_mcslock_t *me)
{if (likely(__atomic_load_n(&me->next, __ATOMIC_RELAXED) == NULL)) {rte_mcslock_t *save_me = __atomic_load_n(&me, __ATOMIC_RELAXED);if (likely(__atomic_compare_exchange_n(msl, &save_me, NULL, 0,__ATOMIC_RELEASE, __ATOMIC_RELAXED)))return;__atomic_thread_fence(__ATOMIC_ACQUIRE);while (__atomic_load_n(&me->next, __ATOMIC_RELAXED) == NULL)rte_pause();}__atomic_store_n(&me->next->locked, 0, __ATOMIC_RELEASE);
}static inline int
rte_mcslock_trylock(rte_mcslock_t **msl, rte_mcslock_t *me)
{__atomic_store_n(&me->next, NULL, __ATOMIC_RELAXED);rte_mcslock_t *expected = NULL;return __atomic_compare_exchange_n(msl, &expected, me, 0,__ATOMIC_ACQ_REL, __ATOMIC_RELAXED);
}
首先看下完整的用法:
#include <stdio.h>
#include <pthread.h>#include "rte_mcslock.h"rte_mcslock_t *pmcslock;void *task1(void*arg){rte_mcslock_t ml;rte_mcslock_lock(&pmcslock, &ml);printf("MCS lock taken on thread %ld\n", pthread_self());sleep(2);rte_mcslock_unlock(&pmcslock, &ml);
}void *task2(void*arg){rte_mcslock_t ml;sleep(1);rte_mcslock_lock(&pmcslock, &ml);printf("MCS lock taken on thread %ld\n", pthread_self());rte_mcslock_unlock(&pmcslock, &ml);
}int main()
{pthread_t t1, t2;pthread_create(&t1, NULL, task1, NULL);pthread_create(&t2, NULL, task2, NULL);pthread_join(t1, NULL);pthread_join(t2, NULL);
}
输出为:
$ ./a.out
MCS lock taken on thread 140330136704768
MCS lock taken on thread 140330128312064
整体思路就是:为了方式缓存cache震荡,为每个线程提供自己的变量锁,这样,就克服了使用像原始的spinlock那样使用volatile关键字不使用cache造成的不能发挥缓存的优势的问题,同时,解决了缓存震荡问题。
4. rwlock
读写自旋锁的数据结构:
typedef struct {volatile int32_t cnt; /**< -1 when W lock held, > 0 when R locks held. */
} rte_rwlock_t;
从数据结构来看,和常规的自旋锁基本一致,除了将lock改成了计数器,这个cnt是用来计算读者个数的。
static inline void
rte_rwlock_read_lock(rte_rwlock_t *rwl)
{int32_t x;int success = 0;while (success == 0) {x = __atomic_load_n(&rwl->cnt, __ATOMIC_RELAXED);/* write lock is held */if (x < 0) {rte_pause();continue;}success = __atomic_compare_exchange_n(&rwl->cnt, &x, x + 1, 1,__ATOMIC_ACQUIRE, __ATOMIC_RELAXED);}
}static inline int
rte_rwlock_read_trylock(rte_rwlock_t *rwl)
{int32_t x;int success = 0;while (success == 0) {x = __atomic_load_n(&rwl->cnt, __ATOMIC_RELAXED);/* write lock is held */if (x < 0)return -EBUSY;success = __atomic_compare_exchange_n(&rwl->cnt, &x, x + 1, 1,__ATOMIC_ACQUIRE, __ATOMIC_RELAXED);}return 0;
}static inline void
rte_rwlock_read_unlock(rte_rwlock_t *rwl)
{__atomic_fetch_sub(&rwl->cnt, 1, __ATOMIC_RELEASE);
}static inline int
rte_rwlock_write_trylock(rte_rwlock_t *rwl)
{int32_t x;x = __atomic_load_n(&rwl->cnt, __ATOMIC_RELAXED);if (x != 0 || __atomic_compare_exchange_n(&rwl->cnt, &x, -1, 1,__ATOMIC_ACQUIRE, __ATOMIC_RELAXED) == 0)return -EBUSY;return 0;
}static inline void
rte_rwlock_write_lock(rte_rwlock_t *rwl)
{int32_t x;int success = 0;while (success == 0) {x = __atomic_load_n(&rwl->cnt, __ATOMIC_RELAXED);/* a lock is held */if (x != 0) {rte_pause();continue;}success = __atomic_compare_exchange_n(&rwl->cnt, &x, -1, 1,__ATOMIC_ACQUIRE, __ATOMIC_RELAXED);}
}static inline void
rte_rwlock_write_unlock(rte_rwlock_t *rwl)
{__atomic_store_n(&rwl->cnt, 0, __ATOMIC_RELEASE);
}
DPDK 锁:ticketlock和mcslock相关推荐
- java 锁的类型_Java锁的种类 - shawnplaying的个人页面 - OSCHINA - 中文开源技术交流社区...
Java锁和并发需要结合在一块了理解,涉及到了多个话题. 本文主要参考了 http://ifeve.com/java_lock_see1/ 但是我认为原文中有某些错误,我在下面的代码中做了修改. 公平 ...
- 一文足以了解什么是 Java 中的锁
作者 | cxuan 责编 | Elle Java 锁分类 Java 中的锁有很多,可以按照不同的功能.种类进行分类,下面是我对 Java 中一些常用锁的分类,包括一些基本的概述 从线程是否需要对资 ...
- mysql 自旋锁,golang 自旋锁
CAS算法(compare and swap) CAS算法是一种有名的无锁算法.无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步( ...
- Golang之自旋锁
Golang之自旋锁 目录 Golang之自旋锁 自旋锁 golang实现自旋锁 可重入的自旋锁和不可重入的自旋锁 自旋锁的其他变种 1. TicketLock TicketLock主要解决的是公平性 ...
- 理解悲观锁乐观锁、同步锁、读锁、写锁
ava 锁分类 Java 中的锁有很多,可以按照不同的功能.种类进行分类,下面是我对 Java 中一些常用锁的分类,包括一些基本的概述 从线程是否需要对资源加锁可以分为 悲观锁 和 乐观锁 从资源已被 ...
- 悲观|乐观锁、自旋|互斥锁、公平|非公平锁
解析锁--悲观|乐观锁.自旋|互斥锁.公平|非公平锁 悲观锁 总认为最坏的情况可能会出现,即认为数据很可能会被他人修改,因此在持有数据时总是先把资源或数据锁住.这样其他线程要请求这个资源时就会阻塞,直 ...
- Java锁——什么是锁?
创作不易,原文: https://mp.weixin.qq.com/s?__biz=MzkwMDE1MzkwNQ==&mid=2247496038&idx=1&sn=10b96 ...
- 自旋锁原理及其应用场景
题外话 自旋锁不是一种锁(类型),自旋锁是线程没有获取到锁时的一种等待策略. 自旋锁的提出背景 由于在多处理器环境中某些资源的有限性,有时需要互斥访问(mutual exclusion),这时候就需要 ...
- Java并发编程—常见面试题
建议: 学习java并发前需要先掌握JVM知识 关于下面问题档案的详细解析都在后面推荐的相关系列文章中 一.线程安全相关 1.什么叫线程安全? 线程安全就是说多线程访问同一代码,不会产生不确定的结果. ...
最新文章
- jquery和JavaScript区别
- 欧几里得范数_机器学习中的范数究竟是个什么鬼?
- 2、(整数类型)INT、TINYINT、SMALLINT、MEDIUMINT、BIGINT
- 他们是最懂数据的商家!智能品牌时代到来
- 科学历史也可以写的如此精彩 ——《量子物理史话:上帝掷骰子吗》读后感
- 【网址收藏】主流Ansible Web UI的部署与使用:Tower AWX Semaphore TiOps
- java线程死锁_Java并发:隐藏线程死锁
- 【报告分享】2020年抖音kol生态研究报告.pdf(附下载链接)
- 产生斜体的html标签,下列可以产生斜体字的 HTML 标签是_____________
- 双系统重装Ubuntu经验分享
- 美国ipv6云服务器配置,Vultr服务器添加ipv6地址的方法
- Linux文本比较指令(diff,cmp)详解
- RPG Maker MV 打包安卓和修改分辨率的方法
- 免费在线证件照制作-超级好用
- Whole Word Masking (wwm) BERT PaddlePaddle常用预训练模型加载
- 不会编程,别着急!免编程工具助你快速开发App
- Grade for Android 之二:Groovy 与Java的语法区别
- 建筑师妹岛和世为日本西武设计新型旗舰“球形车头”列车
- ListView的简单使用《一》—普通图文展示案例
- three.js开发全景视频播放器的现实方法
热门文章
- Java使用iText实现对PDF文件的操作
- java 语法检查_java编译期间的语法检查
- 对数据类型而言运算符无效。运算符为 add,类型为 text。
- Pi 3B+编译安装python3.6.8
- 微信小程序Server端环境配置
- Android应用程序文件缓存getCacheDir()和getExternalCacheDir()
- MFC多文档中opencv处理图像打开、保存
- 4-3-串的块链存储结构-串-第4章-《数据结构》课本源码-严蔚敏吴伟民版
- QTP中实现对文本文件(txt)的读写操作
- 转载 Android入门学习_代码常用布局