membarrier(2)
πΈ NAME πΈ
membarrier β issue memory barriers on a set of threads. Yes bestie, this is the membarrier(2) page from the System Calls Manual. π
π LIBRARY π
Standard C library (libc, -lc).
β¨ SYNOPSIS β¨
#include <linux/membarrier.h> /* Definition of MEMBARRIER_* constants */
#include <sys/syscall.h> /* Definition of SYS_* constants */
#include <unistd.h>
int syscall(SYS_membarrier, int cmd, unsigned int flags, int cpu_id);
Note: glibc provides no wrapper formembarrier(), so you literally have to usesyscall(2). No cap. π«Ά
π DESCRIPTION π
Okay so bestie, membarrier() is here to help reduce the overhead of the memory barrier instructions you need to order memory accesses on multi-core systems. But β and this is important β this syscall is heavier than a regular memory barrier. So you can't just swap your memory barriers for this syscall and call it a day. You need to understand the deets below. π
When you use memory barriers, keep in mind that a memory barrier always needs to either be matched with its memory barrier counterparts, or the architecture's memory model doesn't require those matching barriers. iykyk.
Sometimes one side of those matching barriers runs way more often than the other. We're gonna call the busy one the "fast side" and the chill one the "slow side". That setup is literally the main character for membarrier(). The whole idea? For these matching barriers, replace the fast-side memory barriers with simple compiler barriers, like this:
asm volatile ("" : : : "memory")
and replace the slow-side memory barriers by calls to membarrier().
So you're adding overhead to the slow side and removing overhead from the fast side. That's a net performance win as long as the slow side is infrequent enough that the membarrier() call overhead doesn't outweigh the fast-side glow-up.
The cmd argument is one of the following:
MEMBARRIER_CMD_QUERY(since Linux 4.3) β This is your vibe check, bestie. It asks: which commands does this kernel support? The return value is a bit mask of the supported commands.MEMBARRIER_CMD_QUERYhas the value0, and it is not itself included in that bit mask. This command is always supported on kernels wheremembarrier()exists. β¨MEMBARRIER_CMD_GLOBAL(since Linux 4.16) β Make every thread in every process on the system pass through a state where all memory accesses to user-space addresses match program order between entry to and return from themembarrier()system call. All threads on the system are targeted by this command. πMEMBARRIER_CMD_GLOBAL_EXPEDITED(since Linux 4.16) β Run a memory barrier on all running threads of all processes that previously registered withMEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED.
When the syscall returns, the calling thread gets a guarantee that all running threads have passed through a state where all memory accesses to user-space addresses match program order between entry to and return from the syscall (non-running threads are de facto in such a state). This guarantee is provided only for the threads of processes that previously registered with MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED.
Since registration is about the intent to receive the barriers, it is valid to invoke MEMBARRIER_CMD_GLOBAL_EXPEDITED from a process that has not employed MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED.
The "expedited" commands finish faster than the non-expedited ones; they never block, but the downside is extra overhead. β‘
MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED(since Linux 4.16) β Register the process's intent to receiveMEMBARRIER_CMD_GLOBAL_EXPEDITEDmemory barriers.MEMBARRIER_CMD_PRIVATE_EXPEDITED(since Linux 4.14) β Run a memory barrier on each running thread that belongs to the same process as the calling thread.
When the syscall returns, the calling thread gets a guarantee that all its running thread siblings have passed through a state where all memory accesses to user-space addresses match program order between entry to and return from the syscall (non-running threads are de facto in such a state). This guarantee is provided only for threads in the same process as the calling thread.
The "expedited" commands finish faster than the non-expedited ones; they never block, but the downside is extra overhead.
A process must register its intent to use the private expedited command before using it.
MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED(since Linux 4.14) β Register the process's intent to useMEMBARRIER_CMD_PRIVATE_EXPEDITED.MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE(since Linux 4.16) β On top of the memory ordering guarantees described inMEMBARRIER_CMD_PRIVATE_EXPEDITED, when the syscall returns, the calling thread gets a guarantee that all its running thread siblings have executed a core serializing instruction. This guarantee is provided only for threads in the same process as the calling thread.
The "expedited" commands finish faster than the non-expedited ones, they never block, but the downside is extra overhead.
A process must register its intent to use the private expedited sync core command before using it.
MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE(since Linux 4.16) β Register the process's intent to useMEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE.MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ(since Linux 5.10) β Make the caller thread, upon return from the syscall, sure that all its running thread siblings have any currently running rseq critical sections restarted if theflagsparameter is0; if theflagsparameter isMEMBARRIER_CMD_FLAG_CPU, then this operation is performed only on the CPU indicated bycpu_id. This guarantee is provided only for threads in the same process as the calling thread.
RSEQ membarrier is only available in the "private expedited" form.
A process must register its intent to use the private expedited rseq command before using it.
MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ(since Linux 5.10) β Register the process's intent to useMEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ.MEMBARRIER_CMD_SHARED(since Linux 4.3) β This is an alias forMEMBARRIER_CMD_GLOBALthat exists for header backward compatibility.
The flags argument must be specified as 0 unless the command is MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ, in which case flags can be either 0 or MEMBARRIER_CMD_FLAG_CPU.
The cpu_id argument is ignored unless flags is MEMBARRIER_CMD_FLAG_CPU, in which case it must specify the CPU targeted by this membarrier command.
All memory accesses performed in program order from each targeted thread are guaranteed to be ordered with respect to membarrier().
If we use the semantic barrier() to represent a compiler barrier forcing memory accesses to be performed in program order across the barrier, and smp_mb() to represent explicit memory barriers forcing full memory ordering across the barrier, we have the following ordering table for each pairing of barrier(), membarrier(), and smp_mb(). The pair ordering is detailed as (O: ordered, X: not ordered):
barrier() smp_mb() membarrier()
barrier() X X O
smp_mb() X O O
membarrier() O O O
π RETURN VALUE π
On success, MEMBARRIER_CMD_QUERY hands you a bit mask of the supported commands. The MEMBARRIER_CMD_GLOBAL, MEMBARRIER_CMD_GLOBAL_EXPEDITED, MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED, MEMBARRIER_CMD_PRIVATE_EXPEDITED, MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED, MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE, and MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE operations return zero. On error, you get -1, and errno is set to tell you what went wrong.
For a given command, with flags set to 0, this syscall is guaranteed to always return the same value until reboot. Further calls with the same arguments will lead to the same result. So with flags set to 0, you only need error handling on the first call to membarrier(). Periodt.
π ERRORS π
EINVALβcmdis invalid, orflagsis nonzero, orMEMBARRIER_CMD_GLOBALis disabled because thenohz_fullCPU parameter has been set, or the architecture doesn't implementMEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_COREandMEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE. πENOSYSβ This kernel doesn't implement themembarrier()system call.EPERMβ The current process wasn't registered before using private expedited commands.
π STANDARDS π
Linux.
π¦ HISTORY π¦
Linux 4.3.
Before Linux 5.10, the prototype was:
int membarrier(int cmd, int flags);
π NOTES π
A memory barrier instruction is part of the instruction set for architectures with weakly ordered memory models. It orders memory accesses before the barrier and after the barrier with respect to matching barriers on other cores. For example, a load fence can order loads before and after that fence with respect to stores ordered by store fences.
Program order is the order in which instructions are ordered in the program assembly code.
Some places where membarrier() can be useful: implementations of Read-Copy-Update libraries and garbage collectors. π
β¨ EXAMPLES β¨
okay bestie, picture this π β you've got a multithreaded application where fast_path() runs a million times a second and slow_path() runs like... rarely. The following code (x86) can be transformed using membarrier():
#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(void)
{
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);
}
and now the glow-up β¨ β the same code transformed to use membarrier() becomes:
#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, unsigned int flags, int cpu_id)
{
return syscall(__NR_membarrier, cmd, flags, cpu_id);
}
static int
init_membarrier(void)
{
int ret;
/* Check that membarrier() is supported. */
ret = membarrier(MEMBARRIER_CMD_QUERY, 0, 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, 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);
}
π and that's the whole page, bestie! no cap, you just read all of membarrier(2).
this one's from the Linux man-pages 6.20 collection, dated 2025-09-21 π go forth and barrier your memories, love u, bye!! π¦β¨π
π¦ where this page comes from
- arch rolling: man-pages 6.20-1 Β· BSD-2-Clause AND BSD-3-Clause AND BSD-4.3TAHOE AND BSD-4-Clause-UC AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-3.0-linking-exception AND LGPL-3.0-or-later AND LicenseRef-Public-Domain AND LicenseRef-UltraPermissive AND Linux-man-pages-1-para AND Linux-man-pages-copyleft AND Linux-man-pages-copyleft-2-para AND Linux-man-pages-copyleft-var AND MIT AND Spencer-94
- nixpkgs unstable: man-pages 6.20
- π the page's license: Linux-man-pages-copyleft
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms