【问题标题】:Segfault while closing a ofstream in C++在 C++ 中关闭 ofstream 时出现段错误
【发布时间】:2012-09-12 19:24:53
【问题描述】:

我有一个函数CloseLogFile 在记录器向其写入 10000 行后被调用以关闭日志文件。我将要记录的行存储在std::string 类型的deque 中。这是标题

#pragma once

#include <deque>
#include <string>
#include <fstream>
#include <map>
#include <iostream>
#include <pthread.h>
#include <time.h>

#define MAX_LINES 1000
#define MESSAGES_PER_WRITE 100

class AtlLogger
{
friend class Driver;
friend class OrderManagementSystem;

public:
    static AtlLogger* Instance();

    void Log(const std::string line, const std::string prefix);
    void DeleteInstance();

    void WriteToFile();
private:
    AtlLogger();

    //the pointer versions of logging is reserved for internal use
    //we don't want a strategy to log with pointers and deal with
    //memory management
    void Log(const std::string*);
    void Log(const std::string*, std::string prefix);

    struct LogRequest
    {
        const std::string* line;
        std::string prefix;
    };

    struct FileInfo
    {
        std::string* name;
        std::ofstream ofs;
        int lines;
    };

    static AtlLogger* instance;

    void OpenLogFile(const std::string&);
    void CloseLogFile(const std::string&);

    bool run;

    std::deque<LogRequest*> message_queue;
    std::map<std::string, FileInfo*> file_map;
};

这是 .cpp 文件:

#include "AtlLogger.h"

AtlLogger* AtlLogger::instance = NULL;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

using std::cout;
using std::endl;

/*
* @construct
* @param
* @description  creates a logger to record system information
*/
AtlLogger::AtlLogger()
{
std::string prefix("Audit");
OpenLogFile(prefix);
run = true;
}

/*
* @return instance: pointer to singleton class
* @description  creates an instance of the singleton
*       if it does not already exist
*/
AtlLogger* AtlLogger::Instance()
{
if(instance == NULL)
{
    instance = new AtlLogger;
}

return instance;
}

/*
* @param
* @return
* @description  deletes the logger after closing all IO
*/
void AtlLogger::DeleteInstance()
{
usleep(100000);
pthread_mutex_lock(&mutex);
run = false;
std::map<std::string, FileInfo* >::iterator it;
for (it = file_map.begin(); it != file_map.end(); it++)
{
    //TODO ofstream* file = (*file_it).second;
    //file->close();
}
pthread_mutex_unlock(&mutex);

delete instance;
instance = NULL;
}

/*
* @param line:  string to be logged
* @return
* @description  adds a line to the queue of lines that 
*       will be written to the log
*/
void AtlLogger::Log(const std::string* line)
{
pthread_mutex_lock(&mutex);
    LogRequest* request = new LogRequest;
    request->line = line;
    request->prefix = "Audit";
    message_queue.push_back(request);
pthread_mutex_unlock(&mutex);
}

/*
* @param line:  string to be logged
* @param name:  name of the file to log with
* @return
* @description  add the line to the given log file
*/
void AtlLogger::Log(const std::string* line, std::string prefix)
{
pthread_mutex_lock(&mutex);
    if (file_map.find(prefix) == file_map.end())
    {
        OpenLogFile(prefix);
    }

    LogRequest* request = new LogRequest;
    request->line = line;
    request->prefix = prefix;
    message_queue.push_back(request);
pthread_mutex_unlock(&mutex);
}

/*
* @param line:  string to be logged
* @param name:  name of the file to log with
* @return
* @description  add the line to the given log file
*/
void AtlLogger::Log(const std::string line, std::string prefix)
{
pthread_mutex_lock(&mutex);
    if (file_map.find(prefix) == file_map.end())
    {
        OpenLogFile(prefix);
    }

    LogRequest* request = new LogRequest;
    request->line = new std::string(line);
    request->prefix = prefix;
    message_queue.push_back(request);
pthread_mutex_unlock(&mutex);
}

/*
* @param
* @return
* @description  runs in its own thread, checking whether it needs
*       to write log statements periodically
*/
void AtlLogger::WriteToFile()
{
    std::map<std::string, FileInfo* >::iterator it;

while(run)
{
    char timestamp[16];
    time_t now;
    time(&now);
    struct tm* current = localtime(&now);
    sprintf(timestamp, "%02u%02u%04u|%02u%02u%02u|", (current->tm_mon+1),
        current->tm_mday,(1900 + current->tm_year), current->tm_hour,
        current->tm_min, current->tm_sec);

    pthread_mutex_lock(&mutex);
        for(it=file_map.begin(); it != file_map.end(); ++it)
        {
            if(it->second->lines > MAX_LINES)
            {
                CloseLogFile(it->first);
                OpenLogFile(it->first);
            }
            else
            {
                int written = 0;

                while(!message_queue.empty() && written < MESSAGES_PER_WRITE)
                {
                    LogRequest* request = message_queue.front();
                    message_queue.pop_front();

                    std::string line(timestamp, 16);
                    line.append(*(request->line));

                    FileInfo* info = file_map[request->prefix];
                    info->ofs << line << std::endl;
                    info->lines++;
                    written++;
                    delete request;
                }
            }
        }
    pthread_mutex_unlock(&mutex);

    usleep(1000);
}
}

/*
* @param
* @return
* @description  opens a new file for logging with a timestamp
*       as the filename
*/
void AtlLogger::OpenLogFile(const std::string& prefix)
{
//get timestamp to use
char timestamp[15];
time_t now;
time(&now);
struct tm* current = localtime(&now);
sprintf(timestamp, "%02u%02u%04u_%02u%02u%02u", (current->tm_mon+1),
    current->tm_mday,(1900 + current->tm_year), current->tm_hour,
    current->tm_min, current->tm_sec);

FileInfo* info = new FileInfo;
cout << "1" << endl;
cout << prefix << endl;
info->name = new std::string("logs/" + prefix + ".log_" + timestamp);
cout << "2" << endl;
cout << "3" << endl;
cout << info->name->c_str() << endl;
info->ofs.open(info->name->c_str());
cout << "4" << endl;
info->lines = 0;
cout << "5" << endl;

file_map[prefix] = info;

cout << "Creating New Log File: " << timestamp << endl;
}

/*
* @param
* @return
* @description  closes the current log file
*/
void AtlLogger::CloseLogFile(const std::string& prefix)
{
cout << "Attempting to Close File!" << endl;
cout << prefix << endl;
cout << "Is Open?: " << file_map[prefix]->ofs.is_open() << endl;
cout << "good?: " << file_map[prefix]->ofs.good() << endl;
cout << "eof?: " << file_map[prefix]->ofs.eof() << endl;
cout << "fail?: " << file_map[prefix]->ofs.fail() << endl;
cout << "bad?: " << file_map[prefix]->ofs.bad() << endl;
cout << "name? " << *file_map[prefix]->name << endl;
cout << "lines? " << file_map[prefix]->lines << endl;
//cout << "rdbuf: " << file_map[prefix]->ofs.rdbuf() << endl;
cout << "rdbuf open?: " << file_map[prefix]->ofs.rdbuf()->is_open() << endl;
file_map[prefix]->ofs.close();
cout << "closed stream" << endl;
delete file_map[prefix];
cout << "deleted memory" << endl;
file_map.erase(prefix);
cout << "Close File End!"<< endl;
}

有时,我的程序段错误,我似乎无法确定这是为什么。它工作了很多次,最终会出现段错误,有时在第一次调用时,有时在以后的多次调用中。

这是我从 gdb 的回溯:

0  0x0000003d8786d1b3 in _IO_un_link_internal () from /lib64/libc.so.6
1  0x0000003d87860da7 in fclose@@GLIBC_2.2.5 () from /lib64/libc.so.6
2  0x000000336febb968 in std::__basic_file<char>::close() () from /usr/lib64/libstdc++.so.6
3  0x000000336fe69c17 in std::basic_filebuf<char, std::char_traits<char> >::close() () 
from /usr/lib64/libstdc++.so.6
4  0x000000336fe69cad in std::basic_ofstream<char, std::char_traits<char> >::close() () from /usr/lib64/libstdc++.so.6
5  0x00000000004c2a25 in AtlLogger::CloseLogFile() ()
6  0x00000000004c2ef1 in AtlLogger::WriteToFile() ()
7  0x0000000000482270 in Driver::launchLog (this=0x7fffffffe86f) at driver.cpp:672
8  0x000000000048228f in launchLogThread (ptr=0x7fffffffe86f) at driver.cpp:654
9  0x0000003d8840673d in start_thread () from /lib64/libpthread.so.0
10 0x0000003d878d3d1d in clone () from /lib64/libc.so.6

这是 seg 错误之前的控制台输出:

Attempting to Close File!
test
Is Open?: 1
good?: 1
eof?: 0
fail?: 0
bad?: 0
name? logs/test.log_09132012_095549
lines? 1001
rdbuf open?: 1

谁能告诉我这可能哪里出错了? (顺便说一句,为什么在 gdb 中我看到跟踪的某些部分的行号,而不是其他部分的行号?)

日志记录功能可用于以下各项: Logger::Instance()-&gt;Log("Log This", "File");

【问题讨论】:

  • 这可能与您的问题完全无关,但在my case 中,它原来是由程序中其他地方的内存损坏引起的(我们继承了一些糟糕的代码)。如果没有其他方法,您可能需要尝试清理程序,如果可以的话。
  • valgrind 是你的朋友。您的程序中的其他地方有错误,valgrind 会为您找到它。或者,尝试将您的程序缩减为仍然出错的最小程序,例如 sscce.org
  • 什么是files["default"]?我以前没见过。
  • 看不出有什么问题,所以我要离开 KRyan 的回答,内存损坏。 valgrind 是你想要的。
  • @wallyk 它只是一个 std::map<:string std::ofstream>

标签: c++ file ofstream


【解决方案1】:

这是一个奇特而不同寻常的架构。

(显然)有一个全局变量 files[] 包含一个预先打开的文件。

CloseLogFile() 使用files["default"] 然后将其关闭。如果CloseLogFile() 再次关闭会怎样?该函数不检查它是否已经被关闭。如果其他逻辑访问files["default"],它会在假设它打开之前检查它是否打开?

让日志文件成为一个自包含的对象——即不依赖非成员函数来关闭它——或者让其内容更“正常”(如大多数程序员所期望的那样)数组不是指针,而是指向的对象,因此删除数组会导致自动清理。

【讨论】:

    【解决方案2】:

    从堆栈跟踪看起来您正在登录一个线程。您看到的条件可能是当您在记录器线程中输入 CloseLogFile() 时,ofstream* 在主线程中被删除或失效的竞争条件。

    您可能需要同步(同时?)对 CloseLogFile() 的调用以及主线程中的任何清理逻辑。

    如果你发布你的 main() 代码,我也许可以给你确切的修复/建议

    【讨论】:

    • 请发布显示线程工作流程的代码...即您如何分离日志线程、如何提交日志记录等。
    猜你喜欢
    • 2019-12-31
    • 1970-01-01
    • 1970-01-01
    • 2021-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多