Каким образом в C++/Linux можно создать файл, только если его не существует (атомарно)?

«Каким образом в C++/Linux можно создать файл, только если его не существует (атомарно)?» — вопрос из категории Linux и ОС, который задают на 25% собеседований C/C++ Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Для атомарного создания файла только при его отсутствии (чтобы избежать состояния гонки между проверкой exists и созданием) необходимо использовать низкоуровневые системные вызовы или флаги файловой системы.

1. Использование системного вызова open() с флагами O_CREAT | O_EXCL (POSIX, наиболее надёжно): Этот метод атомарен даже в многопоточной и многопроцессной среде.

#include <fcntl.h>
#include <unistd.h>
#include <cerrno>
#include <iostream>

int fd = open("myfile.txt", O_CREAT | O_EXCL | O_WRONLY, 0644);
if (fd == -1) {
    if (errno == EEXIST) {
        std::cout << "Файл уже существует.n";
    } else {
        perror("open failed");
    }
} else {
    std::cout << "Файл успешно создан.n";
    close(fd); // Не забываем закрыть дескриптор
}

2. Использование std::ofstream с флагами (C++17, но не полностью атомарно на всех файловых системах):

#include <fstream>
#include <iostream>

std::ofstream file("myfile.txt", std::ios::out | std::ios::app); // app не перезаписывает
// Но это не гарантирует, что файл не существовал до этого.
// Более точный, но всё же не атомарный способ (C++17):
#include <filesystem>
namespace fs = std::filesystem;
if (!fs::exists("myfile.txt")) {
    std::ofstream new_file("myfile.txt");
}
// !ВНИМАНИЕ: Между проверкой exists и созданием файла другой процесс может успеть его создать.

Вывод: Для гарантированно атомарного создания в конкурентной среде следует использовать системный вызов open() с флагами O_CREAT | O_EXCL.