Open3

mmap()

zulinx86zulinx86

mmap()

https://man7.org/linux/man-pages/man2/mmap.2.html

ファイルやデバイスをプロセスの仮想メモリ空間にマップ・アンマップするためのシステムコール

       #include <sys/mman.h>

       void *mmap(void addr[.length], size_t length, int prot, int flags,
                  int fd, off_t offset);
       int munmap(void addr[.length], size_t length);
  • prot: 対象のマッピングに対するメモリ保護の設定
    • PROT_EXEC: 実行可能
    • PROT_READ: 読み込み可能
    • PROT_WRITE: 書き込み可能
    • PROT_NONE: アクセス不可
  • flags: マッピングへの変更が他のプロセスにも見えるかどうか + 元となるファイルにも適用されるか
    • MAP_SHARED: 他のプロセスにも変更が見え、元のファイルにも変更が適用される
    • MAP_PRIVATE: 他のプロセスには変更が見えず、元のファイルにも変更は適用されない (Copy-on-Write: 書き込み時にコピーをする)
zulinx86zulinx86

prot を変えて実験

PROT_NONE

  • 特定のメモリアドレス領域を予約しておくのに利用することができる。
  • 後に mprotect() によって、メモリ保護の設定を変更することが可能である。

実行結果

$ ./a.out
Page size: 4096
Reserved memory region with PROT_NONE: 0x7f9aa5f66000
Made the first page readable and writable.
Wrote data to first page of reserved memory.
Read data from the first page: 0 1 4 9 16 25 36 49 64 81

コード

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>

int main() {
	// Get the system's page size.
	long page_size = sysconf(_SC_PAGESIZE);
	printf("Page size: %ld\n", page_size);

	// Reserve a memory region of 2 pages with PROT_NONE.
	void *reserved_memory = mmap(NULL, 2 * page_size, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
	if (reserved_memory == MAP_FAILED) {
		perror("mmap");
		exit(EXIT_FAILURE);
	}
	printf("Reserved memory region with PROT_NONE: %p\n", reserved_memory);

	// Make the first page of the reserved memory readable and writable.
	if (mprotect(reserved_memory, page_size, PROT_READ | PROT_WRITE) == -1) {
		perror("mprotect");
		munmap(reserved_memory, 2 * page_size);
		exit(EXIT_FAILURE);
	}
	printf("Made the first page readable and writable.\n");

	// Write some data to the first page of the reserved memory.
	int *numbers = (int *)reserved_memory;
	for (int i = 0; i < 10; i++) {
		numbers[i] = i * i;
	}
	printf("Wrote data to first page of reserved memory.\n");

	// Read the written data and print it to stdout.
	printf("Read data from the first page:");
	for (int i = 0; i < 10; i++) {
		printf(" %d", numbers[i]);
	}
	printf("\n");

	// Clean up.
	munmap(reserved_memory, 2 * page_size);
	return 0;
}

PROT_READ

実行結果

$ echo hello > sample.txt
$ ./a.out
sample.txt is mapped in read-only mode successfully.
File contents:
hello
Attempting to write to the read-only mapped area...
Segmentation fault

コード

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>

int main() {
	// Open a file in read-only mode
	const char *filepath = "sample.txt";
	int fd = open(filepath, O_RDONLY);
	if (fd == -1) {
		perror("open");
		exit(EXIT_FAILURE);
	}

	// Obtain the file size.
	off_t size = lseek(fd, 0, SEEK_END);
	if (size == -1) {
		perror("lseek");
		close(fd);
		exit(EXIT_FAILURE);
	}

	// Map the file into memory with read-only access.
	void *mapped = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
	if (mapped == MAP_FAILED) {
		perror("mmap");
		close(fd);
		exit(EXIT_FAILURE);
	}
	printf("sample.txt is mapped in read-only mode successfully.\n");

	// Read from the mapped area.
	printf("File contents:\n");
	write(STDOUT_FILENO, mapped, size);

	// Attemp to write to the mapped area.
	printf("Attempting to write to the read-only mapped area...\n");
	memcpy(mapped, "Modified", 9); // This will cause a segmentation fault.
	printf("This part should not be reached!!");

	// Clean up
	munmap(mapped, size);
	close(fd);
	return 0;
}

PROT_READ | PROT_WRITE

実行結果

$ echo hello > sample.txt
$ ./a.out
$ cat sample.txt
Hello, mmap!

コード

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>

int main() {
	const char *msg = "Hello, mmap!";
	size_t msg_len = strlen(msg);

	// Open a file in read-write mode.
	const char *filepath = "sample.txt";
	int fd = open(filepath, O_RDWR);
	if (fd == -1) {
		perror("open");
		exit(EXIT_FAILURE);
	}

	// Obtain the file size.
	off_t size = lseek(fd, 0, SEEK_END);
	if (size == -1) {
		perror("lseek");
		close(fd);
		exit(EXIT_FAILURE);
	}

	// Resize the file size if necessary.
	if (msg_len > size) {
		if (ftruncate(fd, msg_len) == -1) {
			perror("ftruncate");
			close(fd);
			exit(EXIT_FAILURE);
		}
		size = msg_len;
	}

	// Map the file into memory in read-write mode.
	void *mapped = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
	if (mapped == MAP_FAILED) {
		perror("mmap");
		close(fd);
		exit(EXIT_FAILURE);
	}

	// Write the message to the mapped memory.
	memcpy(mapped, msg, msg_len);

	// Clean up.
	munmap(mapped, size);
	close(fd);
	return 0;
}
zulinx86zulinx86

flags を変えて実験

MAP_SHARED vs. MAP_PRIVATE

先ほどの PROT_READ | PROT_WRITE のコードで、MAP_SHARED を使っていたのを MAP_PRIVATE に変えて比較する。

# MAP_PRIVATE の場合
$ cat sample.txt 
hello
$ ./a.out
$ cat sample.txt
hello

# MAP_SHARED の場合
$ cat sample.txt 
hello
$ ./a.out
$ cat sample.txt
Hello, mmap!