ZZZ
Июнь 9, 2010 11:56:04
У меня есть интересный вопрос… Привет всем!
Есть такой кусочек кода:
pid = os.fork()
if pid > 0:
sys.exit(0)
sys.stdout.flush()
sys.stderr.flush()
si = open('/dev/null', 'r')
so = open('/dev/null', 'a+')
se = open('/dev/null', 'a+', 0)
os.dup2(si.fileno(), sys.stdin.fileno())
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())
На момент форка уже присутствует соединение с базой данных (psycopg2) и после форка это соединение активно используется. Проблем пока не заметил, но всё-таки интересно: как вообще форкаются сокеты? И что будет, если я не убью родителя, а продолжу работать с коннектом?
Андрей Светлов
Июнь 9, 2010 16:53:51
Форкаются через dup, как и файлы.
Т.е. с самим сокетом проблемы нет.
Беда может прийти, если родитель и потомок пытаются выполнить запрос к БД без взаимной синхронизации. Сервер может не понять, если ему в один сокет будут
пихать одновременно два запроса. А еще у клиента базы данных есть свой state - PGconn из libpq.
При обработке запроса pg_conn изменяется. Это - локальная память процесса, синхронизации я не увидел. Т.е. выбирая, например, результаты запроса в родителе библиотека никак не сигналит об этом потомку. А pg_conn имеет внутренний буфер - примерно как FILE* является надстройкой над int file descriptor.
В итоге - порвется все к чертям.
ZZZ
Июнь 9, 2010 17:20:14
Хм… Спасибо, ясно.
ZZZ
Июнь 9, 2010 17:27:45
Нет. Стоп. Чего-то я запутался.
В моём примере у нас умирает родитель, в котором отрабатывается gc, который закрывает (в теории) соединение. По идее клиент должен послать серверу сообщение о том, что коннект закрыт и потомок, при попытке постучаться туда, ничего хорошего получить не должен.
Или я бред несу?
Андрей Светлов
Июнь 11, 2010 22:38:39
ZZZ, я пока не знаю, что тебе ответить. Питоновская часть проста как пять копеек - и все сводится к libpg.
Я смотрел ее реализацию очень бегло - незнакомый код и все такое. Соединение закрывается на PGfinish - это верно.
Код этой функции шлет сообщение _внутренней_ системе - нужно закрыться. Что дальше - не смотрел, извини.
Если это вопрос жизни или смерти - могу попытаться понять, как эта штука ведет себя в деталях.
Ответ “в общем” - так же, как и C код, использующий libpq. Никаких неожиданностей или трюков, как я вижу.
ZZZ
Июнь 12, 2010 00:40:53
Андрей Светлов
Если это вопрос жизни или смерти
Ой нет, спасибо, это больше академический интерес по принципу: а вдруг кто-нибудь знает. :-)
Всё-таки хочется понимать это лучше.
Теперь про практику. Проблема-таки нашлась. Дело в том, что после форка я делаю два запроса – сначала выборку с довольно долгим (несколько часов) перебором, а потом “update” о том, что всё отработало. Ну ещё “commit”. Трабла возникла в том, что во время апдейта или коммита (там ооочень сложно увидеть трейсбек, поэтому точнее не скажу) происходило падение и апдейт не срабатывал.
Решилось просто созданием нового коннекта после форка.
poltergeist
Июнь 12, 2010 00:51:34
ZZZ
в котором отрабатывается gc, который закрывает (в теории) соединение.
Не, в теории как раз соединением владеют два процесса, и оно закроется только тогда, когда оба процесса освободят этот ресурс.
З.Ы. Хотя мне не мешало бы освежить свои знания из области системного программирования, могу в чём-то ошибаться.
ZZZ
Июнь 12, 2010 22:54:05
poltergeist
Не, в теории как раз соединением владеют два процесса, и оно закроется только тогда, когда оба процесса освободят этот ресурс.
С точки зрения сокета да, но с точки зрения более высокоуровневой части… Я думаю, что в норме соединение должно закрываться не убийством сокета, а посыланием пакета (сообщения), говорящим о том, что соединение закончено. Вот тут и проблема. :-)
Андрей Светлов
Июнь 14, 2010 19:02:53
ZZZ
С моей точки зрения не стоит надеятся, что fork нормально отработает на объектах сложнее файла, сокета или shared memory. Т.е. новое подключение в потомке - это идеологически правильно.
fork отлично работает с объектами уровня ядра. Если поверх этого объекта (а то и нескольких - канал связи, объекты синхронизации) что-то надстроили в user space - результат в общем случае непредсказуемый. Если только заранее не была предусмотрена эта возможность.
ZZZ
Июнь 14, 2010 19:39:57
Ну вот где-то так я и представил. :-)