取消引用映射内存时出现分段错误
Segmentation Fault on dereferencing mapped memory
我 运行 遇到了一个恼人的问题,我无法以任何方式访问从 mmap
获得的内存而不会出现分段错误。
我用来获取映射内存的函数如下所示。
/**
* Preconditions: filename must be verified as referencing a valid file.
*/
char *IOUtils::memory_map_file(string const& filename, size_t length, int open_flags){
int fd = open(filename.c_str(), open_flags | O_CREAT,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
int prot;
if (open_flags == O_RDONLY)
prot = PROT_READ;
else
prot = PROT_READ | PROT_WRITE;
void *output = mmap(NULL, length, prot, MAP_SHARED, fd, 0);
if (output == (void *) -1){
cerr << filename << ": " << strerror(errno) << '\n';
_exit(2);
}
close(fd);
return (char *)output;
}
我的主要功能是这样的。
int main(int argc, char *argv[]){
size_t input_length = IOUtils::file_size(argv[1]); //This works fine
char *input_buffer = IOUtils::memory_map_file(argv[1], input_length,
O_RDONLY); //This succeeds
char *output_buffer = IOUtils::memory_map_file(argv[2], 2*input_length,
O_RDWR); //This succeeds
DomainParser parser(input_length, input_buffer, output_buffer);
while(!parser.finished()){
parser.write_entry();
}
mremap(output_buffer, 2*input_length, MREMAP_MAYMOVE,
parser.bytes_written());
munmap(output_buffer, parser.bytes_written());
}
解析器的相关代码如下所示
void DomainParser::write_entry(void){
char const *in = input(); //Gets position in input file
char const *copy_up_to = end(); //Gets position at input EOF
for(char const *it = in; it < copy_up_to; ++it){
cerr << *it; //SIGSEGV!
if(*it == '\n') break;
}
cerr << '\n';
/* Do writes */
}
程序在 cerr << *it
后立即出现段错误。我不知道为什么会发生这种情况,考虑到所有映射内存都配备了读取权限并已成功分配。
编辑:如果有人怀疑 class 有问题,这里是完整的源代码。
using std::stringstream;
using std::string;
class DomainParser{
size_t _input_offset;
const size_t _input_length;
size_t _output_offset;
char const *input_buffer;
char *output_buffer;
char const *input(void){
return input_buffer + _input_offset;
}
char *output(void){
return output_buffer + _output_offset;
}
char const* end(void){
return input_buffer + _input_length;
}
char const *find(char const *begin, char const *max, char c){
while (*begin != c){
cerr << *begin++;
}
cerr << c;
return begin;
}
public:
DomainParser(size_t length, char const *input, char *output) :
_input_length(length), input_buffer(input), output_buffer(output)
{}
bool finished(void){
return _input_offset == _input_length;
}
size_t bytes_written(void){
return _output_offset;
}
size_t write_entry(void){
if (finished()){
return 0;
}
char const *in = input();
char const *copy_up_to = find(in, end(), '\n');
size_t input_entry_length = copy_up_to - in;
string s(in, copy_up_to);
stringstream ss(s);
string name, type, host;
ss >> name >> type >> host;
if (!ss){
cerr << s << '\n';
_input_offset += input_entry_length;
return 0;
}
ss.str(""); ss.clear();
ss << "{\"name\":\"" << name << "\"," <<
"\"host\":\"" << host << "\"," <<
"\"type\":\"" << type << "\"}\n";
string entry = ss.str();
std::memcpy(output(), entry.c_str(), entry.size());
_input_offset += input_entry_length;
_output_offset += entry.size();
return entry.size();
}
};
我没有看到 _input_offset
的任何初始化。
如果修复该问题,您将 运行 遇到输出文件为空的问题,因此访问任何页面都会触发 SIGBUS
信号。您需要使用 ftruncate
将其调整为预期大小(可能与映射的大小相匹配,但这取决于您要执行的操作)。
也不是说 munmap
可能非常昂贵(尤其是在大型系统上),因此内存映射 I/O 仅在文件非常大时才有效。
我 运行 遇到了一个恼人的问题,我无法以任何方式访问从 mmap
获得的内存而不会出现分段错误。
我用来获取映射内存的函数如下所示。
/**
* Preconditions: filename must be verified as referencing a valid file.
*/
char *IOUtils::memory_map_file(string const& filename, size_t length, int open_flags){
int fd = open(filename.c_str(), open_flags | O_CREAT,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
int prot;
if (open_flags == O_RDONLY)
prot = PROT_READ;
else
prot = PROT_READ | PROT_WRITE;
void *output = mmap(NULL, length, prot, MAP_SHARED, fd, 0);
if (output == (void *) -1){
cerr << filename << ": " << strerror(errno) << '\n';
_exit(2);
}
close(fd);
return (char *)output;
}
我的主要功能是这样的。
int main(int argc, char *argv[]){
size_t input_length = IOUtils::file_size(argv[1]); //This works fine
char *input_buffer = IOUtils::memory_map_file(argv[1], input_length,
O_RDONLY); //This succeeds
char *output_buffer = IOUtils::memory_map_file(argv[2], 2*input_length,
O_RDWR); //This succeeds
DomainParser parser(input_length, input_buffer, output_buffer);
while(!parser.finished()){
parser.write_entry();
}
mremap(output_buffer, 2*input_length, MREMAP_MAYMOVE,
parser.bytes_written());
munmap(output_buffer, parser.bytes_written());
}
解析器的相关代码如下所示
void DomainParser::write_entry(void){
char const *in = input(); //Gets position in input file
char const *copy_up_to = end(); //Gets position at input EOF
for(char const *it = in; it < copy_up_to; ++it){
cerr << *it; //SIGSEGV!
if(*it == '\n') break;
}
cerr << '\n';
/* Do writes */
}
程序在 cerr << *it
后立即出现段错误。我不知道为什么会发生这种情况,考虑到所有映射内存都配备了读取权限并已成功分配。
编辑:如果有人怀疑 class 有问题,这里是完整的源代码。
using std::stringstream;
using std::string;
class DomainParser{
size_t _input_offset;
const size_t _input_length;
size_t _output_offset;
char const *input_buffer;
char *output_buffer;
char const *input(void){
return input_buffer + _input_offset;
}
char *output(void){
return output_buffer + _output_offset;
}
char const* end(void){
return input_buffer + _input_length;
}
char const *find(char const *begin, char const *max, char c){
while (*begin != c){
cerr << *begin++;
}
cerr << c;
return begin;
}
public:
DomainParser(size_t length, char const *input, char *output) :
_input_length(length), input_buffer(input), output_buffer(output)
{}
bool finished(void){
return _input_offset == _input_length;
}
size_t bytes_written(void){
return _output_offset;
}
size_t write_entry(void){
if (finished()){
return 0;
}
char const *in = input();
char const *copy_up_to = find(in, end(), '\n');
size_t input_entry_length = copy_up_to - in;
string s(in, copy_up_to);
stringstream ss(s);
string name, type, host;
ss >> name >> type >> host;
if (!ss){
cerr << s << '\n';
_input_offset += input_entry_length;
return 0;
}
ss.str(""); ss.clear();
ss << "{\"name\":\"" << name << "\"," <<
"\"host\":\"" << host << "\"," <<
"\"type\":\"" << type << "\"}\n";
string entry = ss.str();
std::memcpy(output(), entry.c_str(), entry.size());
_input_offset += input_entry_length;
_output_offset += entry.size();
return entry.size();
}
};
我没有看到 _input_offset
的任何初始化。
如果修复该问题,您将 运行 遇到输出文件为空的问题,因此访问任何页面都会触发 SIGBUS
信号。您需要使用 ftruncate
将其调整为预期大小(可能与映射的大小相匹配,但这取决于您要执行的操作)。
也不是说 munmap
可能非常昂贵(尤其是在大型系统上),因此内存映射 I/O 仅在文件非常大时才有效。