MEMBARRIER - Linux手册页

时间:2019-08-20 17:58:59  来源:igfitidea点击:

Linux程序员手册 第2部分
更新日期: 2020-06-09

名称

membarrier-在一组线程上发出内存障碍

语法

#include <linux/membarrier.h>

int membarrier(int cmd, int flags);

说明

membarrier()系统调用有助于减少订购多核系统上的内存访问所需的内存屏障指令的开销。但是,此系统调用比内存屏障重,因此有效地使用它并不像用此系统调用替换内存屏障那样简单,而是需要了解以下详细信息。

需要考虑到内存屏障始终需要与其对应的内存屏障匹配,或者体系结构的内存模型不需要匹配的屏障,因此需要使用内存屏障。

在某些情况下,匹配障碍的一侧(我们将其称为"快速侧")执行得比另一侧(我们将其称为"慢侧")频繁得多。这是使用membarrier()的主要目标。关键思想是用简单的编译器屏障来代替这些匹配的屏障,例如快速侧存储器屏障:

asm volatile ("" : : : "memory")

并通过调用membarrier()来替换慢侧内存屏障。

这将增加慢端的开销,并消除快端的开销,因此,只要慢端的频率不足以使membarrier()调用的开销不会超过性能提高所带来的影响,就可以提高整体性能。快速的一面。

cmd参数是以下之一:

MEMBARRIER_CMD_QUERY(since Linux 4.3)
查询支持的命令集。调用的返回值是受支持命令的位掩码。该值掩码本身不包括值为0的MEMBARRIER_CMD_QUERY。始终支持此命令(在提供了membarrier()的内核上)。
MEMBARRIER_CMD_GLOBAL(since Linux 4.16)
确保来自系统上所有进程的所有线程都经过一种状态,在该状态下,对用户空间地址的所有内存访问均与membarrier()系统调用的进入和返回之间的程序顺序相匹配。该命令将系统上的所有线程作为目标。
MEMBARRIER_CMD_GLOBAL_EXPEDITED(since Linux 4.16)
在先前向MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED注册的所有进程的所有运行线程上执行内存屏障。
从系统调用返回后,调用线程可以确保所有正在运行的线程都已通过一种状态,在该状态下,对用户空间地址的所有内存访问都与从系统调用进入和返回之间的程序顺序相匹配(非运行线程是事实)。仅对先前在MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED中注册的进程的线程提供此保证。
鉴于注册是关于接受障碍的意图,因此从未使用MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED的进程中调用MEMBARRIER_CMD_GLOBAL_EXPEDITED是有效的。
"快速"命令比非快速命令完成得更快。它们从不阻塞,但是有造成额外开销的不利方面。
MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED(since Linux 4.16)
注册流程的意图,以接收MEMBARRIER_CMD_GLOBAL_EXPEDITED内存屏障。
MEMBARRIER_CMD_PRIVATE_EXPEDITED(since Linux 4.14)
在与调用线程属于同一进程的每个运行线程上执行内存屏障。
从系统调用返回后,调用线程可以确保其所有正在运行的线程同级都通过以下状态:对用户空间地址的所有内存访问均与进入和返回系统调用之间的程序顺序相匹配(非运行线程)实际上处于这种状态)。仅在与调用线程处于同一进程的线程中提供此保证。
"快速"命令比非快速命令完成得更快。它们从不阻塞,但是有造成额外开销的不利方面。
进程在使用它之前必须先注册使用私有加速命令的意图。
MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED(since Linux 4.14)
注册该流程的意图以使用MEMBARRIER_CMD_PRIVATE_EXPEDITED。
MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE(since Linux 4.16)
除了提供MEMBARRIER_CMD_PRIVATE_EXPEDITED中描述的内存顺序保证之外,从系统调用返回时,调用线程还保证其所有运行线程同级都已执行了核心序列化指令。仅在与调用线程处于同一进程的线程中提供此保证。
"加急"命令的完成速度比未加急的命令快,它们不会阻塞,但不利之处在于会导致额外的开销。
进程在使用它之前必须先注册其意图,以使用私有的快速同步核心命令。
MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE(since Linux 4.16)
注册该流程的意图以使用MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE。
MEMBARRIER_CMD_SHARED(since Linux 4.3)
这是MEMBARRIER_CMD_GLOBAL的别名,为了别名向后兼容而存在。

flags参数当前未使用,必须指定为0。

保证从每个目标线程按程序顺序执行的所有内存访问都相对于membarrier()进行排序。

如果我们使用语义barrier()表示一个编译器屏障,强制以程序顺序跨该屏障执行内存访问,而smp_mb()表示显式内存屏障,则强制跨该屏障进行完整内存排序,则我们有以下排序表barrier()membarrier()和smp_mb()的每对配对。配对顺序详细说明为(O:有序,X:无序):

barrier()smp_mb()membarrier()
barrier()X X O
smp_mb()X X O
membarrier()O O O

返回值

如果成功,则MEMBARRIER_CMD_QUERY操作返回支持的命令位掩码和MEMBARRIER_CMD_GLOBALMEMBARRIER_CMD_GLOBAL_EXPEDITEDMEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITEDMEMBARRIER_CMD_PRIVATE_EXPEDITEDMEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED,MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE和MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE操作返回零。如果出错,则返回-1,并正确设置errno。

对于给定的命令(标志设置为0),保证该系统调用始终返回相同的值,直到重新引导为止。具有相同参数的进一步调用将导致相同的结果。因此,在标志设置为0的情况下,仅在首次调用membarrier()时才需要错误处理。

错误说明

EINVAL
cmd无效,或者标志非零,或者由于设置了nohz_full CPU参数而禁用了MEMBARRIER_CMD_GLOBAL命令,或者体系结构未实现MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE和MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE命令。
ENOSYS
该内核未实现membarrier()系统调用。
EPERM
在使用私有快速命令之前,当前进程尚未注册。

版本

在Linux 4.3中添加了membarrier()系统调用。

遵循规范

membarrier()是特定于Linux的。

备注

存储器屏障指令是具有弱顺序存储器模型的体系结构指令集的一部分。相对于其他内核上的匹配屏障,它在屏障之前和屏障之后对内存访问进行排序。例如,对于由商店围栏定购的商店,货物围栏可以在该围栏之前和之后定购货物。

程序顺序是指在程序汇编代码中对指令进行排序的顺序。

其中membarrier()有用的示例包括Read-Copy-Update库和垃圾回收器的实现。

示例

假设一个多线程应用程序经常执行" fast_path()",而很少执行" slow_path()",则可以使用membarrier()转换以下代码(x86):

#include <stdlib.h>

static volatile int a, b;

static void
fast_path(int *read_b)
{
    a = 1;
    asm volatile ("mfence" : : : "memory");
    *read_b = b;
}

static void
slow_path(int *read_a)
{
    b = 1;
    asm volatile ("mfence" : : : "memory");
    *read_a = a;
}

int
main(int argc, char **argv)
{
    int read_a, read_b;

    /*
     * Real applications would call fast_path() and slow_path()
     * from different threads. Call those from main() to keep
     * this example short.
     */

    slow_path(&read_a);
    fast_path(&read_b);

    /*
     * read_b == 0 implies read_a == 1 and
     * read_a == 0 implies read_b == 1.
     */

    if (read_b == 0 && read_a == 0)
        abort();

    exit(EXIT_SUCCESS);
}

上面转换为使用membarrier()的代码变为:

#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/membarrier.h>

static volatile int a, b;

static int
membarrier(int cmd, int flags)
{
    return syscall(__NR_membarrier, cmd, flags);
}

static int
init_membarrier(void)
{
    int ret;

    /* Check that membarrier() is supported. */

    ret = membarrier(MEMBARRIER_CMD_QUERY, 0);
    if (ret < 0) {
        perror("membarrier");
        return -1;
    }

    if (!(ret & MEMBARRIER_CMD_GLOBAL)) {
        fprintf(stderr,
            "membarrier does not support MEMBARRIER_CMD_GLOBAL\n");
        return -1;
    }

    return 0;
}

static void
fast_path(int *read_b)
{
    a = 1;
    asm volatile ("" : : : "memory");
    *read_b = b;
}

static void
slow_path(int *read_a)
{
    b = 1;
    membarrier(MEMBARRIER_CMD_GLOBAL, 0);
    *read_a = a;
}

int
main(int argc, char **argv)
{
    int read_a, read_b;

    if (init_membarrier())
        exit(EXIT_FAILURE);

    /*
     * Real applications would call fast_path() and slow_path()
     * from different threads. Call those from main() to keep
     * this example short.
     */

    slow_path(&read_a);
    fast_path(&read_b);

    /*
     * read_b == 0 implies read_a == 1 and
     * read_a == 0 implies read_b == 1.
     */

    if (read_b == 0 && read_a == 0)
        abort();

    exit(EXIT_SUCCESS);
}

出版信息

这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/