Последовательность записи или одиночная запись - PullRequest
2 голосов
/ 12 октября 2019

Я программирую на C в системе Unix. Я знаю, что:

write(fd,"ABCD",4);

лучше, чем это делать:

write(fd, "A", 1);
write(fd, "B", 1);
write(fd, "C", 1);
write(fd, "D", 1);

Очевидно, я не говорю о разборчивости кода, но я хочу знать, чтопроблемы, которые может дать вторая методология. Мы говорим об атомарности казни? Если да, то почему?

Спасибо

Ответы [ 3 ]

1 голос
/ 12 октября 2019

Вы правы, что беспокоитесь об атомарности здесь.

Если другой процесс или другой поток в том же процессе также пишет в тот же канал связи, то

write(fd, "ABCD", 4);

будет записывать четыре байта «ABCD» атомарно, в том смысле, что выходные данные других авторов не могут появляться в середине этих четырех байтов. С другой стороны,

write(fd, "A", 1);
write(fd, "B", 1);
write(fd, "C", 1);
write(fd, "D", 1);

будет записывать каждый байт отдельно, а вывод других писателей может появляться между буквами.

Однако атомарность толькогарантировано для short записи, где третий аргумент меньше, чем константа PIPE_BUF. Если вы пишете больше данных, чем это, вы не сможете гарантировать, что выходные данные других авторов не появятся посередине. PIPE_BUF может быть столь же маленьким, как 512, но обычно несколько больше, чем в наши дни.

Самым простым способом убедиться в этом для себя является использование fork и запись в стандартный вывод:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

int main(void)
{
    pid_t pid = fork();
    if (pid < 0) {
        perror("fork");
        return 1;

    } else if (pid == 0) {
        // child
        write(1, "ABCD", 4);  sleep(1);
        write(1, "A", 1);     sleep(1);
        write(1, "B", 1);     sleep(1);
        write(1, "C", 1);     sleep(1);
        write(1, "D", 1);
        return 0;

    } else {
        // parent
        write(1, "1234", 4);  sleep(1);
        write(1, "1", 1);     sleep(1);
        write(1, "2", 1);     sleep(1);
        write(1, "3", 1);     sleep(1);
        write(1, "4", 1);

        waitpid(pid, 0, 0);
        write(1, "\n", 1);
        return 0;
    }
}

Эта программа напечатает что-то вроде 1234ABCD1AB23CD4. Первый ABCD всегда будет отображаться как группа, а первый 1234 всегда будет отображаться как группа, но любой из них может быть первым;вторые ABCD и 1234 могут быть произвольно перепутаны.

(Вопрос теста: почему я поставил окончательный write(1, "\n", 1) в родительском, после * waitpid?)

0 голосов
/ 12 октября 2019

POSIX требует, чтобы write системный вызов был атомарным в 2.9.7 Взаимодействие потоков с обычными файловыми операциями :

Если два потока каждый вызывают одну из этих функций [write является одним из них], каждый вызов должен видеть либо все указанные эффекты другого вызова, либо ни один из них.

Однако, write не требуется для записи всей запрашиваемой суммы. Максимальный размер атомарных записей зависит от файловой системы. Для каналов это PIPE_BUF.

Обратите внимание, что write является системным вызовом, и эти вызовы намного дороже, чем обычные вызовы. Итак, популярный метод заключается в том, чтобы сначала записать в буфер, а когда буфер заполнен или его необходимо очистить немедленно, только затем вызвать write syscall. Это то, что библиотека CI / O fwrite делает для вас.

Следовательно, вы можете предпочесть свести к минимуму количество вызовов write.

0 голосов
/ 12 октября 2019

Проблема заключается просто в эффективности: каждый вызов требует помещения параметров в стек, затем их извлечения в функции, а затем возврата. Все это повторяется без необходимости при повторном вызове.

...