Исходный код ядра: что такое parent_tidptr и child_tidptr в do_fork()?

Исходный код ядра: что такое parent_tidptr и child_tidptr в do_fork()?

О вышеизложенномсвязьчто подразумевается под этим parent_tidptrи child_tidptrна основании do_fork()чего создается новый процесс?

решение1

Давайте начнем с рассмотрения интерфейса системных вызовов. Он немного различается в зависимости от архитектуры, но на x86-64 он выглядит так:

   long clone(unsigned long flags, void *child_stack,
              int *ptid, int *ctid,
              unsigned long newtls);

ptidи ctidваши parent_tidptrи child_tidptr. Теперь давайте посмотрим, чтоclone(2)на странице руководства написано:

   CLONE_CHILD_CLEARTID (since Linux 2.5.49)
          Erase  the  child  thread  ID  at the location ctid in
          child memory when the child exits, and do a wakeup  on
          the  futex  at that address.

   CLONE_CHILD_SETTID (since Linux 2.5.49)
          Store  the child thread ID at the location ctid in the
          child's memory.

   CLONE_PARENT_SETTID (since Linux 2.5.49)
          Store  the child thread ID at the location ptid in the
          parent's memory. 

Эти флаги были изначально разработаны для реализации потоковых библиотек. Если мы посмотрим на реализацию NPTLpthread_create()внутри glibc мы в конечном итоге находим код вsysdeps/unix/sysv/linux/createthread.cкоторый делает clone()вызов, включающий CLONE_PARENT_SETTIDи CLONE_CHILD_CLEARTIDв flags.

В этом clone()вызове мы также можем видеть, что аргументы ptidиctidуказывают на тот же адрес. (Помните, что потоки POSIX совместно используют адресное пространство; это достигается с помощью флага clone() CLONE_VM.)

Итак, здесь происходит следующее.

  • CLONE_PARENT_SETTIDиспользуется для обеспечения того, чтобы идентификатор потока ядра сохранялся в определенном месте в пространстве пользователя. Сторона пользовательского пространства реализации потоков должна знать этот идентификатор потока.
  • CLONE_CHILD_CLEARTIDиспользуется для очистки (т. е. обнуления) того же места при clone()завершении потока, созданного .

Давайте пойдем немного дальше...

Идентификатор потока, возвращаемый через ptid/, ctidравеннетто же самое, что и идентификатор потока POSIX ( pthread_t), хотя в реализациях потоков 1:1, таких как NPTL, существует однозначное соответствие между идентификаторами потоков ядра и идентификаторами потоков POSIX. Идентификатор потока ядра — это тот же идентификатор, который вы получаете, используя Linuxgettid()call. Он также возвращается clone()как возвращаемое значение системного вызова, что наводит на вопрос: зачем нам нужен ptid/ ctid? Проблема в том, что со стороны пользовательского пространства все выглядит так:

tid = clone(...);

С точки зрения реализации потоков в пользовательском пространстве здесь имеет место гонка, поскольку присваивание tidпроисходит толькопосле clone()возвращает. Это означает, что потоковая библиотека пользовательского пространства может столкнуться с определенными проблемами, если она захочет получить эту информацию до того, как новый поток что-либо сделает (например, завершит). Использование CLONE_PARENT_SETTIDгарантирует, что новый идентификатор потока будет помещен в место, на которое указываетptid до clone()возвращает и, таким образом, позволяет потоковой библиотеке избегать подобных состояний гонки. ( CLONE_CHILD_SETTIDможет также использоваться для аналогичного эффекта.)

Причина, по которой CLONE_CHILD_CLEARTIDиспользуется очистка ptid/, ctidзаключается в том, чтобы предоставить путь дляpthread_join()позвоните в другой поток, чтобы обнаружить, что поток завершился. По сути, местоположение ptid/ ctidиспользуется какфутекс, иfutex()системный вызов используется для блокировки, ожидая изменения целого числа в этом месте. (Детали немного запутаны, но grepдля использования lll_wait_tidи lll_futex_waitв исходном коде glibc. В конечном счете, происходит FUTEX_WAITоперация. Вспомните выше, что CLONE_CHILD_CLEARTIDделает пробуждение фьютекса по целевому адресу.)

решение2

tidозначает "thread id". Параметры parent_tidptrи child_tidptrуказывают на память пользовательского пространства в адресном пространстве родительского процесса и адресном пространстве дочернего процесса соответственно. Идентификатор вновь созданного потока хранится в переменных int, на которые указывают указатели.

Для получения более подробной информации см.clone(2)страница руководства.

Связанный контент