编写多任务应用程序时需要做出的决定之一是使用进程(使用fork系统调用)还是使用线程(使用posix库)。使用多个线程的主要好处是它们之间共享内存,但是当一个线程例如捣毁另一个线程缓冲区时,此功能也会产生问题。调试这种情况很困难,因为我们看到了垃圾数据,但是我们无法弄清楚是哪个线程执行的。

解决该问题的一种方法是在缓冲区之间添加没有权限的保护页。在Linux中使用posix线程时,默认情况下,其保护大小属性为一页自动完成。

例:

#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
#include <sys/mman.h>void *threadfn(void *p)
{int val=100;printf("thread val address=%lx\n",&val);sleep(100);return NULL;
}int main()
{int val=100;printf("main val address=%lx\n",&val);pthread_t t1,t2,t3;pthread_create(&t1,NULL,threadfn,NULL);pthread_create(&t2,NULL,threadfn,NULL);pthread_create(&t3,NULL,threadfn,NULL);sleep(100);pthread_exit(NULL);return 0;
}/* Output:
main val address=7ffe2438515c
thread val address=7fdba3989f44
thread val address=7fdba3a8af44
thread val address=7fdba4274f44
*/

我们创建3个线程并打印局部变量的地址(位于堆栈中)

现在,使用cat / proc / [pid] / maps查看进程内存映射

您可以看到linux线程库在线程堆栈之间添加了一页(4k),以防止其中一个限制另一个。

您可以在创建新线程时使用pthread_attr_t参数并使用pthread_attr_setguardsize函数来更改防护大小。

#include <pthread.h>int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize);
int pthread_attr_getguardsize(pthread_attr_t *attr, size_t *guardsize);

但是,诸如动态分配或BSS / DATA节中的缓冲区之类的未位于堆栈中的内存又如何呢?

在这里您可以使用mprotect创建该保护区

#include <sys/mman.h>int mprotect(void *addr, size_t len, int prot);

mprotect可用于更改属于该进程的任何映射内存的MMU权限,例如:

mprotect(buffer,0x1000,PROT_READ);

在这里,我们将缓冲区地址页设置为只读,任何尝试写入的操作都会触发SIGSEGV信号(分段故障)。

需要注意的一点是,缓冲区必须与页面边界对齐,因此我们不能使用malloc对其进行分配。我们可以使用memalignaligned_alloc

#include <stdlib.h>int posix_memalign(void **memptr, size_t alignment, size_t size);
void *aligned_alloc(size_t alignment, size_t size);
void *valloc(size_t size);#include <malloc.h>void *memalign(size_t alignment, size_t size);
void *pvalloc(size_t size);

但是在这种情况下,最好的方法是直接调用mmap,而不使用任何堆。mmap返回页面,如果我们将其与MAP_ANONYMOUS一起使用,我们将从内核获取内存

p1=mmap(0,0x20000,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0);mprotect(p1+page_size,page_size,PROT_NONE);
mprotect(p1+0x1f000,0x1000,PROT_NONE);pthread_attr_setstack(&attr,p1+0x1000,0x1d000);pthread_create(&t1,&attr,threadfn1,NULL);

在此代码中,我们首先分配128kb(0x20000),然后使用mprotect设置没有权限的第一页和最后一页,并使该内存区域成为线程堆栈。

如果线程溢出缓冲区,则进程将得到SIGSEGV,并且如果为该信号编写信号处理程序,它将在有问题的线程上下文中运行,因此我们可以捕获它。

完整示例:

#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
#include<unistd.h>
#include <sys/mman.h>void *threadfn1(void *p1)
{int num;int val=90;
/*     //uncomment this to see the thread faultint *p=&val;printf("val addr=%lx\n",&val);for(num=0;num<0x500;num++){*p=20;p++;} */while(1){puts("thread1");sleep(10);}return NULL;
}void *threadfn2(void *p)
{while(1){puts("thread2");sleep(10);}return NULL;
}int main()
{void *p1,*p2;pthread_t t1,t2,t3;int page_size=sysconf(_SC_PAGESIZE);printf("page size=%x\n",page_size);pthread_attr_t attr;struct sched_param param;pthread_attr_init(&attr);p1=mmap(0,0x20000,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0);printf("mmap ret=%lx\n",p1); // the return address is page alignedmprotect(p1+page_size,page_size,PROT_NONE);mprotect(p1+0x1f000,0x1000,PROT_NONE);p2=malloc(0x20000);printf("malloc ret=%lx\n",p2); // malloc return address is not page alignedpthread_attr_setstack(&attr,p1+0x1000,0x1d000);pthread_create(&t1,&attr,threadfn1,NULL);pthread_attr_setstack(&attr,p2,0x20000);pthread_create(&t2,&attr,threadfn2,NULL);sleep(100);puts("end test");return 0;
}

结果是相同的,但是您可以对任何缓冲区使用此方法,不仅在堆栈中

https://devarea.com/using-mprotect-system-call-to-debug-memory-problems/#.X3RLSWgzaUk

使用mprotect系统调用来调试内存问题:memalign,aligned_alloc相关推荐

  1. 使用CRT调试内存分配堆来找出未释放的内存空间

    忘记释放已经分配的内存是一种常见的编程错误,当然我指的是在C++编程当中,例如下面的代码里面就存在一个忘记释放内存的编程错误.我个人觉得忘记释放内存的编程错误是不可避免的,毕竟程序员都是人,困了,心情 ...

  2. Golang程序调试 -- 内存泄漏pprof工具

    Golang程序调试 -- 内存泄漏pprof工具 代码引入pprof WEB访问模式 命令行模式定位内存 命令行模式定位耗时 命令行模式定位内存分配 代码引入pprof import (" ...

  3. 在vc2008中用_crtBreakAlloc调试内存泄漏

    在vc2008中用_crtBreakAlloc调试内存泄漏 2012-07-27 16:58:25 分类: C/C++ 这两天调一个程序,发现每次退出都有内存泄漏,在此总结一个调试内存泄漏的好方法. ...

  4. 调试内存_C/C++程序调试和内存检测

    来源:wxquare www.cnblogs.com/wxquare/p/4840063.html 程序出现错误很正常,一个优秀的程序员必须学会调试,发现错误并改正.减少程序错误最有效的方法是:在敲代 ...

  5. Linux环境编程:fork系统调用及其陷阱 - 内存复用,文件不复用

    Table of Contents 内存呢?是重复的 CoW(写时复制) 文件在fork上不重复 使用fork创建任务 亲子代号不同 例如:2个使用管道进行通信的进程: 推荐阅读 要在Linux / ...

  6. 怎么调试内存溢出的c++代码_使用jvisualvm排查一次内存溢出(OOM)过程

    内存溢出在开发中或者线上出现的概率很高,造成的直接原因就是系统运行缓慢,或者直接宕机了. 小编在这里模拟下内存溢出的情况以防患于线上出现内存溢出要如何排查问题.题外话(线上出问题你需要生成一个快照(h ...

  7. 调试内存_如何调试Python 程序的内存泄露问题

    IT服务圈儿 有温度.有态度的IT自媒体平台 本文经公众号:未闻Code(ID:itskingname) 授权转载 如需二次转载请联系出处 如果大家在 Linux 或者 macOS 下面运行一段可能导 ...

  8. stm32调试内存越界情况

    最近项目调试中总是遇到内存越界,现象比较诡异.再此写下记录,以便后续学习. 现象:测试整个流程 莫名其妙的 重启,有的时候不重启但是进入死循环.单步跟踪 有时不在同一个地方进入异常. 调试过程: 测试 ...

  9. gflags 调试内存_gflags工具使用——用于监控内存分配、检查内存泄露

    想要知道程序从开始到运行一段时间内存的使用情况,可以使用gflags工具. 1. windbg同级目录下有gflags.exe,点开后进行如下设置. 2. 在gflags.exe的目录下直接打开命令窗 ...

最新文章

  1. Win32API 窗口程序的创建7大步骤
  2. 《压缩感知回顾与展望》读书笔记
  3. 信息系统项目管理师案例分析
  4. JavaScript变量作用域和内存问题(js高级程序设计总结)
  5. SparkSql官方文档中文翻译(java版本)
  6. CSS浮动、定位与z-index层叠等级详解——响应式Web系列学习笔记
  7. 如何用jsp连接mysql_如何用jsp连接mysql数据库
  8. 怎样将程序猿写出来的程序打包成安装包(最简单的)
  9. 解决办法:GTK+ 2.x symbols detected
  10. win10 C语言qt调试,如何在Windows中调试Qt(MSVC)应用程序
  11. URLDecoder解码异常 URLDecoder: Illegal hex characters in escape (%) pattern - For input string: “xxx“
  12. python 使用networkx绘制带权无向图和带权有向图,以及标注特定路径
  13. javascript三角函数的使用
  14. sqlite驱动加载失败的解决办法
  15. 【见闻录系列】我所理解的搜索业务二三事
  16. Linux常用命令个人记录
  17. 大数据开发工程师必看书籍
  18. 计算机管理无用怎么办,win7系统如何将资源管理器窗口中无用的图标删除掉?...
  19. (一)走进Linux世界(安装Centos8,初始化生产环境,GNU bash)
  20. asp.net中引用System.Web.UI.DataVisualization.Charting命名空间

热门文章

  1. Matlab求高斯脉冲函数
  2. Java线程局部变量ThreadLocal
  3. C++学习笔记(九)——引用
  4. TCP/IP具体解释--TCP的分段和IP的分片
  5. 工具--常见eclipse配置导入web工程(tomcat容器)步骤
  6. Restlet入门例子 - RESTful web framwork for java
  7. nodejs计算时间间隔_NodeJs笔记:setTimeout 或 setInterval 的间隔时间执行
  8. 计算机设置成一个网络,同一个路由器上的电脑怎么设置成局域网连网打 – 手机爱问...
  9. 树莓派升级Linux内核,树莓派编译升级内核
  10. 华润燃气各大区总经理_华润燃气“十四五”战略研讨会在华润大学小径湾校区举办...