mmap初步学习

mmap学习

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/kdev_t.h>
#include <linux/mm.h>
#include <linux/gfp.h>
#include <linux/printk.h>

#define DEVICE_CNT	1
#define DEVICE_NAME "demo_dev "

struct chr_dev{
	dev_t devid;
	struct cdev cdev;
	struct class *class;
	struct device *device;
	int major;
	int minor;
};

struct chr_dev demo_dev;

unsigned long virtual_addr_start;
unsigned long phy_addr_start;
struct page *page;

static int demo_open(struct inode *inode, struct file *file)
{
	unsigned long pfn;

	virtual_addr_start = 0;
	phy_addr_start = 0;
	page = NULL;

	printk(DEVICE_NAME "%s, ===>\n", __func__);
	virtual_addr_start = __get_free_page(GFP_KERNEL | __GFP_ZERO);
	if (!virtual_addr_start) {
		printk(DEVICE_NAME "%s, memory allocation failed\n", __func__);
		virtual_addr_start = 0;
		return -ENOMEM;
	}

	printk(DEVICE_NAME "%s, virtual_addr_start 0x%lx\n", __func__, virtual_addr_start);

	page = virt_to_page((void *)virtual_addr_start); // 通过该接口直接获取 page
	printk(DEVICE_NAME "%s, page %p\n", __func__, page);

	// 虚拟地址和物理地址的映射
	phy_addr_start = virt_to_phys((volatile void *)virtual_addr_start);
	pfn = phy_addr_start >> PAGE_SHIFT;
	printk(DEVICE_NAME "%s, phy_addr_start 0x%lx\n", __func__, phy_addr_start);
	printk(DEVICE_NAME "%s, pfn of phy addr 0x%lx\n", __func__, pfn);

	if (IS_ERR_OR_NULL(page)) {
		printk(DEVICE_NAME "%s, page is invalid\n", __func__);
		return -EFAULT;
	}

	memset((void *)virtual_addr_start, 0, PAGE_SIZE);

	SetPageReserved(page);

	printk(DEVICE_NAME "%s, <===\n", __func__);
    return 0;
}

int demo_release(struct inode *inode, struct file *file)
{
	char usr_data[20] = {0};
	printk(DEVICE_NAME "%s, ===>\n", __func__);
	memcpy((void *)&usr_data, (const void *)virtual_addr_start, 20);
	printk(DEVICE_NAME "%s, user data %s\n", __func__, usr_data);

	ClearPageReserved(page);

	if (virtual_addr_start) {
		free_page(virtual_addr_start);
		virtual_addr_start= 0;
		printk(DEVICE_NAME "%s, memory free success\n", __func__);
	} else {
		printk(DEVICE_NAME "%s, memory free failed\n", __func__);
	}

	virtual_addr_start = 0;
	phy_addr_start = 0;
	page = NULL;
	
	printk(DEVICE_NAME "%s, <===\n", __func__);
    return 0;
}

int demo_mmap(struct file *file, struct vm_area_struct *vma)
{
	unsigned long vm_end, vm_start, vm_len;
	int ret = 0;

	printk(DEVICE_NAME "%s, ===>\n", __func__);

	if (IS_ERR_OR_NULL(page)) {
		printk(DEVICE_NAME "%s, page is invalid\n", __func__);
		return -EFAULT;
	}

	vm_end = vma->vm_end;
	vm_start = vma->vm_start;
	vm_len = vm_end - vm_start;
	printk(DEVICE_NAME "%s, vma size %lu\n", __func__, vm_len);
	// 这里的 vma 代表的是 用户空间的地址
	// 而 virtual_addr_start 和 phy_addr_start 都是内核空间的地址
	// 因为这里搞混了,耽搁了一下午
	ret = remap_pfn_range(vma, 
						  vma->vm_start, 
						  phy_addr_start>>PAGE_SHIFT, 
						  vm_len, 
						  vma->vm_page_prot);
	if (ret == 0) {
		printk(DEVICE_NAME "%s, remap success\n", __func__);
	} else {
		printk(DEVICE_NAME "%s, remap failed\n", __func__);
	}

	printk(DEVICE_NAME "%s, <===\n", __func__);
    return 0;
}

static struct file_operations demo_fops = {
    .owner = THIS_MODULE,
    .open = demo_open,
	.mmap = demo_mmap,
    .release = demo_release,
};

static int demo_init(void) {
	int result = 0;

	printk(DEVICE_NAME "%s ===>\n", __func__);

	if (demo_dev.major) {
		demo_dev.devid = MKDEV(demo_dev.major, 0);
		result = register_chrdev_region(demo_dev.devid, DEVICE_CNT, DEVICE_NAME);
		if(result < 0){ 
			goto out_err_1;
		}
	} else {
		result = alloc_chrdev_region(&demo_dev.devid, 0, DEVICE_CNT, DEVICE_NAME);
		if(result < 0){
			goto out_err_1;
		}
		demo_dev.major = MAJOR(demo_dev.devid);
		demo_dev.minor = MINOR(demo_dev.devid);
	}
	printk(DEVICE_NAME "%s, major=%d, minor=%d\r\n", __func__, demo_dev.major, demo_dev.minor);	

	demo_dev.cdev.owner = THIS_MODULE;
	cdev_init(&demo_dev.cdev, &demo_fops);
	cdev_add(&demo_dev.cdev, demo_dev.devid, DEVICE_CNT);

	demo_dev.class = class_create(THIS_MODULE, DEVICE_NAME);
	if (IS_ERR(demo_dev.class)) {
		printk(KERN_ERR "%s, class_create failed\n", __func__);
		result = PTR_ERR(demo_dev.class);
		goto out_err_2;
	}

	demo_dev.device = device_create(demo_dev.class, NULL, demo_dev.devid, NULL, DEVICE_NAME);
	if (IS_ERR(demo_dev.device)) {
		printk(KERN_ERR "%s, device_create failed\n", __func__);
		result = PTR_ERR(demo_dev.device);
		goto out_err_3;
	}

	printk(DEVICE_NAME "%s PAGE_SHIFT 0x%lx\n", __func__, PAGE_SHIFT);
	printk(DEVICE_NAME "%s PAGE_OFFSET 0x%lx\n", __func__, PAGE_OFFSET);

	printk(DEVICE_NAME "%s <===\n", __func__);
	return result; 

out_err_3:
	device_destroy(demo_dev.class, demo_dev.devid);
out_err_2:
	class_destroy(demo_dev.class);
	unregister_chrdev_region(demo_dev.devid, DEVICE_CNT);
	cdev_del(&demo_dev.cdev);
out_err_1:
	return 	result; 
}

static void demo_exit(void) {
	printk(DEVICE_NAME "%s ===>\n", __func__);
	device_destroy(demo_dev.class, demo_dev.devid);
	class_destroy(demo_dev.class);
	unregister_chrdev_region(demo_dev.devid, DEVICE_CNT);
	cdev_del(&demo_dev.cdev);
	printk(DEVICE_NAME "%s <===\n", __func__);
	return;
}

module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");

// %p 用于打印 void *
// %lx 用于打印unsigned long

//==============================================================

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

int main() {
	char *filename = "/dev/demo_dev";
	char *bufp;
	size_t length = 4096;
	int fd = open(filename,O_RDWR);
	if(fd<0){
		perror("open fail \n");
		return -1;
	}
	printf("open file %s success!\n", filename);

	sleep(3);

    // void* mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
	bufp = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
	if (bufp == MAP_FAILED) {
		printf("mmap kernel file page failed!\n");
	} else {
		printf("mmap kernel file page success!\n");
	}

	sleep(2);

	memcpy(bufp, "hello world", 12);

	munmap(bufp, length);

	sleep(2);
	close(fd);
	return 0;
}

//==============================================================

[ 3047.622950] demo_dev demo_init ===>
[ 3047.623268] demo_dev demo_init, major=244, minor=0
[ 3047.623476] demo_dev demo_init <===

[ 3051.910723] demo_dev demo_open, ===>
[ 3051.910733] demo_dev demo_open, virtual_addr_start 0xffff90a2c26cf000
[ 3051.910737] demo_dev demo_open, page 00000000ab9feae0
[ 3051.910739] demo_dev demo_open, phy_addr_start 0x26cf000
[ 3051.910741] demo_dev demo_open, pfn of phy addr 0x26cf
[ 3051.910743] demo_dev demo_open, <===

[ 3054.912966] demo_dev demo_mmap, ===>
[ 3054.912975] demo_dev demo_mmap, vma size 4096
[ 3054.912989] demo_dev demo_mmap, remap success
[ 3054.912991] demo_dev demo_mmap, <===

[ 3058.914745] demo_dev demo_release, ===>
[ 3058.914751] demo_dev demo_release, user data hello world
[ 3058.914757] demo_dev demo_release, memory free success
[ 3058.914759] demo_dev demo_release, <===

[ 3179.697944] demo_devdemo_exit ===>
[ 3179.703738] demo_devdemo_exit <===

//=========================================================
【内核虚拟地址和内核物理地址的转换】
phy_addr_start >> PAGE_SHIFT 是 0x26cf
打印 PAGE_SHIFT 和 PAGE_OFFSET 如下:

[ 5365.521986] demo_dev demo_init PAGE_SHIFT 0xc
[ 5365.521992] demo_dev demo_init PAGE_OFFSET 0xffff90a2c0000000

// 内核虚拟地址转内核物理地址

#define __pa(x)			((unsigned long) (x) - PAGE_OFFSET)

// 内核物理地址转内核虚拟地址

#define __va(x)			((void *)((unsigned long) (x) + PAGE_OFFSET))

//内核虚拟地址转page

#define virt_to_page(kaddr)	pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)
// 这里的 __pa(kaddr) 是将 内核虚拟地址转换为 内核物理地址
// __pa(kaddr) >> PAGE_SHIFT 是通过 内核物理地址 计算 内核页帧号
// pfn_to_page 是通过 内核页帧号 得到 page*

//=========================================================
【获取一个物理页】

#define __get_free_page(gfp_mask) \
		__get_free_pages((gfp_mask), 0)
/*
 * Common helper functions. Never use with __GFP_HIGHMEM because the returned
 * address cannot represent highmem pages. Use alloc_pages and then kmap if
 * you need to access high mem.
 */
unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order)
{
	struct page *page;

	page = alloc_pages(gfp_mask & ~__GFP_HIGHMEM, order);
	if (!page)
		return 0;

	// void *转换为 unsigned long
	// 虚拟地址接口,位于mm.h,这里返回的是 page->virtual
	return (unsigned long) page_address(page);
}
EXPORT_SYMBOL(__get_free_pages);

//=========================================================
【获取一个物理零页】

unsigned long get_zeroed_page(gfp_t gfp_mask)
{
	return __get_free_pages(gfp_mask | __GFP_ZERO, 0);
}
EXPORT_SYMBOL(get_zeroed_page);

//=========================================================
【释放page】

#define __free_page(page) __free_pages((page), 0)
/**
 * __free_pages - Free pages allocated with alloc_pages().
 * @page: The page pointer returned from alloc_pages().
 * @order: The order of the allocation.
 *
 * This function can free multi-page allocations that are not compound
 * pages.  It does not check that the @order passed in matches that of
 * the allocation, so it is easy to leak memory.  Freeing more memory
 * than was allocated will probably emit a warning.
 *
 * If the last reference to this page is speculative, it will be released
 * by put_page() which only frees the first page of a non-compound
 * allocation.  To prevent the remaining pages from being leaked, we free
 * the subsequent pages here.  If you want to use the page's reference
 * count to decide when to free the allocation, you should allocate a
 * compound page, and use put_page() instead of __free_pages().
 *
 * Context: May be called in interrupt context or while holding a normal
 * spinlock, but not in NMI context or while holding a raw spinlock.
 */
void __free_pages(struct page *page, unsigned int order)
{
	if (put_page_testzero(page))
		free_the_page(page, order);
	else if (!PageHead(page))
		while (order-- > 0)
			free_the_page(page + (1 << order), order);
}
EXPORT_SYMBOL(__free_pages);

//=========================================================
【释放page】

#define free_page(addr) free_pages((addr), 0)
void free_pages(unsigned long addr, unsigned int order)
{
	if (addr != 0) {
		VM_BUG_ON(!virt_addr_valid((void *)addr));
		__free_pages(virt_to_page((void *)addr), order);
	}
}

EXPORT_SYMBOL(free_pages);

//=========================================================
【映射内核内存到user空间】

/**
 * remap_pfn_range - remap kernel memory to userspace
 * @vma: user vma to map to
 * @addr: target page aligned user address to start at
 * @pfn: page frame number of kernel physical memory address
 * @size: size of mapping area
 * @prot: page protection flags for this mapping
 *
 * Note: this is only safe if the mm semaphore is held when called.
 *
 * Return: %0 on success, negative error code otherwise.
 */
int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr,
		    unsigned long pfn, unsigned long size, pgprot_t prot)
{
	int err;

	err = track_pfn_remap(vma, &prot, pfn, addr, PAGE_ALIGN(size));
	if (err)
		return -EINVAL;

	err = remap_pfn_range_notrack(vma, addr, pfn, size, prot);
	if (err)
		untrack_pfn(vma, pfn, PAGE_ALIGN(size));
	return err;
}
EXPORT_SYMBOL(remap_pfn_range);

//=========================================================
【SetPageReserved 和 ClearPageReserved 的封装】

/* Free the reserved page into the buddy system, so it gets managed. */
static inline void free_reserved_page(struct page *page)
{
	ClearPageReserved(page);
	init_page_count(page);
	__free_page(page);
	adjust_managed_page_count(page, 1);
}
#define free_highmem_page(page) free_reserved_page(page)

static inline void mark_page_reserved(struct page *page)
{
	SetPageReserved(page);
	adjust_managed_page_count(page, -1);
}

//=========================================================

扩展做法

void* mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset) 的最后一个标记
可以作为和kernel的约定。

如下所示:
user空间传递 offset ,kernel空间决定将它作为页的偏移
这样用户空间可以决定用哪一页

static int demo_mmap(struct file *file, struct vm_area_struct *vma)
{
	int ret = 0;
	unsigned long pfn_start = (phy_addr_start >> PAGE_SHIFT) + vma->vm_pgoff; //页帧号
	unsigned long size = vma->vm_end - vma->vm_start;

	ret = remap_pfn_range(vma, vma->vm_start, pfn_start, size, vma->vm_page_prot);
	if (ret) {
		printk(DEVICE_NAME "%s remap_pfn_range failed [0x%lx  0x%lx)\n",
			__func__, vma->vm_start, vma->vm_end);
	}

	return ret;
}

还有另一个方式:
void* mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset) 的最后一个
直接指定物理地址,前提是用户知道板子的物理地址

//=========================================================

熟悉如下概念

内核物理地址
内核虚拟地址
page(页)
页帧
vma

前四个之间的转换关系见上文代码

推荐博客:https://www.cnblogs.com/pengdonglin137/p/8149859.html

//=========================================================
(验证)

映射前(main中加sleep)

root@ubuntu:~# cat /proc/2741/maps
00400000-00401000 r-xp 00000000 08:01 269767                             /home/kiss1994/study/test35/main
00600000-00601000 r--p 00000000 08:01 269767                             /home/kiss1994/study/test35/main
00601000-00602000 rw-p 00001000 08:01 269767                             /home/kiss1994/study/test35/main
0113f000-01160000 rw-p 00000000 00:00 0                                  [heap]
7f41c2186000-7f41c2346000 r-xp 00000000 08:01 396057                     /lib/x86_64-linux-gnu/libc-2.23.so
7f41c2346000-7f41c2546000 ---p 001c0000 08:01 396057                     /lib/x86_64-linux-gnu/libc-2.23.so
7f41c2546000-7f41c254a000 r--p 001c0000 08:01 396057                     /lib/x86_64-linux-gnu/libc-2.23.so
7f41c254a000-7f41c254c000 rw-p 001c4000 08:01 396057                     /lib/x86_64-linux-gnu/libc-2.23.so
7f41c254c000-7f41c2550000 rw-p 00000000 00:00 0 
7f41c2550000-7f41c2576000 r-xp 00000000 08:01 396068                     /lib/x86_64-linux-gnu/ld-2.23.so
7f41c2759000-7f41c275c000 rw-p 00000000 00:00 0 
7f41c2775000-7f41c2776000 r--p 00025000 08:01 396068                     /lib/x86_64-linux-gnu/ld-2.23.so
7f41c2776000-7f41c2777000 rw-p 00026000 08:01 396068                     /lib/x86_64-linux-gnu/ld-2.23.so
7f41c2777000-7f41c2778000 rw-p 00000000 00:00 0 
7ffc07773000-7ffc07794000 rw-p 00000000 00:00 0                          [stack]
7ffc077a0000-7ffc077a3000 r--p 00000000 00:00 0                          [vvar]
7ffc077a3000-7ffc077a5000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

映射后(main中加sleep)

root@ubuntu:~# cat /proc/2741/maps
00400000-00401000 r-xp 00000000 08:01 269767                             /home/kiss1994/study/test35/main
00600000-00601000 r--p 00000000 08:01 269767                             /home/kiss1994/study/test35/main
00601000-00602000 rw-p 00001000 08:01 269767                             /home/kiss1994/study/test35/main
0113f000-01160000 rw-p 00000000 00:00 0                                  [heap]
7f41c2186000-7f41c2346000 r-xp 00000000 08:01 396057                     /lib/x86_64-linux-gnu/libc-2.23.so
7f41c2346000-7f41c2546000 ---p 001c0000 08:01 396057                     /lib/x86_64-linux-gnu/libc-2.23.so
7f41c2546000-7f41c254a000 r--p 001c0000 08:01 396057                     /lib/x86_64-linux-gnu/libc-2.23.so
7f41c254a000-7f41c254c000 rw-p 001c4000 08:01 396057                     /lib/x86_64-linux-gnu/libc-2.23.so
7f41c254c000-7f41c2550000 rw-p 00000000 00:00 0 
7f41c2550000-7f41c2576000 r-xp 00000000 08:01 396068                     /lib/x86_64-linux-gnu/ld-2.23.so
7f41c2759000-7f41c275c000 rw-p 00000000 00:00 0 
7f41c2774000-7f41c2775000 rw-s 00000000 00:06 406                        /dev/demo_dev 
7f41c2775000-7f41c2776000 r--p 00025000 08:01 396068                     /lib/x86_64-linux-gnu/ld-2.23.so
7f41c2776000-7f41c2777000 rw-p 00026000 08:01 396068                     /lib/x86_64-linux-gnu/ld-2.23.so
7f41c2777000-7f41c2778000 rw-p 00000000 00:00 0 
7ffc07773000-7ffc07794000 rw-p 00000000 00:00 0                          [stack]
7ffc077a0000-7ffc077a3000 r--p 00000000 00:00 0                          [vvar]
7ffc077a3000-7ffc077a5000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

通过在map前后加sleep打印maps的信息,得出用户空间在如下位置映射了一个页

7f41c2774000-7f41c2775000 rw-s 00000000 00:06 406                       /dev/demo_dev
7f41c2775000 - 7f41c2774000 = 0x1000

"7f41c2774000"是vma->vm_start的值,"7f41c2775000"是vma->vm_end的值
vma表示的user空间虚拟内存区域大小
"rw-s"表示的是vma->vm_flags,其中’s’表示share,'p’表示private
"00000000"表示偏移量,也就是vma->vm_pgoff的值
"00:06"表示该设备节点的主次设备号
"406"表示该设备节点的inode值
"/dev/demo_dev"表示设备节点的名字

//==============================================================================
该进程的进程栈如下:

7ffc07773000-7ffc07794000 rw-p 00000000 00:00 0                          [stack]
7ffc07794000 - 7ffc07773000 = 0x21000B = 132KB

mmap区恰好位于栈区和堆区之之间,它和栈区都是向下增长,其他区都是向上增长
进程的栈太大,会导致mmap区太小
mmap区除了mmap映射用,还会给动态库的映射和 匿名映射用

参考博客:https://www.cnblogs.com/pengdonglin137/p/8150462.html
vmalloc也可以和用户空间进行映射,使用它分配的内存虚拟地址是连续的,但是不保证物理页帧也连续。
vmalloc在分配内存时是调用alloc_page一页一页的分配,就是每次从伙伴系统只分配一页,
然后将分配得到的单页物理页帧映射到内核的vmalloc区连续的虚拟地址上。

比如:
用vmalloc分配128KB的内存,vmalloc计算发现需要分配32个page,
然后会调用32次alloc_page(),每次从伙伴系统分配1个page,
每分配一个page就将该page映射到准备好的连续的虚拟地址上,
当然也就无法保证这些page之间对应的物理页帧的连续性。

在调用remap_pfn_range的时候就需要注意,必须一页一页地映射。
通过vmalloc分配的页,要用vmalloc_to_pfn 进行页帧的映射。

static void *kbuff = vmalloc(BUF_SIZE);
static int remap_pfn_mmap(struct file *file, struct vm_area_struct *vma)
{
    unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
    unsigned long virt_start = (unsigned long)kbuff + (unsigned long)(vma->vm_pgoff << PAGE_SHIFT);
    unsigned long pfn_start = (unsigned long)vmalloc_to_pfn((void *)virt_start);
    unsigned long size = vma->vm_end - vma->vm_start;
    int ret = 0;
    unsigned long vmstart = vma->vm_start;
    int i = 0;

    printk("phy: 0x%lx, offset: 0x%lx, size: 0x%lx\n", pfn_start << PAGE_SHIFT, offset, size);

    while (size > 0) {
		// 一页一页映射
        ret = remap_pfn_range(vma, vmstart, pfn_start, PAGE_SIZE, vma->vm_page_prot);
        if (ret) {
            printk("%s: remap_pfn_range failed at [0x%lx  0x%lx]\n",
                __func__, vmstart, vmstart + PAGE_SIZE);
            ret = -ENOMEM;
            goto err;
        } else
            printk("%s: map 0x%lx (0x%lx) to 0x%lx , size: 0x%lx, number: %d\n", __func__, virt_start,
                pfn_start << PAGE_SHIFT, vmstart, PAGE_SIZE, ++i);

        if (size <= PAGE_SIZE)
            size = 0;
        else {
            size -= PAGE_SIZE;
            vmstart += PAGE_SIZE;
            virt_start += PAGE_SIZE;
            pfn_start = vmalloc_to_pfn((void *)virt_start);
        }
    }

    return 0;
err:
    return ret;
}

remap_pfn_range映射内存中的保留页和设备IO内存

附件1

kmalloc 和 __get_free_page 的区别
kmalloc() 分配连续的内核虚拟地址,用于小内存分配。
__get_free_page() 分配连续的内核虚拟地址,用于整页分配。

前者基于slab(slab基于buddy),后者基于buddy,两者最终都使用 __alloc_page() 来返回物理页的page结构。
都是在“物理内存映射区” lowmem 进行内存分配,其内核虚拟地址与物理地址仅相差一个常量。

附件2

匿名映射是否可以作为父子进程通信的手段?
学习博客:https://blog.csdn.net/liuyuchen282828/article/details/99537988

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

int var=100;

int main(void) {
    int *p; 
    pid_t pid;

    //不使用文件参数传-1                                                           
    p=(int *)mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANON,-1,0);//对映射区>各自独占
    if(p==MAP_FAILED){ //不是p==NULL时出错
        perror("mmap,error");    
        exit(1);
    }   

    //完成数据传递
    pid=fork();
    if(pid==0){
        *p=2000;
        var=1000;
        printf("child,*p=%d,var = %d\n",*p,var);
    }else{
        sleep(2);
    
        printf("parent,*p = %d,var = =%d\n",*p,var);
        wait(NULL);

        int ret= munmap(p,4); 
        if(ret==-1){
            perror("munmap error");
          exit(1);
        }
    }
    return 0;
}

是可以通信的,前提是要设置好 MAP_ANONYMOUS 和 MAP_SHARED
一个用于匿名(懒得映射某个文件),一个强调共享
借助文件系统,mmap可以实现血缘进程的映射(支持命名和匿名),非血缘进程的映射(只支持命名)

如果 open 时 O_RDONLY ,mmap 时 PROT 参数指定 PROT_READ|PROT_WRITE ,会映射出错
创建映射区的权限小于等于打开文件的权限,映射区创建的过程中存在一次读文件操作权限不足。

映射区的释放与文件关闭无关。只要映射建立成功,文件可以立即关闭

附件3

验证如下语句:
当调用malloc分配内存的时候,如果传给malloc的参数小于128KB时,系统会在heap区分配内存,
分配的方式是向高地址调整brk指针的位置。
当传给malloc的参数大于128KB时,系统会在mmap区分配,即分配一块新的vma

Guess you like

Origin blog.csdn.net/wangkai6666/article/details/121447402