【问题标题】:Shared Memory sometimes not null terminatied共享内存有时不是空终止的
【发布时间】:2012-02-21 15:11:44
【问题描述】:
// reading                        
if((shmid = shmget(key, 0, 0)) < 0) {                  
    perror("shmget");                
    exit(50); 
}                                                         

// attach                                                 
if((data = shmat(shmid, (void *)0, 0)) == (char *) -1) {        
    perror("shmat");                                      
    exit(100);                                            
}

// get memory size
struct shmid_ds shm_info;
size_t shm_size;
int shm_rc;
if((shm_rc = shmctl(shmid, IPC_STAT, &shm_info)) < 0)
    exit(101);
shm_size = shm_info.shm_segsz;

有时数据不是以空值终止的,调用 strlen(data) 会导致段错误...

所以我试图通过 make 来确保它是空终止的

data[shm_size] = '\0';

但现在有时它会在该行代码上失败。

我做错了什么?

编辑:感谢您的支持!我认为在您对 strlen() + 1 == shm_size 的解释之后,我已经更改了未在此处发布的其余代码,并且似乎很好。我正在等待新的段错误,希望我不会得到任何;)

【问题讨论】:

  • 告诉我你是如何为data定义和分配空间的。我猜是由不正确的内存分配引起的问题。 data 数组可能需要 +1 个字节的空间。
  • 是的,您正在对非字符串对象调用字符串函数。这是第一个问题,这表明您在开始解决此问题的其余部分之前需要阅读更多内容。
  • PHP 正在使用 shmop 填充共享内存...我确信它包含字符串,因为当我使用 gdb 检查核心转储时,我可以看到数据的内容。 ..
  • 你也能看到尺寸吗? GDB 可能只会向您显示您指定的数据量(例如 x/3xb 只给您 3 个字节)。即使是“打印”函数也可能并不总是检查字符串的大小,而是打印到“\0”字符,这可能是在你的“字符串”之后偶然出现的,所以看起来它是 NULL 终止的。
  • 不行,制作gdb后> 设置打印元素0,在文本末尾我可以看到地址xxx超出范围错误...

标签: c memory-leaks shared-memory


【解决方案1】:

数组是0-origin,你想要:

data[shm_size - 1] = '\0';

【讨论】:

  • 这样,它总是使用 100% CPU...请注意,我的代码几乎总是像魅力一样工作...但是在某些情况下它会出现段错误...
  • @edo888:确保 shm_size 大于零。如果它变为零,这可能是 100% CPU 使用率的原因。
  • 如果共享内存一切正常并且它是空终止的,那么 strlen(data) == shm_size 不应该吗?
【解决方案2】:

首先:内存不必为空终止。它在开始时是未定义的。 您可能要考虑使用

memset(data,0,shm_size);

第二个:

data[shm_size] = '\0';

一个索引是错误的。数组从零开始,所以你应该使用

data[shm_size-1] = '\0';

【讨论】:

  • 内存总是被填充的。我只是从中阅读。如果共享内存一切正常,strlen(data) == shm_size 不应该吗?
  • shm_size 将是 strlen(data)+1 因为你最后有 '\0' 字符。
  • @edo888:内存并不总是被填充,您无法控制是否有超过共享内存块末尾的内存或该内存将包含什么。从中读取(例如通过strlen)是未定义的行为。是的,新共享内存缓冲区的内容最初将全为零,但您不是在询问缓冲区的内容;您是在询问缓冲区末尾之后的内存内容。
  • 感谢您的澄清!
【解决方案3】:

空终止符的赋值:

data[shm_size] = '\0';

正在写入超过分配内存的末尾,这是未定义的行为。如果缓冲区有长度为shm_size 的字符数据,则需要将其复制到另一个缓冲区以空终止它。

【讨论】:

  • 如果共享内存一切正常并且它是空终止的,strlen(data) == shm_size 不应该吗?
  • 不,strlen(data) 可以返回 shm_size 的唯一方法是如果您调用了未定义的行为,例如通过在非空终止的缓冲区上调用strlen。与所有字符串缓冲区一样,在选择缓冲区长度时需要考虑空终止。
【解决方案4】:

通过做

`data[shm_size] = '\0';` 

您实际上是在访问共享内存边界外部的内存区域...指向原始内存块的指针上的data[index_value] 语法与所说的相同

*(data + index_value*sizeof(unsigned char))  

因此data[0] 将取消引用并返回共享内存段中第一个内存地址处的值,data[shm_size] 将在共享内存段末尾处执行相同的操作。

【讨论】:

  • 如果共享内存一切正常,strlen(data) == shm_size 不应该吗?
  • 否,因为strlen() 不包括 NULL 终止值。因此,存储以零结尾的字符串所需的内存块的实际最小大小需要等于strlen() + 1
【解决方案5】:

data[shm_size] 是一个超越终点。相反,您应该使用data[shm_size-1],并且只有在shm_size != 0 时才这样做。

不过,调用strlen() 仅在您确实在其中放入字符串时才有意义。否则,如果碰巧有 \0 字符,它可能会返回任何值 &lt; shm_size

【讨论】:

  • 如果共享内存一切正常并且它是空终止的,那么 strlen(data) == shm_size 不应该吗?
  • @edo888 否。如果内存区域未初始化,它可能包含任何随机垃圾,如所写,可能包含停止计数的 \0 字符。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-12-12
  • 2018-09-29
  • 2018-07-28
  • 2010-11-13
  • 2014-03-22
  • 2017-03-07
  • 2014-10-08
相关资源
最近更新 更多