免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 13601 | 回复: 10
打印 上一主题 下一主题

加分啦:memcpy出现了总线错误 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-04-06 20:11 |只看该作者 |倒序浏览
20可用积分
用mmap映射两个文件,然后把一个文件的内容复制到另一个文件
采用的是memcpy函数
但是使用这个函数出现了总线错误
我把两个映射的地址和长度都输出来了
请高手给分析一下。
输出结果:
file size = 18
src = 0xb7fa9000
dst = 0xb7fa8000
总线错误

  1. #include <stdio.h>
  2. #include <fcntl.h>
  3. #include <sys/mman.h>
  4. #include <sys/stat.h>
  5. #include <sys/types.h>
  6. #include <stdlib.h>
  7. #include <string.h>

  8. int main(int argc,char *argv[])
  9. {
  10.         int fdin,fdout;
  11.         void *src,*dst;

  12.         struct stat statbuf;

  13.         if(argc != 3)
  14.         {
  15.                 printf("usage : %s <fromfile> <tofile>\n",argv[0]);
  16.                 return 1;
  17.         }
  18.         if((fdin = open(argv[1],O_RDONLY)) == -1)
  19.         {
  20.                 printf("can't open file %s\n",argv[1]);
  21.                 return 1;
  22.         }
  23.         if((fdout = open(argv[2],O_RDWR | O_CREAT | O_TRUNC,0644)) == -1)
  24.         {
  25.                 printf("can't create file %s\n",argv[2]);
  26.                 return 1;
  27.         }
  28.         if(fstat(fdin,&statbuf) < 0)
  29.         {
  30.                 printf("fstat error.\n");
  31.                 return 1;
  32.         }
  33.         printf("file size = %d\n",statbuf.st_size);
  34.         if(lseek(fdout,statbuf.st_size - 1,SEEK_SET) == -1)
  35.         {
  36.                 printf("set file size error.\n");
  37.                 return 1;
  38.         }
  39.         printf("file size = %d\n",statbuf.st_size);
  40.         if(lseek(fdout,statbuf.st_size - 1,SEEK_SET) == -1)
  41.         {
  42.                 printf("set file size error.\n");
  43.                 return 1;
  44.         }
  45.         if((src = mmap(0,statbuf.st_size,PROT_READ,MAP_SHARED,fdin,0)) == MAP_FAILED)
  46.         {
  47.                 printf("mmap error for fdin.\n");
  48.                 return 1;
  49.         }
  50.         printf("src = 0x%x\n",src);
  51.         if((dst = mmap(0,statbuf.st_size,PROT_READ | PROT_WRITE,MAP_SHARED,fdout,0)) == MAP_FAILED)
  52.         {
  53.                 printf("mmap error for fdout.\n");
  54.                 return 1;
  55.         }
  56.         printf("dst = 0x%x\n",dst);
  57.         memcpy(dst,src,statbuf.st_size);
  58.         close(fdin);
  59.         close(fdout);
  60.         exit(0);
  61. }
复制代码

最佳答案

查看完整内容

可以正常工作的代码类似:一种输出:[lgr@localhost Lab]$ ./a.out mmap.c out.cPageSize: 4096filein size = 1568fileout size = 1568LZ出Bus Error的原因:1,lseek只是改变文件偏移量,不是文件实际大小,用ls -l就可以明显看到两个文件的区别。可以先通过lseek到文件尾,再write一个字节来改变输出文件的大小或者直接使用ftruncate。2,内核很清楚被映射的对象大小(你的输出文件大小为0),而内存映射区域总是以页为单位(如4 ...

论坛徽章:
11
技术图书徽章
日期:2014-03-01 14:44:34天蝎座
日期:2014-05-21 22:11:59金牛座
日期:2014-05-30 17:06:14
2 [报告]
发表于 2009-04-06 20:11 |只看该作者
可以正常工作的代码类似:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>

int main(int argc,char *argv[])
{
    int fdin,fdout;
    void *src,*dst;
    size_t pagesize;

    struct stat statbuf;

    if(argc != 3)
    {
        printf("usage : %s <fromfile> <tofile>\n",argv[0]);
        return 1;
    }
    pagesize = sysconf(_SC_PAGESIZE);
    printf("PageSize: %ld\n", (long)pagesize);
    if((fdin = open(argv[1],O_RDONLY)) == -1)
    {
        printf("can't open file %s\n",argv[1]);
        return 1;
    }
    if((fdout = open(argv[2],O_RDWR | O_CREAT | O_TRUNC,0644)) == -1)
    {
        printf("can't create file %s\n",argv[2]);
        return 1;
    }
    assert(fstat(fdin,&statbuf) == 0);
    printf("filein size = %ld\n",statbuf.st_size);
    assert(ftruncate(fdout, statbuf.st_size) == 0
        && fstat(fdout, &statbuf) == 0);
    printf("fileout size = %ld\n",statbuf.st_size);
    src = mmap(0,statbuf.st_size,PROT_READ,MAP_SHARED,fdin,0);
    if( src == MAP_FAILED )
    {
        printf("mmap error for fdin.\n");
        return 1;
    }
    dst = mmap(0,statbuf.st_size,PROT_READ | PROT_WRITE,MAP_SHARED,fdout,0);
    if( dst == MAP_FAILED )
    {
        printf("mmap error for fdout.\n");
        return 1;
    }
    memcpy(dst,src,statbuf.st_size);
    close(fdin);
    close(fdout);

    return 0;
}



一种输出:
[lgr@localhost Lab]$ ./a.out mmap.c out.c
PageSize: 4096
filein size = 1568
fileout size = 1568

LZ出Bus Error的原因:
1,lseek只是改变文件偏移量,不是文件实际大小,用ls -l就可以明显看到两个文件的区别。可以先通过lseek到文件尾,再write一个字节来改变输出文件的大小或者直接使用ftruncate。
2,内核很清楚被映射的对象大小(你的输出文件大小为0),而内存映射区域总是以页为单位(如4096字节),LZ的例子中直接引用0~4095的字节都超出被映射对象的大小,将会出现SIGBUS,如果引用4096~inf时则会超出内存映射区域(一个页面)的边界,CPU内存保护将会会触发SIGSEGV,LZ自己可以动手尝试这两种情况。

论坛徽章:
5
3 [报告]
发表于 2009-04-06 20:13 |只看该作者
file size 能试一下16 or 20吗?

论坛徽章:
0
4 [报告]
发表于 2009-04-06 20:19 |只看该作者
虽然是bus error
如果没有是因为地址没有对齐的话 memcpy应该会自己处理的
我调试了下 好象是lseek函数用的有问题
你可以先查看一下你新创建的那个文件的大小是多少
stat filename

你也就是lseek了一个空洞 但是并引起i/o操作
也就是说如果lseek之后没有发生i/o操作时,lseek只是把偏移量给改了一下
并没有在磁盘上分配存储区
而你的文件是新建的 存储的空间长度为0 lseek之后还是为0
把mmap到系统的线性地址空间,然后进行复制的话就会出错

所以建议LZ在lseek之后进行一个I/O操作,这样就会把lseek跳过的长度给你分配磁盘存储区的
这样的话应该就没有问题了
我在我机器上试过了

论坛徽章:
0
5 [报告]
发表于 2009-04-06 20:20 |只看该作者
原帖由 yidou 于 2009-4-6 20:13 发表
file size 能试一下16 or 20吗?


这个已经试过了
我加了两个字符 变成了20
还是不得行

论坛徽章:
0
6 [报告]
发表于 2009-04-06 20:21 |只看该作者
原帖由 eclipse_2 于 2009-4-6 20:19 发表
虽然是bus error
如果没有是因为地址没有对齐的话 memcpy应该会自己处理的
我调试了下 好象是lseek函数用的有问题
你可以先查看一下你新创建的那个文件的大小是多少
stat filename

你也就是lseek了一个 ...



我试一下先

论坛徽章:
0
7 [报告]
发表于 2009-04-06 22:48 |只看该作者
转载一段:
(1)总线错误:
书上的一段程序:
union { char a[10];
        int i;
      }u;
int *p = (int *)&(u.a[1]);
*p = 17;
书上是说,这段代码会引发总线错误(bus error),但是我不论在ubuntu Linux上调试还是在Window XP(TC)中调试,均未有报错。后来在网上搜索,很多人提到是因为SUN操作系统的原因。
摘录一点书上关于总线错误的描述:
总线错误几乎都是由未对齐的读或写引起的。它之所以称为总线错误,是因为出现未对齐的内存访问请求时,被阻塞的组件就是地址总线。对齐(alignment)的意思就是数据项只能存储在地址是数据项大小的整数倍的内存位置上。在现代的计算机架构中,尤其是RSIC架构,都需要数据对齐,因为与任意的对齐有关的额外操作会是整个内存系统更大更缓慢。通过迫使每个内存访问局限在一个Cache行或一个单独的页面内,可以极大的简化(并加速)如Cache控制器和内存管理单元这样的硬件。
我们表达“数据项不能跨越页面或Cache边界”的规则的方法多少有些间接,因为我们用地址对齐这个术语陈述这个问题。页和Cache的大小是经过精心设计的,这样只要遵循对齐规则就可以保证一个原子数据项不会跨越一个页或Cache块的边界。
这是c专家编程上说的

论坛徽章:
0
8 [报告]
发表于 2009-04-06 22:57 |只看该作者

回复 #6 emmoblin 的帖子

但是从LZ输出的的src和dst地址来看
它是页对齐的
只是lseek函数使用不是很正确
在lseek之后加上一条I/O操作的话 就正确了
这个bus error 似乎在这里面得不到很好体吧
不知道为什么会报bus error错误

论坛徽章:
0
9 [报告]
发表于 2009-04-07 01:47 |只看该作者

回复 #8 timespace 的帖子

用ftruncate函数可以代替lseek函数更好些

论坛徽章:
0
10 [报告]
发表于 2009-04-07 09:57 |只看该作者
[code]/*mycp.c*/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>

int main(int argc,char *argv[])
{
&nbsp;int fdin,fdout;
&nbsp;void* src;
&nbsp;void* dst;
&nbsp;struct stat statbuf;

if(argc != 3)
{
&nbsp;&nbsp;&nbsp;&nbsp;printf("please input two file!\n");
&nbsp;&nbsp;&nbsp;&nbsp;exit(1);
}

if((fdin=open(argv[1],O_RDONLY))<0) /*打开原文件*/
&nbsp;&nbsp;&nbsp;&nbsp;perror(argv[1]);

if((fdout=open(argv[2],O_RDWR|O_CREAT|O_TRUNC))<0)/*创建并打开目标文件*/
&nbsp;&nbsp;&nbsp;&nbsp;perror(argv[2]);

if(fstat(fdin,&statbuf)<0) /*获得文件大小信息*/
&nbsp;&nbsp;&nbsp;&nbsp;printf("fstat error");

if(lseek(fdout,statbuf.st_size-1,SEEK_SET)==-1)/*初始化输出映射存储区*/
&nbsp;&nbsp;&nbsp;&nbsp;printf("lseek error");

if(write(fdout,"1",1)!=1)
&nbsp;&nbsp;&nbsp;&nbsp;printf("write error");

if((src=mmap(0,statbuf.st_size,PROT_READ,MAP_SHARED,fdin,0))==MAP_FAILED)
&nbsp;&nbsp;&nbsp;&nbsp;printf("mmap error");

if((dst=mmap(0,statbuf.st_size,PROT_READ|PROT_WRITE,MAP_SHARED,fdout,0)) ==MAP_FAILED)
&nbsp;&nbsp;&nbsp;&nbsp;printf("mmap error");

&nbsp;memcpy(dst,src,statbuf.st_size);/*复制映射存储区*/
&nbsp;munmap(src,statbuf.st_size); /*解除输入映射*/
&nbsp;munmap(dst,statbuf.st_size); /*解除输出映射*/

&nbsp;close(fdin);
&nbsp;close(fdout);

&nbsp;return 0;
}[/code]
lz的问题只是没加红色的哪条语句....
如果加,当写入数据的时候会遇到文件结束符,产生SIGBUS信号


[ 本帖最后由 ubuntuer 于 2009-4-7 13:11 编辑 ]
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP