#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写入:
可以看到本机的分页大小是4096字节。第一次写入9个字节,原来用dd命令创建的文件为空,old text为空。第二次写入4个字节,只覆盖了最前面的1234。
3.验证可访问现有分页的内存。写入超过10字节的数据:
上面我们写入了17个字节,虽然64行的mmap()映射了MMAP_FILE_SIZE=10字节。但从输入new text可以看出,我们���然可以访问10字节后面的内存,因为该数据都在一个分页(4096)里面。cat查看a.txt后,只有前10个字节写入了a.txt。
4.验证SIGSEGV信号。把64行注释调,58行打开,设置映射属性为只读,编译后访问:
设置只读属性后,第77行有写操作。我们自定义的信号处理器就捕捉到了该信号。如果没有自定义信号处理器,终端就会输出Segmentation fault
5.验证SIGBUS信号。用61行的方法来映射内存。映射了一个分页大小再加1字节的内存,并放开72,73行的代码,让指针指向一个分页后的区域。编译后运行:
SIGBUS信号被自定义处理器捕捉到了。如果没有自定义信号处理器,终端就会输出Bus error