1.头文件
#include <sys/mman.h>
2.函数作用
1.mprotect() changes protection for the calling process’s memory page(s) containing any part of the address range in the interval [addr, addr+len-1]. addr must be aligned to a page boundary.
2.如果进程以破坏保护规则的方式访问内存,那么内核就会给进程产生SIGSEGV信号(如果调用进程内存访问行为侵犯了这些设置的保护属性,内核会为该进程产生 SIGSEGV (Segmentation fault,段错误)信号,并且终止该进程。)
3.函数及参数描述
int mprotect(void *addr, size_t len, int prot);
- addr
地址,参数类型是void*
- len
存储数据的长度
- prot
PROT_NONE
The memory cannot be accessed at all.完全无法访问内存。
PROT_READ
The memory can be read.可以读取内存。
PROT_WRITE
The memory can be modified.内存可以修改。
PROT_EXEC
The memory can be executed.内存可以执行。
- 返回值
On success, mprotect() returns zero. On error, -1 is returned, and errno is set appropriately.成功返回0,失败返回-1
- 错误的原因:
1)EACCES:该内存不能设置为相应权限。这是可能发生的,比如,如果你 mmap(2) 映射一个文件为只读的,接着使用 mprotect() 标志为PROT_WRITE。
2)EINVAL:start 不是一个有效的指针,指向的不是某个内存页的开头。
3)ENOMEM:内核内部的结构体无法分配
4)ENOMEM:进程的地址空间在区间 [start, start+len] 范围内是无效,或者有一个或多个内存页没有映射。 (Before kernel 2.4.19, the error EFAULT was incorrectly produced for these cases.)
4.示例代码
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/mman.h>
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
char *buffer;
static void
handler(int sig, siginfo_t *si, void *unused)
{
printf("Got SIGSEGV at address: 0x%lx\n",
(long) si->si_addr);
exit(EXIT_FAILURE);//#define EXIT_FAILURE 1 /* Failing exit status. */
}
int
main(int argc, char *argv[])
{
char *p;
int pagesize;
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);//清空信号集
sa.sa_sigaction = handler;
if (sigaction(SIGSEGV, &sa, NULL) == -1)//信号集添加要屏蔽的信号
handle_error("sigaction");
pagesize = sysconf(_SC_PAGE_SIZE);//sysconf( _SC_PAGESIZE ); 此宏查看缓存内存页面的大小;打印用%ld长整型。
if (pagesize == -1)
handle_error("sysconf");
/* Allocate a buffer aligned on a page boundary;
initial protection is PROT_READ | PROT_WRITE */
buffer = (char*)memalign(pagesize, 4 * pagesize);//在GNU系统中,malloc或realloc返回的内存块地址都是8的倍数(如果是64位系统,则为16的倍数)。如果你需要更大的粒度,请使用memalign或valloc。这些函数在头文件“stdlib.h”中声明
//函数memalign将分配一个由size指定大小,地址是boundary的倍数的内存块。参数boundary必须是2的幂!函数memalign可以分配较大的内存块,并且可以为返回的地址指定粒度。
//使用函数valloc与使用函数memalign类似,函数valloc的内部实现里,使用页的大小作为对齐长度
if (buffer == NULL)
handle_error("memalign");
printf("Start of region: 0x%lx\n", (long) buffer);
if (mprotect(buffer + pagesize * 2, pagesize,
PROT_READ) == -1)
handle_error("mprotect");
for (p = buffer ; ; ) //就是在这一步报错的
*(p++) = 'a'; //设置为可以读内存,但是却修改内存了
printf("Loop completed\n"); /* Should never happen */
exit(EXIT_SUCCESS);//#define EXIT_SUCCESS 0 /* Successful exit status. */
}