LinuxForum.Fans
Тема

Загадка: 'Почтальон всегда стучит дважды' (printf и fork)

Автор: inneska 29 сообщений 7206 просмотров
#41966
Hvzh написал:
По мне тут все просто. Потомок наследует все окружение родителя в том числе буфера. Поскольку после первого вызова printf последовал вызов fork, первый тук остался в буфере и передался дочернему процессу. Дочерний процесс прицепил свой тук к уже имеющемуся и распечатал вместо одного тука два. Одно из решений - вставить строку fflush(stdout) перед вызовом форка. Кстати, весь процесс очень хорошо виден если все туки пронумеровать (tuk1, tuk2, tuk3)

Я всё это и понял после чтения "man fork", вопрос был в том, как сбросить буфер.
tcflush почему-то этого не делает, fflush сбрасывает.
А вот добиться сброса буфера из потомка мне не удалось.

Пётр.

#41968

Peter,
Сорри, я просто не читал весь флейм сразу, а прочитал задачу, написал ответ, и только сейчас все перечитал

#42186
inneska написал:
Что по твоему кривого в коде? Скажи где конкретно кривизна.

дык...

inneska написал:
Главное только понять где и почему надо принудительно сбросить буфер.

дело вот в чём: при запуске двух потоков очень важно проследить, что-бы они не использовали общих ресурсов, а если это необходимо, то нужно как-либо заблокировать этот ресурс. невыполнение этого постого правила ведёт к очень сложно выявляемым багам, вроде твоего. Проблема этого кода вот в чём: тут printf выполняется действительно трижды, однако есть одна тонкость: в первый раз выполняются одновременно две printf. Ну мало того, что саму printf так нельзя использовать, так у вас эта функция ещё и пишет в один и тот-же поток! Не удивительно, что программу начинает жестоко глючить. В данном случае вовсе не нужно сбрасывать буфер, точнее нужно. Одним потоком надо его сбросить, а второй должен ждать этого сброса, что-бы после этого начать запись.

Здесь всё просто, потому глюк простой и воспроизводимый. В реальной жизни это приведёт к совсем плачевным последствиям: твоя программа при тестировании будет вести себя замечательно, однако у пользователя она будет жутко глючить. Писать такой код == плохая идея.

Peter написал:
А вот добиться сброса буфера из потомка мне не удалось.

удалось. только это было незаметно. наверное ваш потомок выполнял сброс чуть позже того, как родительский процесс уже записал туда своё. Повторяю - процессы протекают одновременно.

Hvzh написал:
По мне тут все просто. Потомок наследует все окружение родителя в том числе буфера.

так будет в том случае, если буфера все находятся в стеке. однако для printf ЕМНИП этого никто не гаранирует - буфера вполне могут находится в другой памяти, и потому два процесса будут писать в одно и тоже место. а так, как они работают одновременно, то результат, вообще говоря, будет не определён...

#42258

drBatty

и потому два процесса будут писать в одно и тоже место

ИМХО, тогда это ошибка архитектуры ОС... Или программера...

#42264
Hvzh написал:
ИМХО, тогда это ошибка архитектуры ОС... Или программера...

программера. или стандартной библиотеки. просто не надо юзать здесь printf (кстати в мане об этом написано).

#42363
drBatty написал:

[quote=Peter]А вот добиться сброса буфера из потомка мне не удалось.

удалось. только это было незаметно. наверное ваш потомок выполнял сброс чуть позже того, как родительский процесс уже записал туда своё. Повторяю - процессы протекают одновременно.
[/quote]
Я пробовал вставлять sleep(1) в один или другой, но возможно, не нашёл
правильную комбинацию.
Я согласен, что код "кривой", но это же тестовая задача.
А вот почему fflush перед fork сбрасывает буфер, а tcflush там же - нет?

Пётр.

#42365
Peter написал:
А вот почему fflush перед fork сбрасывает буфер, а tcflush там же - нет?

наверное потому, что tcflush вообще не для потоков :)

#42378
drBatty написал:
[quote=Peter]А вот почему fflush перед fork сбрасывает буфер, а tcflush там же - нет?

наверное потому, что tcflush вообще не для потоков :)[/quote]
Действительно, в "man termios" в самом начале сказано,
для чего все эти функции.

Пётр.

#42401
Peter написал:
Действительно, в "man termios" в самом начале сказано,
для чего все эти функции.

если честно - сам никогда не юзал, потому пришлось и мне ман открыть :)

Войдите или зарегистрируйтесь, чтобы ответить.