티스토리 뷰

개발/C,C++

File Monitor - Thread + HASH 방식

-=HaeJuK=- 2024. 12. 16. 13:50
728x90
CFileMon - Windows & POSIX 구현

CFileMon 클래스 설계 및 구현

개요

CFileMon 클래스는 특정 파일의 변경 사항을 감지하며, Windows와 POSIX 환경 모두에서 작동하도록 설계되었습니다. Named Mutex를 활용하여 다중 프로세스 동기화를 보장하며, 파일이 삭제되거나 수정되는 상황에 안정적으로 대응합니다.

주요 기능

  • Windows 및 POSIX 환경 지원
  • Named Mutex를 활용하여 다중 프로세스 동기화
  • 파일 변경 시 해시를 비교하여 변경 여부 감지
  • 파일이 삭제되거나 접근 불가한 경우 예외 처리
  • 주기적인 파일 상태 확인 (스레드 기반 구현)

플랫폼별 구현

Windows

  • Named Mutex를 CreateMutexA로 생성
  • WaitForSingleObjectReleaseMutex로 동기화 관리
  • 파일 존재 확인은 std::filesystem::exists 사용

POSIX

  • Named Mutex는 pthread_mutexshm_open으로 구현
  • 공유 메모리를 사용해 다중 프로세스 간 동기화 관리
  • 파일 존재 확인은 stat API 사용

코드

아래는 Windows와 POSIX 환경 모두를 지원하는 CFileMon 클래스의 구현 예제입니다:


#include <iostream>
#include <fstream>
#include <string>
#include <thread>
#include <atomic>
#include <chrono>
#include <mutex>
#include <stdexcept>

#ifdef _WIN32
#include <windows.h>
#include <filesystem>
#include <openssl/sha.h>
#else
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <openssl/sha.h>
#endif

class CFileMon {
public:
    CFileMon(const std::string& filePath, const std::string& mutexName, int intervalMs)
        : filePath(filePath), mutexName(mutexName), intervalMs(intervalMs), stopFlag(false) {
#ifdef _WIN32
        hMutex = CreateMutexA(nullptr, FALSE, mutexName.c_str());
        if (hMutex == nullptr) {
            throw std::runtime_error("Failed to create mutex");
        }
#else
        shmFd = shm_open(mutexName.c_str(), O_CREAT | O_RDWR, 0666);
        if (shmFd < 0) {
            throw std::runtime_error("Failed to create shared memory");
        }
        ftruncate(shmFd, sizeof(pthread_mutex_t));
        sharedMutex = static_cast<pthread_mutex_t*>(mmap(nullptr, sizeof(pthread_mutex_t), PROT_READ | PROT_WRITE, MAP_SHARED, shmFd, 0));
        if (sharedMutex == MAP_FAILED) {
            throw std::runtime_error("Failed to map shared memory");
        }
        pthread_mutexattr_init(&mutexAttr);
        pthread_mutexattr_setpshared(&mutexAttr, PTHREAD_PROCESS_SHARED);
        pthread_mutex_init(sharedMutex, &mutexAttr);
#endif
    }

    ~CFileMon() {
        stopMonitoring();
#ifdef _WIN32
        if (hMutex) CloseHandle(hMutex);
#else
        if (sharedMutex) {
            pthread_mutex_destroy(sharedMutex);
            munmap(sharedMutex, sizeof(pthread_mutex_t));
        }
        if (shmFd >= 0) {
            close(shmFd);
            shm_unlink(mutexName.c_str());
        }
        pthread_mutexattr_destroy(&mutexAttr);
#endif
    }

    void startMonitoring() {
        stopFlag = false;
        monitorThread = std::thread(&CFileMon::monitorFile, this);
    }

    void stopMonitoring() {
        stopFlag = true;
        if (monitorThread.joinable()) monitorThread.join();
    }

private:
    std::string filePath, mutexName;
    int intervalMs;
    std::atomic<bool> stopFlag;
    std::thread monitorThread;
    std::string lastHash;
    std::mutex localMutex;

#ifdef _WIN32
    HANDLE hMutex;
#else
    int shmFd;
    pthread_mutex_t* sharedMutex;
    pthread_mutexattr_t mutexAttr;
#endif

    std::string calculateHash(const std::string& filePath) {
        std::ifstream file(filePath, std::ios::binary);
        if (!file.is_open()) throw std::runtime_error("File cannot be opened");

        SHA256_CTX sha256;
        SHA256_Init(&sha256);
        char buffer[4096];
        while (file.good()) {
            file.read(buffer, sizeof(buffer));
            SHA256_Update(&sha256, buffer, file.gcount());
        }

        unsigned char hash[SHA256_DIGEST_LENGTH];
        SHA256_Final(hash, &sha256);

        std::ostringstream result;
        for (unsigned char c : hash) {
            result << std::hex << std::setw(2) << std::setfill('0') << (int)c;
        }
        return result.str();
    }

    void monitorFile() {
        while (!stopFlag) {
#ifdef _WIN32
            if (WaitForSingleObject(hMutex, INFINITE) == WAIT_OBJECT_0) {
#else
            if (pthread_mutex_lock(sharedMutex) == 0) {
#endif
                std::lock_guard<std::mutex> lock(localMutex);
#ifdef _WIN32
                if (std::filesystem::exists(filePath)) {
#else
                struct stat buffer;
                if (stat(filePath.c_str(), &buffer) == 0) {
#endif
                    std::string currentHash = calculateHash(filePath);
                    if (lastHash != currentHash) {
                        lastHash = currentHash;
                        onFileChanged();
                    }
                } else {
                    onFileDeleted();
                }
#ifdef _WIN32
                ReleaseMutex(hMutex);
#else
                pthread_mutex_unlock(sharedMutex);
#endif
            }
            std::this_thread::sleep_for(std::chrono::milliseconds(intervalMs));
        }
    }

    void onFileChanged() {
        std::cout << "File changed! Reading file...\n";
    }

    void onFileDeleted() {
        std::cout << "File deleted or not accessible. Waiting for recreation...\n";
    }
};

int main() {
    const std::string filePath = "example.txt";
    const std::string mutexName = "CFileMonMutex";
    const int intervalMs = 1000;

    try {
        CFileMon fileMonitor(filePath, mutexName, intervalMs);
        fileMonitor.startMonitoring();
        std::this_thread::sleep_for(std::chrono::seconds(10));
        fileMonitor.stopMonitoring();
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << "\n";
    }

    return 0;
}
    

결론

CFileMon 클래스는 Named Mutex를 사용해 Windows와 POSIX 환경에서 안정적인 파일 모니터링을 제공합니다. 이 코드를 기반으로 다중 프로세스 간 안전한 파일 접근을 구현할 수 있습니다.

반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함
250x250