membarrier(2)
Issue a memory barrier across all threads in the calling process, offloading the barrier cost from readers to the writer.
- Since
- Linux 4.3check your kernel →
- glibc
- glibc 2.35; use syscall(2) on older systems
- System
- Linux
- Man page
- membarrier(2)
Synopsis
#include <linux/membarrier.h>
#include <sys/syscall.h>
#include <unistd.h>
int ret = syscall(SYS_membarrier, int cmd, unsigned int flags, int cpu_id);Rod's take
membarrier(2) lets the writer pay the memory-ordering cost so readers need no LOCK-prefix or DMB. Register with MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED at startup, then call MEMBARRIER_CMD_PRIVATE_EXPEDITED after each store. This is the foundation of liburcu's sys_membarrier scheme. The SYNC_CORE variant is the right tool after JIT code patching on ARM64 and RISC-V.
Flags and arguments that matter
| Name | What it does |
|---|---|
| MEMBARRIER_CMD_QUERY | Returns a bitmask of commands supported by this kernel. |
| MEMBARRIER_CMD_GLOBALLinux 4.3 | Wait for every CPU on the system to pass through a memory barrier. Slow (includes all processes); no registration required. |
| MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITEDLinux 4.16 | Register to receive global expedited barriers from other processes. |
| MEMBARRIER_CMD_GLOBAL_EXPEDITEDLinux 4.16 | IPI every CPU running a thread that called MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED. Faster than GLOBAL but requires coordination. |
| MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITEDLinux 4.14 | Register intent to use MEMBARRIER_CMD_PRIVATE_EXPEDITED. Must be called before any reader threads are spawned. |
| MEMBARRIER_CMD_PRIVATE_EXPEDITEDLinux 4.14 | IPI every thread in the calling process. Readers execute a barrier on the IPI return path. Requires prior REGISTER call; returns EPERM otherwise. |
| MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORELinux 4.16 | Like PRIVATE_EXPEDITED plus an icache-serializing instruction on each IPI return. Required after JIT code patching on non-x86 CPUs. |
| MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQLinux 5.10 | Interrupt any rseq critical section running on a remote CPU. cpu_id arg selects the target CPU; pass -1 for all. |
Errors you will actually hit
| errno | Meaning |
|---|---|
| EPERM | PRIVATE_EXPEDITED called without prior REGISTER call. |
| EINVAL | Unknown command or nonzero flags for a command that does not accept flags. |
| ENOSYS | Kernel older than 4.3 or CONFIG_MEMBARRIER not set. |
Example
#include <linux/membarrier.h>
#include <sys/syscall.h>
#include <unistd.h>
/* at startup, before reader threads exist */
syscall(SYS_membarrier, MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED, 0, 0);
/* writer: store, then IPI all threads */
atomic_store_explicit(&shared_ptr, new_val, memory_order_relaxed);
syscall(SYS_membarrier, MEMBARRIER_CMD_PRIVATE_EXPEDITED, 0, 0);
/* reader: no barrier needed */
void *val = atomic_load_explicit(&shared_ptr, memory_order_acquire);Trimmed from the full walkthrough in membarrier(2): flip the memory-ordering tax from readers to writers.
BSD equivalent
No equivalent on FreeBSD. liburcu falls back to sending a signal to each reader thread, whose signal handler executes the barrier. Correct but slower than kernel-assisted IPIs.
The long version, from the console
Sources
Version numbers are read off the cited man pages, not memory. Spot one that's wrong? Tell Rod.
