从CPU cache一致性的角度看Linux spinlock的不可伸缩性(non-scalable)

2023-11-16

凌晨一点半的深圳雨夜:
这里写图片描述
豪雨当夜惊起有人赏,笑叹落花无声空飘零。
喜欢这种豪雨,让人兴奋。惊起作文以呜呼之感叹!


用上一篇文章:
优化多核CPU的TCP新建连接性能–重排spinlockhttps://blog.csdn.net/dog250/article/details/80575731
在这篇文章中,我将一个spinlock拆解成了per cpu的,然而并没有提及spinlock本身的性能和可伸缩性(scalable),那么本文就来说一下。


一点说明

正文开始之前,先给出本文讨论的各个场景基于的CPU布局图,本文中我们将描述很多的场景实验,因为是分析原理,我将其定义为思想实验,假设我们思想实验的系统拥有16核CPU,其中每一个CPU封装有2个物理核,每一个物理核有两个有独立cache的核心,其布局如下:
这里写图片描述

由于实现的可扩展性的原因,当前的大多数平台在实现CPU cache一致性协议时有两个选择,一个是snoopy,一个是点对点unicast。

  • snoopy方式
    这意味着cache一致性消息通过总线广播,于是就要侦听总线以在某个时机获得控制权。这种方式及其不易扩展,在CPU数目达到一定量时,总线的争抢会很严重。
    参考以太网的CSMA/CD协议,会得出一样的结论,正如CSMA/CD的基于总线的以太网由于其不可扩展性进化到了交换式以太网一样,CPU间的cache一致性协议的点对点unicast实现方式最终替代了snoopy(小规模本地cache一致性依然会采用snoopy的方式)。总而言之,是总线带宽限制了snoopy想大规模SMP发展。
  • 点对点unicast方式
    既然snoopy需要严格的总线仲裁,采用点对点的方式进行cache一致性的消息路由就成了一个可选的应对措施,但是对于多个目的地的消息的发送方而言,one by one的按序in turn方式发送就成了唯一的选择,正是这种处理方式成了点对点unicast方式的另一个不可扩展性根源
    但这种不可扩展性是针对更上层而言,在cache一致性协议的实现上,它克服了总线仲裁带来的本层的不可扩展性。业界很多的场景都遵循了类似的总线–>点对点的发展轨迹,除了以太网之外,还有PCI到PCIe的进化。无论哪种实现方式,其过程都体现了采用被动的NAK仲裁向主动的消息路由方向的进化之路似乎是一条放之四海而皆准的康庄大道

理解这些,特别是理解点对点unicast方式的one by one处理时延会随着CPU核数的增加而线性增加为后面的内容埋下了伏笔。


在Linux内核中,先后有过新旧两种版本的自旋锁被大规模使用,一个是willd spinlock,另一个则是当前内核默认使用的ticket spinlock,下面的章节会简而述之。

好了,正文开始!


wild spinlock

这种自旋锁非常简单,简而言之就是一个整数:

typedef struct {
    volatile unsigned int lock;
} spinlock_t;

然后其加锁和解锁过程为:

void spin_lock(spinlock_t *lock)
{
    while (lock->lock == 1)
        ; // 自旋等待
}
void spin_unlock(spinlock_t *lock)
{
    lock->lock = 0;
}

然而在这种简单的背后却掩盖着一个血雨腥风的战场,我们看一下why。

根据上面的CPU布局图,我们可以了解到CPU之间的亲密关系是不同的,特定CPU的cache line更新同步到不同CPU的cache line的时间也不同,这就意味着等待自旋锁的CPU中哪个CPU先感知到lock->lock的变化,哪个就能优先获得锁。

这将大大有损等锁者之间的公平性。这就好比一群人不排队上火车,体格好的总是优先登车一样。

为了解决这个公平性问题,ticket spinlock登场了。

ticket spinlock登场

ticket spinlock在设计上增加了一个等锁期间单调递增的ticket字段(当然,在实现上如何体现这个ticket就是trick了),确保了先到者先获得锁,从而保证了公平性。


Linux ticket spinlock的实现和执行过程

我先给出Linux ticket spinlock的伪代码实现,为了讨论简单,以下的代码中所有的自加(i++)操作均为原子操作,避免锁中锁。

  • 定义spinlock
struct spinlock_t {
    // 上锁者自己的排队号
    int my_ticket;
    // 当前叫号
    int curr_ticket;
}

非常简单,一个标准的排队装置,类似银行叫号系统,每个办理业务的取一个号,然后按照号码的大小依次串行被服务。

  • spin_lock上锁
void spin_lock(spinlock_t *lock)
{
    int my_ticket;
    // 顺位拿到自己的ticket号码;
    my_ticket = lock->my_ticket++;
    while (my_ticket != lock->curr_ticket)
        ; // 自旋等待!
}
  • spin_unlock解锁
void spin_unlock(spinlock_t *lock)
{
    // 呼叫下一位!
    lock->curr_ticket++;
}

以上基本就是Linux ticket spinlock实现的全部了,没什么复杂的,没什么大不了的。如果说仅仅是为了读懂代码,确实没有什么大不了的,然而深究起来却可以发现它的不妙。

以下我就从cache的角度来分析一下这个自旋锁的执行过程,以下面的代码片段为例:

void demo()
{
    spin_lock(&g_lock);
    g_var1 ++;
    g_var2 --;
    g_var3 = g_var1 + g_var2;
    spin_unlock(&g_lock)
}

依然是按步骤来。好了,开始我们的步骤。

  • step 1:CPU0申请锁,获取本地ticket到申请者的CPU cache
    这里写图片描述
  • step 2:执行锁定区域指令的同时,其它CPU企图获取锁而自旋
    这里写图片描述
  • step 3:CPU0释放锁
    这里写图片描述

我们发现,上述的step3中的步骤有点太复杂了。当前的大多数平台倾向于用点对点unicast的方式来更新cache line(因为broadcast方式对总线带宽有要求,随着CPU核数的增加,实现复杂度会增加,这是另一种不可伸缩!),因此step3中更新每一个CPU cache line是一个one by one的过程,如果是write invalidate方式,就更会多出超级多的访存指令,这些对于理解Linux ticket spinlock的不可伸缩性至关重要!

很显然,随着CPU核数的增加,随着spinlock申请者的增加,step3中动作的执行时间会线性增加,最终,当spinlock的申请者达到一定数量时,多核CPU非但没有提高性能,反而由于cache line更新的时间过久,反过来损害性能。

注意,wild spinlock同样存在这个问题,wild spinlock背后的cache一致性过程和ticket spinlock完全一致,只是ticket spinlock严格限定了谁将下一个获得锁而wild spinlock并不能。


我们已经定性地描述了Linux ticket spinlock的当前实现会有什么问题,为了定量地衡量这种实现会带来哪些具体的后果,我需要简单说一下Linux ticket spinlock(此后简称Linux spinlock)经典的Markov chain模型,请看下节。

Linux spinlock的马尔科夫链模型

为了阐释Linux spinlock的具体性能表现和带来的结果,必然需要建立一个模型,本节我就说一下Linux spinlock的Markov chain模型,该模型结合排队论可以精确描述和预测Linux spinlock的行为。

先看一下图示:
这里写图片描述
解释一下:

这是一个Markov chain,其中一共有 0 0 ,11,… n n 一共n+1n+1个状态,每一个状态 k k ,表示系统中有kk个CPU正在自旋等待锁, A[k] A [ k ] 表示状态 k k 转换到状态k+1k+1的速率,也就是相当于请求锁的速率,而 S[k] S [ k ] 则表示状态 k+1 k + 1 转换到状态 k k 的速率,也就是相当于释放锁的速率。

简化起见,我们忽略掉spinlock到达的细节,即设在单一CPU核心上spinlock的请求达平均间隔为TarriveTarrive(事实上应该是指数分布),那么单一CPU核心的spinlock请求到达的速率就是 1Tarrive 1 T a r r i v e (事实上应该是泊松分布),由于我们的思想实验基于多核CPU平台,因此总体上的spinlock请求到达的速率和空闲CPU个数成正比。假设当前的 n n 核CPU系统上已经有kk个CPU在自旋等锁了,那么 nk n − k 个CPU上spinlock的到达速率为 nkTarrive n − k T a r r i v e ,因此,上图中的 A[k] A [ k ] 便求了出来:

A[k]=nkTarrive A [ k ] = n − k T a r r i v e

现在考虑释放锁的过程导致的状态转换。

先理解状态 S[k] S [ k ] 的意义, S[] S [ ] 这里是的是Service,相应的对应 A[] A [ ] 则表示Arrival,所谓的服务时间指的是从当前CPU获得spinlock到该spinlock成功转给下一个CPU之间的时间!这部分时间包括两个部分,一个部分是执行lock/unlock之间代码的时间 E E ,另一部分时间指的是unlock操作中消耗的时间,我们把这部分时间设为RR,现在的问题转化为根据以上的信息,求出 R R 的表达式。

我们在讨论中有个假设,即我们思想实验的平台对cache一致性中update的管理方式是点对点one by one的unicast方式,而不是broadcast的方式,设处理一个CPU的update时间为cc,那么在 k k 个CPU同时自旋等锁的条件下,所有的CPU的cache line全部更新的时间就是k×ck×c,由于ticket spinlock是严序的,而cache line update的到达先后却不可控制,因此下一个获得锁的CPU的cache line得到更新的时间平均值为 k×c2 k × c 2 ,即平均在这个时间后,下一个CPU即可以成功获取锁,从spin_lock自旋状态返回。于是,我们有:

S[k]=1E+k×c2 S [ k ] = 1 E + k × c 2

以上关于 A[k] A [ k ] S[k] S [ k ] 的表达式非常好理解,我们可以看到,请求的到达速率,即spin_lock的速率是和空闲CPU数量,即不在自旋状态的CPU数量成正比的,显然地,空闲CPU越多,请求越容易到达,相反,spin_unlock的速率,即 S[k] S [ k ] ,则是和当前自旋状态的CPU数量成反比的,至少是负相关的,因为自旋状态的CPU越多,更新这么多CPU的cache line的时间开销就越大,这意味着服务速率S[k]就会越小。

有了上面基本的结论,加上下面一个稳态下Markov chain状态转换率守恒的基本原则,就可以导出模型本身了:

P0,P1,...PnnΣPk=1 设 , P 0 , P 1 , . . . P n 是 系 统 处 在 这 n 个 状 态 的 概 率 , 显 然 Σ P k = 1 。 当 系 统 处 在 稳 态 时 , 下 面 的 式 子 成 立 :
Pk×A[k]=Pk+1×S[k] P k × A [ k ] = P k + 1 × S [ k ]

这里非常感谢阿里云的哥们儿帮我拉了一位数学界的大牛群聊,让我对Markov chain的状态转换率有了更深的理解!

这是一个递推的开始,我们可以求出 Pk P k 关于 k k 的表达式,然后模型里的参数就可以去用实际值填充了。具体过程不在这里罗列,最终通过上面的三个式子推导出来的公式为:

Pk=1Tkarrive(nk)!ki=0(E+ic)ni=01Tiarrive(ni)!ij=0(E+jc)Pk=1Tarrivek(nk)!i=0k(E+ic)i=0n1Tarrivei(ni)!j=0i(E+jc)

有了这个式子,我们便可以求出任意时刻系统中处在自旋等锁状态的CPU总量:

C=ni=0iPi C = ∑ i = 0 n i P i

模型最终在于解释历史数据和预测未来数据,在上面的基础上,我们导出一个加速比的概念,即 x x 个CPU的系统中争抢特定的sinlock,CPU总量xx中有多少CPU未处在自旋等锁的状态。显然,它的值为:

S=xC S = x − C

如果随着 x x 的增加,SS也增加,这意味着增加了CPU核心数量确实缓解了spinlock带来的串行化问题,但是不幸的是,情况并非如此。该模型显示出的 S/x S / x 曲线显示出当总CPU数量 x x 增加到一定程度,加速比会断崖跌落(CPU太多了,one by one的cache line操作太重太耗时了!),这意味着在达到某种条件的情况下,增加CPU核心数量反而会损害系统性能!

不可伸缩也就罢了,cache line的操作没想到影响如此之大!

关于Markov chain spinlock模型的推论

我们再看S[k]S[k]的表达式:

S[k]=1E+k×c2 S [ k ] = 1 E + k × c 2

这个式子是spinlock不可伸缩的根源。注意这里的 E E ,它对spinlock整体不可伸缩性的程度的影响至关重要!我们进一步可以将式子抽象为:

f(x)=1α+xf(x)=1α+x

然后我们来看一下 x/y x / y 曲线的走势:

这里写图片描述

回到我们的模型, E E 越大,cache一致性时间线性增长的影响就越不明显,只有在EE比较小的时候,我们才深受spinlock不可伸缩之害。然而spinlock的应用场合不就是critical section很小的时候吗?呜呼!

假设我们的critical section确实很小,那么 S[k] S [ k ] k k 之间就近似反比关系了,这样事情就严重多了。它的根源在于cache一致性的时间是O(n)O(n)的而不是常数级 O(1) O ( 1 ) 的,因此我们也就有了解决问题的方向。下一节详述。

<参考文献:Non-scalable locks are dangerous>

MCS spinlock如何解决问题

问题已经很明确,如何破解显然已经不是重要的事了。

既然问题出在公共共享变量上自旋导致的cache一致性时间开销太大,那么改成在私有变量上自旋即可,这显然是一个 O(1) O ( 1 ) 的操作,替掉之前不可伸缩的 O(n) O ( n ) 操作即可。

为了做到这一点,需要重新设计spinlock的数据结构。本节所描述的新的spinlock正是业内有名的MCS spinlock,一些简单的介绍请参考下面的文章:
MCS locks and qspinlockshttps://lwn.net/Articles/590243/

OK,让我们从代码的角度来看一下为什么MCS spinlock可以解决cache一致性导致的non-scalable问题。

mcs spinlock在Linux的实现在kernel/locking/mcs_spinlock.h文件,不过直到4.14版本内核,Linux并没有哪个子系统采用这个新的spinlock,不管怎样,既然代码在手,我们便可以自己用。

和ticket spinlock的描述一致:

  • 定义MCS spinlock
struct mcs_spinlock {
    struct mcs_spinlock *next; // 类似ticket的概念,所有争锁排队
    int locked; /* 1 if lock acquired */ // 本地的自旋变量,不再是全局
};

可见其创举在于在自旋变量纳入了排队实体本身。在mcs spinlock中,一个struct mcs_spinlock实例就是一个排队实体,它拥有自己的自旋变量locked,即它只需要不断check自己的locked变量即可。这体现了OO的思想

  • MCS spinlock上锁
// 自己的排队节点node需要自己在外部初始化,它将被排队到lock指示的等锁队列中。
void mcs_spin_lock(struct mcs_spinlock **lock, struct mcs_spinlock *node)
{
    struct mcs_spinlock *prev;

    /* Init node */
    node->locked = 0;
    node->next   = NULL;
    // 原子执行*lock = node并返回原来的*lock
    prev = xchg(lock, node);
    if (likely(prev == NULL)) {
        return;
    }
    // 原子执行 prev->next = node;
    // 这相当于一个排入队的操作。记为(*)
    WRITE_ONCE(prev->next, node);

    while (0 == &node->locked)
        ; // 在自己的locked变量上自旋等待!
}

没啥说的。

  • MCS spinlock解锁
// 自己要传入一个排入到lock的队列中的自己的node对象node,解锁操作就是node出队并且主动将队列中下一个对象的locked帮忙设置成1.
void mcs_spin_unlock(struct mcs_spinlock **lock, struct mcs_spinlock *node)
{
    // 原子获取node的下一个节点。
    struct mcs_spinlock *next = READ_ONCE(node->next);

    if (likely(!next)) {
        // 二次确认真的是NULL,则返回,说明自己是最后一个,什么都不需要做。
        if (likely(cmpxchg_release(lock, node, NULL) == node))
            return;
        // 否则说明在if判断next为MULL和cmpxchg原子操作之间有node插入,随即等待它的mcs_spin_lock调用完成,即上面mcs_spin_lock中的(*)注释那句完成以后
        while (!(next = READ_ONCE(node->next)))
            cpu_relax_lowlatency();
    }
    // 原子执行next->locked = 1;
    arch_mcs_spin_unlock_contended(&next->locked);
}

你已经看到了,unlock操作仅仅操作了next的locked字段,该字段属于在某个CPU上自旋等待的mcs spinlock对象,不会触发全局的cache一致性刷新!

上述的mcs spinlock实现,之所以解决了保证cache一致性而导致的开销随着CPU核数的增加线性增加的问题,就是因为一个简单的动作,即传球代替了抢球。ticket spinlock的问题在于,既然你已经知道谁是下一个获得锁的等待者,为什么不直接将控制权交给它呢?为什么还要有一个全局盲抢的动作呢?


告一段落?No!


也许你已经看到了当前Linux的MCS spinlock的实现问题,它的API变了,这意味着你无法对内核中已经使用的ticket spinlock进行无缝替换。我们需要下面的API:

void mcs_spin_lock(struct mcs_spinlock *lock);
void mcs_spin_unlock(struct mcs_spinlock *lock);

Ahha,由于同一个CPU同时只能等待一个spinlock,这件事就很简单了。来吧,让我们实现它:

struct mcs_spinlock_node {
        struct mcs_spinlock_node *next;
        int locked; /* 1 if lock acquired */
};

struct mcs_spinlock {
        struct mcs_spinlock_node nodes[NR_CPUS];
        struct mcs_spinlock_node *tail;
};


void mcs_spin_lock(struct mcs_spinlock *lock)
{
        struct mcs_spinlock_node *local;
        struct mcs_spinlock_node *prev;
        int cpu = smp_processor_id();

        local = &(lock->nodes[cpu]);
        local->locked = 0;
        local->next = NULL;

        prev = xchg(&lock->tail, local);
        if (prev == NULL) {
                return;
        }
        WRITE_ONCE(prev->next, local);

        while (!local->locked); 
}

void mcs_spin_unlock(struct mcs_spinlock *lock)
{
        struct mcs_spinlock_node *local;
        struct mcs_spinlock_node *next;
        int cpu = smp_processor_id();

        cpu = smp_processor_id();
        local = &(lock->nodes[cpu]);
        next = READ_ONCE(local->next);

        if (likely(!next)) {
                if (cmpxchg_release(lock->tail, local, NULL) == local) {
                        return;
                }
                while (!(next = READ_ONCE(local->next)));
        }

        local->next->locked = 1;
}

Look at this.是不是好看了很多呢?

好了,关于spinlock的话题差不多该结束了。贴出几篇我之前关于自旋锁的文章以体现延续性吧:
新的排队spin_lock–有序和无序https://blog.csdn.net/dog250/article/details/5303249?locationNum=10&fps=1
自旋锁的枝枝蔓蔓https://blog.csdn.net/dog250/article/details/5302886
本地自旋锁与信号量/多服务台自旋队列-spin wait风格的信号量https://blog.csdn.net/dog250/article/details/47098055
一个Linux内核的自旋锁设计-接力嵌套堆栈式自旋锁https://blog.csdn.net/dog250/article/details/46921989

<参考文献:MCS locks and qspinlocks>

Aliworkqueue

感谢fcicq大神给介绍aliworkqueue,这是同样优化策略的另一种体现,我还没有测试,但是看其实现感觉略微重量的些。不多说,直接看patch吧:
aliworkqueue: Adaptive lock integration on multi-core platformhttps://patchwork.kernel.org/patch/8844841/

Wire-latency(RC delay) dominate modern computer performance,
conventional serialized works cause cache line ping-pong seriously,
the process spend lots of time and power to complete.specially on multi-core platform.
However if the serialized works are sent to one core and executed
ONLY when contention happens, that can save much time and power,
because all shared data are located in private cache of one core.
We call the mechanism as Adaptive Lock Integration.
(ali workqueue)

此外,这里有一个fcicq大神的解说:
MaLing 离开某厂纪念文 & 介绍 aliworkqueuehttps://www.douban.com/note/596489332/

Aliworkqueue除了考虑到了spinlock本身加锁解锁对cache一致性的影响,还考虑到了spinlock保护的critical section操作的共享数据读写对cache一致性的影响,如此一来,本文上述的Markov chain模型就要进行修订了,因为critical section的操作时间不能再被当是常量 E E <script type="math/tex" id="MathJax-Element-85">E</script>了,而它也要作为一个和CPU核数相关的量参与到模型的构建中了。

不多说。

后记

凌晨3点半。豪雨继续中…
黑云压顶白珠跳,浙江温州皮鞋湿。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

从CPU cache一致性的角度看Linux spinlock的不可伸缩性(non-scalable) 的相关文章

  • linux内核的自旋锁spin_lock和互斥锁mutex_lock

    访问共享资源的代码区域称作临界区 自旋锁 spin lock 和互斥锁 mutex lock 是保护内核临界区的两种基本机制 我们逐个分析 自旋锁可以确保在同时只有一个线程进入临界区 其他想进入临界区的线程必须不停地原地打转 直到第1个线程
  • 互斥机制之自旋锁(spinlock)

    一 基础 自旋锁 如果测试结果表明锁仍被占用 程序将在一个小的循环内重复这个 测试并设置 操作 即进行所谓的 自旋 1 定义自旋锁 spinlock t spin 2 初始化自旋锁 spin lock init lock 该宏用于动态初始化
  • 英特尔 Inspector 报告我的自旋锁实现中存在数据争用

    我使用 Windows 中的 Interlocked 函数制作了一个非常简单的自旋锁 并在双核 CPU 两个线程递增变量 上进行了测试 该程序似乎工作正常 每次都给出相同的结果 但不使用同步时情况并非如此 但是英特尔并行检查器说有一个竞争条
  • 自旋锁与 std::mutex::try_lock

    使用专门设计的自旋锁有什么好处 例如http anki3d org spinlock 与这样的代码 std mutex m while m try lock do work m unlock 在典型的硬件上 有巨大的好处 你天真的 假自旋锁
  • 损坏的 GLSL 自旋锁/GLSL 锁概要

    我有一个设置 需要锁定 读取一些数据 处理 写入一些数据 然后解锁 为此 我制作了一个锁定纹理作为layout r32ui coherent uniform uimage2D 临界区的数据的声明方式类似 不幸的是 我对自旋锁的所有尝试都无法
  • 自旋锁所需的最小 X86 组件是多少

    在汇编中实现自旋锁 在这里我发布一个我想出的解决方案 这是对的吗 你知道更短的吗 lock mov ecx 0 loop xchg eax ecx cmp ecx 0 je loop release lock dec dword eax e
  • 如何实现自旋锁以避免阻塞

    考虑以下代码 Below block executed by thread t1 synchronized obj obj wait 0 This block executed by thread t2 synchronized obj o
  • 为什么获取语义仅适用于读取,而不适用于写入? LL/SC 如何获取 CAS 锁定而不用临界区进行存储重新排序?

    首先 考虑发布语义 如果数据集受到自旋锁 互斥锁等 的保护 无论使用什么具体实现 现在 假设 0 表示它空闲 1 繁忙 更改数据集后 线程将 0 存储到自旋锁地址 为了强制在将 0 存储到自旋锁地址之前所有先前操作的可见性 存储是使用释放语
  • 为什么持有自旋锁时不允许“睡眠”? [复制]

    这个问题在这里已经有答案了 可能的重复 为什么持有自旋锁时不能睡觉 https stackoverflow com questions 4752031 why cant you sleep while holding spinlock 据我
  • “线程屏障”同步模式上的 C++ 正确原子内存排序

    我需要在预定义数量的工作线程 通过应用程序配置静态已知 和预定义数量的控制平面线程之间正确同步对某些共享资源的访问 控制平面线程接收来自外部的请求 并基于该请求可能修改共享资源 工作线程只是运行一个无限循环 其中共享资源是只读的 为了以线程
  • 微软的OpenMP自旋锁时间如何控制?

    Intel编译器使用的OpenMP支持环境变量KMP BLOCKTIME docs http software intel com sites products documentation doclib iss 2013 compiler
  • 为什么linux在内核代码持有自旋锁后禁用内核抢占?

    我是 Linux 新手 正在阅读 Rubini Corbet 撰写的 Linux 设备驱动程序书籍 我对与以下相关的一项声明感到困惑spinlocks 书中指出 如果非抢占式单处理器系统曾经进入 旋转一把锁 它会永远旋转 没有其他线程能够获
  • 通过内联汇编锁定内存操作

    我对低级的东西很陌生 所以我完全不知道你可能会遇到什么样的问题 我什至不确定我是否正确理解 原子 一词 现在我正在尝试通过扩展程序集围绕内存操作制作简单的原子锁 为什么 为了好奇心 我知道我正在重新发明轮子 并且可能过度简化了整个过程 问题
  • Linux 内核 - 我可以在不同的函数中锁定和解锁 Spinlock 吗?

    我是内核编程和锁编程的新手 在不同的函数中锁定和解锁自旋锁是否安全 我这样做是为了同步代码流 另外 在 schedule 中使用自旋锁 锁定和解锁 是否安全 让调度程序等待获取锁是否安全 提前致谢 代替spinlock 你可以使用semap
  • 忙等待循环中是否需要内存屏障或原子操作?

    考虑以下spin lock 实现 最初来自这个答案 https stackoverflow com a 32658335 3169754 void spin lock volatile bool lock for inserts an ac
  • 自旋锁与忙等待[关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 请解释原因忙碌等待通常不受欢迎 而Spinning通常被认为还可以 据我所知 它们都会无限循环 直到满足某些条件 A 自旋锁通常在资源争用较少时
  • 自动持有自旋锁时返回是否不安全?

    受人尊敬的书说 The flags参数传递给spin unlock irqrestore必须与传递给的变量相同spin lock irqsave 您还必须致电spin lock irqsave and spin unlock irqrest
  • 自旋锁在单处理器单核架构中有用吗?

    我对自旋锁的功能感到困惑 自旋锁用于阻止进程重新调度 然而 在只有一个核心的机器上 使用自旋锁有用吗 防止上下文切换 您的观察结果很好 在单处理器系统上 旋转等待资源是没有意义的 因为您最好尽早切换线程 互斥体和信号量正是这样做的 在多处理
  • Linux 内核:Spinlock SMP:为​​什么 spin_lock_irq SMP 版本中有 preempt_disable()?

    Linux内核中的原始代码是 static inline void raw spin lock irq raw spinlock t lock local irq disable preempt disable spin acquire l
  • 为什么这段代码会死锁?

    我在可加载模块中创建了 2 个 Linux 内核线程 并将它们绑定到在双核 Android 设备上运行的单独 CPU 内核 运行几次后 我注意到设备重新启动并重置了硬件看门狗定时器 我一直在解决这个问题 什么可能导致僵局 基本上 我需要做的

随机推荐

  • 2023华为OD机试真题Java实现【士兵过河/动态规划】

    题目内容 一支N个士兵的军队正在趁夜色逃亡 途中遇到一条湍急的大河 敌军在T的时长后到达河面 没到过对岸的士兵都会被消灭 现在军队只找到了1只小船 这船最多能同时坐上2个士兵 1 当1个士兵划船过河 用时为 a i 0 lt i lt N
  • 记录vue js-table2excel 导出带有多张图片到Excel

    新建js table2excel js文件 eslint disable let idTmr const getExplorer gt let explorer window navigator userAgent ie if explor
  • Springmvc拦截器三个方法的执行时机

    一 拦截器三个方法分别是 1 1 preHandle 预处理回调方法 实现处理器的预处理 如登录检查 第三个参数为响应的处理器 如具体的Controller实现 返回值 true表示继续流程 如调用下一个拦截器或处理器 false表示流程中
  • 微信小程序与应用服务的关系和“代码安全“

    今天给客户回答了下小程序项目的代码安全问题 他担心源代码提交以及发布系统后被第三方知晓源代码 导致代码泄露 虽然作为程序员来说 这个问题不用考虑 但是非技术人员似懂非懂 所以我还是做了一个解释 一般做微信小程序开发 需要知道微信小程序只是纯
  • 记录PaddleOcr的使用2 -- GPU

    项目场景 之前使用了cpu 但是效率感人 所以想尝试一下GPU的版本 安装环境 windows下使用的 别问 问就是没有有GPU的服务器 1 python 3 7 如果是linux建议3 8 2 pip 版本 20 2 2或更高版本 64
  • LALR(1)语法自动分析生成器Mathew

    首次写博客 文采不好大家不要见笑额 简介 Mathew 马修 马修名字源于 魔力女管家 里的星神马修 马修是一个LALR 1 型活动板房式的语法自动分析生成器 马修继承了Lemon 也许大家对LEX和YACC比较熟悉 这两个工具配合使用可以
  • 记录Chrome截屏整个页面的命令

    F12 右键检查 进入开发者工具 调出命令 MAC command shift P Window ctrl shift P 输入命令 capture full size screenshot
  • C/C++如何输入包含空格的字符串

    对于C 字符串的输入我们看一下下面这段代码 string s 定义空字符串 cin gt gt s 输入字符串 cout lt lt s 打印 但我们会发现如果我们输入了还有空格的字符串 s里读入的字符串遇到空格 回车 tab都会结束 比如
  • 目前 AIGC 工具到底能帮我们做什么?

    最近直播超级多 预约保你有收获 今天腾讯发布了混元大模型 大模型赛道越来越内卷了 今天咱们来聊聊目前的 AIGC 工具能帮助我们做什么 AIGC 的本质是由 AI 来生产内容 通过自然语言交互的方式 让 AIGC 工具输出内容 AIGC 工
  • 20-文件下载及读取漏洞

    WEB 漏洞 文件操作之文件下载读取全解 思维导图 1 文件被解析 则是文件解析漏洞 2 显示源代码 则是文件读取漏洞 3 提示文件下载 则是文件下载漏洞 文件下载漏洞 利用条件 1 存在读文件的函数和操作 2 读取文件的路径用户可控且未校
  • Android 保存资源图片到相册最新写法适用于Android10.0及以上

    博主前些天发现了一个巨牛的人工智能学习网站 通俗易懂 风趣幽默 忍不住也分享一下给大家 点击跳转到网站 一 首先在AndroidManifest xml中加入权限
  • APFS 文件系统探究

    本文的创作初衷是因为我发现从底层详解 APFS 的资料很少 所以自己来进行了一些探究和整理 一点说明 如果你在看 APFS 的文档或者其他内容 不要把高层级的分区理解成 Windows 中的分区 因为 APFS 里卷 Volume 才是显示
  • OUT指令时,就进入了I/O端口读写周期

    1 译码电路的输入信号 每当CPU执行IN或者OUT指令时 就进入了I O端口读写周期 此时首先是端口地址有效 然后是I O读写控制信号 IOR和 IOW有效 把对端口地址译码而产生的译码信号同 IOR和 IOW结合起来一同控制对I O端口
  • 聊聊FFT

    关于FFT 全称为快速傅里叶变换 目的是把时域的信号转变为频域的信号 具体的科学解释及计算方程组可以去查百度百科 不过小编不建议这么做 因为查了也看不懂的 先看一张都能看懂的图 这是某种食物的配方表 每种配方包含了多少比例标注的很清楚 对于
  • 计算机网络教程_第二章物理层_整理与复习

    计算机网络教程 第一章 概述 第二章 物理层 第三章 数据链路层 提示 写完文章后 目录可以自动生成 如何生成可参考右边的帮助文档 文章目录 计算机网络教程 1 物理层的作用及主要任务 2 数据传输的方式 并行 串行 异步 同步 P40 3
  • python 设置下载源,全局设置

    推荐使用豆瓣的 个人感觉最好用 当然 你如果喜欢其它的 也可以设置 pip config set golbal index url https pypi douban com simple 设置成功 windows 提示的配置文件在 ini
  • Spyder上使用tensorflow训练完成时出现SystemExit异常

    使用spyder tensorflow实现迁移学习训练inception v3网络 训练完成后提示 SystemExit home zhijuan anaconda3 lib site packages python3 6 site pac
  • 深度学习 图像分割综述

    文章目录 前言 语义分割 实例分割 技术路线 掩膜建议分类法 先检测再分割法 标记像素后聚类法 密集滑动窗口法 参考 前言 图像分割在计算机视觉中是个重要的任务 在地理信息系统 医学影像 自动驾驶 机器人等领域都有着很重要的应用技术支持作用
  • TensorFlow框架做实时人脸识别小项目(二)

    在第一部分中 分析了整个小项目的体系 重点讨论了用于人脸检测对齐的mtcnn网络的实现原理 并利用笔记本电脑自带的摄像头进行了测试 今天在这里要讨论的重点是人脸识别中的核心部分 facenet网络 facenet是Google开源的人脸识别
  • 从CPU cache一致性的角度看Linux spinlock的不可伸缩性(non-scalable)

    凌晨一点半的深圳雨夜 豪雨当夜惊起有人赏 笑叹落花无声空飘零 喜欢这种豪雨 让人兴奋 惊起作文以呜呼之感叹 引用上一篇文章 优化多核CPU的TCP新建连接性能 重排spinlock https blog csdn net dog250 ar