Linux编程之内存映射(2)

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

#define MMAP_FILE_NAME "a.txt"
#define MMAP_FILE_SIZE 10

void err_exit(const char *err_msg)
{
    printf("error:%s\n", err_msg);
    exit(1);
}

/* 信号处理器 */
void signal_handler(int signum)
{
    if (signum == SIGSEGV)
        printf("\nSIGSEGV handler!!!\n");
    else if (signum == SIGBUS)
        printf("\nSIGBUS handler!!!\n");
    exit(1);
}

int main(int argc, const char *argv[])
{
    if (argc < 2)
    {
        printf("usage:%s text\n", argv[0]);
        exit(1);
    }

char *addr;
    int file_fd, text_len;
    long int sys_pagesize;

/* 设置信号处理器 */
    if (signal(SIGSEGV, signal_handler) == SIG_ERR)
        err_exit("signal()");
    if (signal(SIGBUS, signal_handler) == SIG_ERR)
        err_exit("signal()");

if ((file_fd = open(MMAP_FILE_NAME, O_RDWR)) == -1)
        err_exit("open()");

/* 系统分页大小 */
    sys_pagesize = sysconf(_SC_PAGESIZE);
    printf("sys_pagesize:%ld\n", sys_pagesize);

/* 内存只读 */
    //addr = (char *)mmap(NULL, MMAP_FILE_SIZE, PROT_READ, MAP_SHARED, file_fd, 0);
   
    /* 映射大于文件长度,且大于该文件分页大小 */
    //addr = (char *)mmap(NULL, sys_pagesize + 1, PROT_READ | PROT_WRITE, MAP_SHARED, file_fd, 0);

/* 正常分配 */
    addr = (char *)mmap(NULL, MMAP_FILE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, file_fd, 0);
    if (addr == MAP_FAILED)
        err_exit("mmap()");

/* 原始数据 */
    printf("old text:%s\n", addr);

/* 越界访问 */
    //addr += sys_pagesize + 1;
    //printf("out of range:%s\n", addr);

/* 拷贝新数据 */
    text_len = strlen(argv[1]);
    memcpy(addr, argv[1], text_len);

/* 同步映射区数据 */
    //if (msync(addr, text_len, MS_SYNC) == -1)
    //    err_exit("msync()");

/* 打印新数据 */
    printf("new text:%s\n", addr);

/* 解除映射区域 */
    if (munmap(addr, MMAP_FILE_SIZE) == -1)
        err_exit("munmap()");

return 0;
}

1.首先创建一个10字节的文件:

1 $:dd if=/dev/zero of=a.txt bs=1 count=10

2.把程序编译运行后,依次执行2写入:

Linux编程之内存映射

可以看到本机的分页大小是4096字节。第一次写入9个字节,原来用dd命令创建的文件为空,old text为空。第二次写入4个字节,只覆盖了最前面的1234。

3.验证可访问现有分页的内存。写入超过10字节的数据:

Linux编程之内存映射

上面我们写入了17个字节,虽然64行的mmap()映射了MMAP_FILE_SIZE=10字节。但从输入new text可以看出,我们���然可以访问10字节后面的内存,因为该数据都在一个分页(4096)里面。cat查看a.txt后,只有前10个字节写入了a.txt。

4.验证SIGSEGV信号。把64行注释调,58行打开,设置映射属性为只读,编译后访问:

Linux编程之内存映射

设置只读属性后,第77行有写操作。我们自定义的信号处理器就捕捉到了该信号。如果没有自定义信号处理器,终端就会输出Segmentation fault

5.验证SIGBUS信号。用61行的方法来映射内存。映射了一个分页大小再加1字节的内存,并放开72,73行的代码,让指针指向一个分页后的区域。编译后运行:

Linux编程之内存映射

SIGBUS信号被自定义处理器捕捉到了。如果没有自定义信号处理器,终端就会输出Bus error

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/283c80df36ab6186a15f08d40274bef3.html