LINUX.ORG.RU

rename не атомарный?

 , ,


0

3

На SO пишут, в манах пишут — да везде полагается, что rename() — атомарная операция.

Однако, пишу сохранение файла:

int Image_write_jpg(const Image *I, const char *name, int eq){
    if(!I || !I->data) return 0;
    uint8_t *outp = NULL;
    if(eq)
        outp = equalize(I, 1, GP->throwpart);
    else
        outp = linear(I, 1);
    DBG("Try to write %s", name);
    char *tmpnm = MALLOC(char, strlen(name) + 5);
    sprintf(tmpnm, "%s-tmp", name);
//    char *tmpnm = tmpnam_r(buf);
    int r = stbi_write_jpg(tmpnm, I->width, I->height, 1, outp, 95);
    if(r){
        if(rename(tmpnm, name)){
            WARN("rename()");
            r = 0;
        }
    }
    FREE(tmpnm);
    FREE(outp);
    return r;
}
Запускаю feh -R 0.3 outpWcrosses.jpg и что же вижу? То сообщения "feh WARNING: outpWcrosses.jpg - Does not look like an image (magic bytes missing)", то "feh WARNING: Couldn't reload image. Is it still there?". И на изображении периодически кусок отсутствует (залит серым).

(закомментированная строчка — это то, что я вначале пытался сделать, с временным файлом в оперативе; однако, когда начала эта непонятная фигня с неатомарностью происходить, решил попробовать в пределах одной ФС, но не помогло)

Что это за Ë-моë?

☆☆☆☆☆

А rename-то тут при чём?

P.S. Замени все теги на «тупак».

LamerOk ★★★★★
()

Все должно работать. Но очень сильно настораживает

закомментированная строчка — это то, что я вначале пытался сделать, с временным файлом в оперативе

Потому что rename не может перемещать файл между разными ФС.

Я бы порекомендовал натравить strace на твое приложение и на feh, дабы понять, что происходит.

kawaii_neko ★★★★
()
Ответ на: комментарий от kawaii_neko

rename не может перемещать файл между разными ФС

В мане сказано:

rename() renames a file, moving it between directories if required.
что я истолковал и как "moving it between partitions".

Я бы порекомендовал натравить strace

Боюсь, утону в выхлопе: все довольно-таки шустро происходит.

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Eddy_Em

что я истолковал и как «moving it between partitions».

Оно даже более ограничено:

EXDEV   oldpath  and  newpath  are  not on the same mounted filesystem.  (Linux permits a filesystem to be mounted at multiple points, but rename() does not work across different mount points, even if the same filesystem is mounted on both.)
xaizek ★★★★★
()
Ответ на: комментарий от xaizek

О, сорян: на этом новом компе я забыл мусорку настроить в tmpfs, так что, она на том же самом разделе.

Получается, у меня и изначально rename работал как бы нормально. Сейчас strace соберется, и я попробую в его дебрях продраться к истине.

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Eddy_Em

Итак, вот что происходит при записи одного из файлов:

openat(AT_FDCWD, "object-tmp", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 12
fstat(12, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
write(12, "\377\330\377\340\0\20JFIF\0\1\1\0\0\1\0\1\0\0\377\333\0\204\0\2\1\1\1\1\1\2"..., 4096) = 4096
... (куча write)
close(12)                               = 0
rename("object-tmp", "object")          = 0
А вот feh сильно мусорит сисвызовами poll, writev, recvmsg. И зачем-то делает уйму stat подряд:
access("outpWcrosses.jpg", R_OK)        = 0
stat("outpWcrosses.jpg", {st_mode=S_IFREG|0644, st_size=42187, ...}) = 0
stat("outpWcrosses.jpg", {st_mode=S_IFREG|0644, st_size=42187, ...}) = 0
stat("outpWcrosses.jpg", {st_mode=S_IFREG|0644, st_size=42187, ...}) = 0
stat("outpWcrosses.jpg", {st_mode=S_IFREG|0644, st_size=42187, ...}) = 0
stat("outpWcrosses.jpg", {st_mode=S_IFREG|0644, st_size=42187, ...}) = 0
stat("/usr/lib64/imlib2/loaders", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
stat("/usr/lib64/imlib2/loaders", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
openat(AT_FDCWD, "outpWcrosses.jpg", O_RDONLY) = 4
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
fstat(4, {st_mode=S_IFREG|0644, st_size=42187, ...}) = 0
read(4, "\377\330\377\340\0\20JFIF\0\1\1\0\0\1\0\1\0\0\377\333\0\204\0\2\1\1\1\1\1\2"..., 4096) = 4096
... (куча read) ...
close(4)                                = 0
А вот так (если я вовремя ctrl+C нажал) — в случае, когда считана только часть файла:
stat("outpWcrosses.jpg", {st_mode=S_IFREG|0644, st_size=42187, ...}) = 0
openat(AT_FDCWD, "outpWcrosses.jpg", O_RDONLY) = 4
fstat(4, {st_mode=S_IFREG|0644, st_size=42187, ...}) = 0
read(4, "\377\330\377\340\0\20JFIF\0\1\1\0\0\1\0\1\0\0\377\333\0\204\0\2\1\1\1\1\1\2"..., 4096) = 4096
close(4)                                = 0

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Eddy_Em

А вот так (если я вовремя ctrl+C нажал) — в случае, когда считана только часть файла

Дичь какая-то — в трейсе нет и намека на неатомарность — размер сразу нормальный, но feh читакт 4 кб и на этом останавливается.

kawaii_neko ★★★★
()

А ты вызываешь fsync()? Я не вижу в коде stbi_write_jpg() принудительного сброса данных на диск.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

не вижу в коде stbi_write_jpg() принудительного сброса данных на диск

и слава б-гу

anonymous
()

Что это за Ë-моë?

Вот и я о том же. Что это за кусок говна с ни о чем не говорящими именами и без документации? %)

Nervous ★★★★★
()
Ответ на: комментарий от i-rinat

У меня на телескопах куча демонов работает, которые таким же способом обновляют файлы для вендовозных машин: записал временный файл, закрыл его, потом сделал rename. Проверял — проблем не было.

Но, думаю, похоже, проблем не было из-за того, что у меня все те временные файлы имели размер меньше стандартного буфера в 4кБ. Попробую в следующий раз, как до работы доберусь (числа 17, наверное, только), воткнуть там таки fsync. Авось, получится...

Честно говоря, я наивно полагал, что если rename делается из того же процесса, в котором пишется файл, то и ядро корректно с файлом поступает. Похоже, это не так.

Вот хоть сиди, да читай исходники ядра ☹

Eddy_Em ☆☆☆☆☆
() автор топика
Последнее исправление: Eddy_Em (всего исправлений: 1)
Ответ на: комментарий от i-rinat

А чем тут должен помочь fsync?

Если не учитывать сбои и внезапные перезагрузки, write+close+rename – корректный способ атомарно обновить файл для того, кто делает open+read (т.е. возможность fsync/fdatasync в нормальное решение всё-таки лучше заложить, но именно из-за сбоев – чтоб не оставить кривой файл с правильным именем). Добавленный fsync тут разве что смажет картину своей длительностью, может даже показаться, что ошибка исчезла.

К сожалению, feh (3.3) два раза делает open() + кучу раз stat, хз зачем, но он может в этом случае заголовок прочитать из одной версии файла, данные из другого и при этом полагаться на размер третьей или четвёртой. Он непоказателен, можно взять другой вьювер или делать хардлинк + feh на хардлинк на «просматривающей» стороне.

Rename(2) «атомарен» за одним исключением, которое тут неважно (в какой-то момент до завершения у файла имеют право быть два имени одновременно, новое и старое).

LeninGad
()
Ответ на: комментарий от LeninGad

Возможно, это именно то, в чем собака порылась!

Похоже, feh и правда сначала делает stat, чтобы выделить буфер памяти, а потом в этот буфер читает файл. И если вдруг он сделал stat на предыдущей версии файла меньшего размера, то в следующий раз внезапно получится косяк. Но какого черта он 4кБ иногда считывает — непонятно, файл заведомо большего размера (30-40кБ).

В общем, разработчик[и] feh намудрил[и], в итоге получилось то, что имеем. В общем, надо патчить гентушный feh, чтобы в нем inotify работал. В этом случае не нужно будет задавать принудительный интервал обновления. Ну или дописать уже свою смотрелку файлов, которую я 3 месяца назад забросил по причине более нужных работ.

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Eddy_Em

Проверял — проблем не было.

Когда вопрос возникает в формулировке «атомарно ли что-то-там», никакие проверки уже не годятся. В том же смысле, что и никакое количество примеров недостаточно для доказательства гипотезы, но одного контрпримера достаточно для опровержения.

И да, я ещё раз почитал тред. Похоже, проблема не в rename, а в какой-то гонке внутри feh. Выше уже писали, что feh открывает файл два раза. Возможно, ожидается, что файл будет тот же самый, но ты успеваешь его подменить.

i-rinat ★★★★★
()
Ответ на: комментарий от LeninGad

А чем тут должен помочь fsync?

fsync помогает, когда хочется надёжности. Если вопрос сформулирован как «rename не атомарный?», а в коде нет fsync, где-то была допущена ошибка.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

С fsync есть большая проблема: иногда он достаточно долго выполняется. А у меня лишние 30мс — уже фатально…

// ну, с 30 загнул, конечно, но 100мс — вообще…

Eddy_Em ☆☆☆☆☆
() автор топика
Последнее исправление: Eddy_Em (всего исправлений: 1)
Ответ на: комментарий от i-rinat

Возможно, ожидается, что файл будет тот же самый

Оказалось, что нет. Первый раз файл открывается, чтобы определить, изображение ли это. Эдакий упрощённый вариант libmagic. А второй раз файл открывает imlib2. Сообщение «Does not look like an image (magic bytes missing)» генерируется самим feh, если его проверка не нашла нужных байт, или если файл оказался слишком короткий.

Хз, что происходит. Имеющихся данных недостаточно для диагностики. Возможно, тебе стоит запатчить feh, навтыкав в него больше логгирования.

i-rinat ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.