【问题标题】:memcpy segmentation fault on linux but not os xlinux上的memcpy分段错误,但不是os x
【发布时间】:2011-02-05 20:02:43
【问题描述】:

我正在为一个文件实现一个基于日志的文件系统作为一个类项目。我在我的 64 位 OS X 笔记本电脑上运行了大量的代码,但是当我尝试在 CS 部门的 32 位 linux 机器上运行代码时,我遇到了 seg 错误。

我们提供的 API 允许一次写入 DISK_SECTOR_SIZE (512) 个字节。我们的日志记录包含用户想要写入的 512 字节以及一些元数据(他想要写入的扇区、操作类型等)。

总而言之,“记录”对象的大小为 528 字节,这意味着每条日志记录跨越磁盘上的 2 个扇区。

第一条记录在扇区 0 上写入 0-512,在扇区 1 上写入 0-15。 第二条记录在扇区 1 上写入 16-512,在扇区 2 上写入 0-31。 第三条记录在扇区 2 上写入 32-512,在扇区 3 上写入 0-47。 等等。

所以我要做的是将要修改的两个扇区读入 2 个新分配的缓冲区,从记录开始复制到 buf1+计算出的 512 个偏移字节的偏移量。这在两台机器上都能正常工作。

但是,第二个 memcpy 失败了。具体来说,以下代码段错误中的“record+DISK_SECTOR_SIZE-offset”,但仅在 linux 机器上。运行一些随机测试,它变得更加好奇。 linux 机器报告 sizeof(Record) 为 528。因此,如果我尝试将 record+500 的 memcpy 转换为 buf 1 个字节,它应该没有问题。

事实上,我能从记录中得到的最大偏移量是 254。也就是说,memcpy(buf1, record+254, 1) 有效,但是 memcpy(buf1, record+255, 1) 段错误。

有人知道我错过了什么吗?

Record *record = malloc(sizeof(Record));
record->tid = tid;
record->opType = OP_WRITE;
record->opArg = sector;
int i;
for (i = 0; i < DISK_SECTOR_SIZE; i++) {
  record->data[i] = buf[i]; // *buf is passed into this function
}

char* buf1 = malloc(DISK_SECTOR_SIZE);
char* buf2 = malloc(DISK_SECTOR_SIZE);

d_read(ad->disk, ad->curLogSector, buf1);  // API to read a specified sector into a buffer
d_read(ad->disk, ad->curLogSector+1, buf2);

memcpy(buf1+offset, record, DISK_SECTOR_SIZE-offset);
memcpy(buf2, record+DISK_SECTOR_SIZE-offset, offset+sizeof(Record)-sizeof(record->data));

【问题讨论】:

  • record + DISK_SECTOR_SIZE -offset 不会给你你需要放置 (byte*)record+(byte*)DISK_SECTOR_SIZE-(byte*)offset 的东西。当您添加到记录类型时,您会按 sizeof(record) 而不是 DISK_SECTOR_SIZE
  • 代码在一个系统上没有出现段错误这一事实并不能说明一切。由于各种原因,错误代码有时会出现段错误,而其他时候则不会。
  • 大卫的评论在这里非常正确。我还没有达到作业中的部分,即我读回我写到日志中的内容并重建内存中的对象——我之前写的内容在那时肯定会失败。
  • 我的猜测是当越界内存访问达到操作码时会发生段错误,这是保护机制。

标签: c pointers segmentation-fault memcpy


【解决方案1】:

当你给指针 p 加 1 时,你不是在加 1 个字节,而是在加 sizeof(p) 个字节。

所以在这种情况下,您需要在添加之前将record 转换为char*。现在record+500 实际上指向距离record 500*528 = 264,000 字节。

当然,这并不能解释为什么memcpy(buf, record+254, 1) 不会出现段错误。我猜只是“幸运”。

【讨论】:

    【解决方案2】:

    record+BLAH的意思是,翻译成机器码时:将BLAH*sizeof(Record)添加到record中的地址。

    所以record+500 不是来自record 的500 个字节;距离record 500*528 = 264000 字节。

    【讨论】:

    • 就是这样,谢谢!考虑到指针可以被视为数组索引,你说的很有道理,但我从来没有想过。
    【解决方案3】:

    尝试在valgrind 下运行您的 Linux 代码 - 这应该可以让您直接找到问题的根本原因。

    【讨论】:

      【解决方案4】:

      record+DISK_SECTOR_SIZE-offset 是你的问题。记录+1 没有给出记录+1 的地址。

      它给你和记录的地址+1*sizeof(record)。 (当您递增或递减一个指针时,它会以您使用的数据类型的倍数步进。只需像这样对您的记录指针进行类型转换:(byte*)record+DISK_SECTOR_SIZE-offset。这可以解释分段错误,因为记录至少是 DISK_SECTOR_SIZE 长.

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-10-05
        • 1970-01-01
        • 2011-11-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多