Sixty-five headers sat side by side in include/, so `#include "irq.h"` said nothing about where irq.h was or what it belonged to, and errno.h, stat.h and fcntl.h at the top shadowed the system's for any host tool built with -I./include. They are in folders now -- arch/ core/ fs/ dev/ gfx/ net/ uapi/ (and ldls/ for the Linux driver layer) -- and every include names the folder, as Linux's do: "core/proc.h". Done mechanically and checked the hard way: the kernel built from this tree disassembles identically to the one built from the tree before it. The full image boots, and userland is the same set of programs. Paths in docs and comments follow; docs/guides/layout.md has a table of what is where, and tools/fix-includes.py rewrites old-style includes from the folders in the tree, for code written before this. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
122 lines
3.9 KiB
C
122 lines
3.9 KiB
C
// shmtest -- prove that shared memory is actually shared, and that a
|
|
// file-backed mmap returns the file.
|
|
//
|
|
// Both of these are things you cannot check by reading a return value. shmget
|
|
// and shmat returned -ENOSYS until now, and mmap of a file "succeeded" while
|
|
// handing back zeroed anonymous pages -- a lie that looks exactly like
|
|
// success. The only convincing test is for one process to write and a
|
|
// different one to read.
|
|
//
|
|
// shmtest run every check
|
|
//
|
|
// Prints one line per check, and a final verdict a script can grep for.
|
|
|
|
#include "core/types.h"
|
|
#include "uapi/user.h"
|
|
#include "uapi/fcntl.h"
|
|
|
|
#define PROT_READ 0x1
|
|
#define PROT_WRITE 0x2
|
|
#define MAP_PRIVATE 0x02
|
|
#define MAP_SHARED 0x01
|
|
#define IPC_PRIVATE 0
|
|
#define IPC_CREAT 01000
|
|
|
|
static int failures;
|
|
|
|
static void
|
|
puts_(const char *s)
|
|
{
|
|
int n = 0;
|
|
while (s[n]) n++;
|
|
write(1, (void*)s, n);
|
|
}
|
|
|
|
static void
|
|
check(int ok, const char *what)
|
|
{
|
|
puts_(ok ? " [ok] " : " FAIL: ");
|
|
puts_(what);
|
|
puts_("\n");
|
|
if (!ok) failures++;
|
|
}
|
|
|
|
int
|
|
main(void)
|
|
{
|
|
// --- shared memory ----------------------------------------------------
|
|
int id = shmget(IPC_PRIVATE, 8192, IPC_CREAT | 0600);
|
|
check(id >= 0, "shmget created a segment");
|
|
if (id < 0) { puts_("shmtest: FAILED\n"); return 1; }
|
|
|
|
char *p = shmat(id, 0, 0);
|
|
check((long)p > 0, "shmat mapped it");
|
|
if ((long)p <= 0) { puts_("shmtest: FAILED\n"); return 1; }
|
|
|
|
// A fresh segment must be zeroed, or it leaks whatever the previous
|
|
// tenant left in those pages to whoever gets them next.
|
|
int zeroed = 1;
|
|
for (int i = 0; i < 8192; i++)
|
|
if (p[i] != 0) { zeroed = 0; break; }
|
|
check(zeroed, "a new segment reads as zeros");
|
|
|
|
p[0] = 'P';
|
|
p[1] = 0;
|
|
|
|
int pid = fork();
|
|
if (pid == 0) {
|
|
// The child inherited the attachment through fork. If the pages had
|
|
// been copied instead of shared, the parent would never see this.
|
|
p[0] = 'C';
|
|
p[100] = 'X';
|
|
exit();
|
|
}
|
|
wait();
|
|
|
|
check(p[0] == 'C', "a write by the child is visible in the parent");
|
|
check(p[100] == 'X', "and so is one further into the segment");
|
|
|
|
// --- file-backed mmap --------------------------------------------------
|
|
// Written and compared through one constant, and never by hand-counted
|
|
// offsets. Those offsets used to spell out SCHUNIX-MMAP-OK a character at a
|
|
// time -- m[3]=='U' and m[14]=='K' -- with the length 15 written out
|
|
// separately. Renaming the marker to SCHOS shortened it to 13 and left both
|
|
// behind: the write ran two bytes off the end of the literal and the
|
|
// compare read past the data, which failed a file-backed mmap that was
|
|
// working correctly.
|
|
static const char MARK[] = "SCHOS-MMAP-OK";
|
|
const int MARKLEN = (int)(sizeof(MARK) - 1);
|
|
|
|
int fd = open("/tmp/shmtest.dat", O_CREATE | O_RDWR);
|
|
check(fd >= 0, "created a file to map");
|
|
if (fd >= 0) {
|
|
write(fd, MARK, MARKLEN);
|
|
close(fd);
|
|
}
|
|
|
|
fd = open("/tmp/shmtest.dat", O_RDONLY);
|
|
if (fd >= 0) {
|
|
char *m = mmap(0, 4096, PROT_READ, MAP_PRIVATE, fd, 0);
|
|
int ok = ((long)m > 0);
|
|
for (int i = 0; ok && i < MARKLEN; i++)
|
|
ok = (m[i] == MARK[i]);
|
|
check(ok, "mmap of a file returns the file, not zeros");
|
|
close(fd);
|
|
} else {
|
|
check(0, "mmap of a file returns the file, not zeros");
|
|
}
|
|
|
|
// MAP_SHARED + PROT_WRITE on a file cannot be honoured without a page
|
|
// cache, and is refused rather than quietly downgraded to a private copy.
|
|
fd = open("/tmp/shmtest.dat", O_RDWR);
|
|
if (fd >= 0) {
|
|
void *m = mmap(0, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
|
check((long)m < 0, "writable MAP_SHARED on a file is refused, not faked");
|
|
close(fd);
|
|
}
|
|
|
|
check(shmdt(p) == 0, "shmdt released the attachment");
|
|
|
|
puts_(failures ? "shmtest: FAILED\n" : "shmtest: all checks passed\n");
|
|
return failures ? 1 : 0;
|
|
}
|