Как ОС определяет идентификатор потока, вызывающего GetCurrentThreadId()?

Я пытаюсь понять, как ОС определяет, какой поток является текущим (например, когда поток вызывает gettid() или GetCurrentThreadId()). Поскольку адресное пространство процесса является общим для всех потоков, сохранение идентификатора потока в нем невозможно. Он должен быть чем-то уникальным для каждого потока (т. е. храниться в его контексте). Если бы я был разработчиком ОС, я бы сохранил его во внутреннем регистре ЦП, доступном для чтения только в режиме ядра. Я много гуглил, но не нашел подобного вопроса (как будто это было супер очевидно).

Так как же это реализовано в реальных операционных системах, таких как Linux или Windows?


person raiks    schedule 22.03.2018    source источник
comment
TEB (Thread Environment Block) является основной частью среды выполнения. Регистр FS хранит указатель на него. Детали реализации, о которых вам не стоит беспокоиться.   -  person Hans Passant    schedule 22.03.2018
comment
Вы можете поместить его в некоторую структуру в верхней части стека потоков.   -  person Martin James    schedule 22.03.2018
comment
@HansPassant это должен был быть ответ, потому что это действительно объясняет, спасибо.   -  person raiks    schedule 22.03.2018
comment
@MartinJames Я пытаюсь представить твой сценарий. Вероятно, вы имели в виду нижнюю часть стека, а не верхнюю (верхняя часть будет содержать аргументы для gettid()). Теоретически ОС может поместить threadId в нижнюю часть стека при планировании потока (на данный момент она знает threadId), но проблема в том, что указатель стека (SP/ESP/RSP) в x86 содержит адрес вершины стека. и информация для определения дна стека не хранится где-либо еще в регистрах. Поправьте меня если я ошибаюсь.   -  person raiks    schedule 22.03.2018


Ответы (3)


Вы ищете блок управления потоком (TCB).

Это структура данных, содержащая информацию о потоках.

Небольшой материал для чтения по этой теме можно найти здесь: https://www.cs.duke.edu/courses/fall09/cps110/slides/threads2.3.ppt

Но я бы порекомендовал получить копию «Современных операционных систем» Эндрю С. Таненбаума, если вы интересуетесь ОС.

Глава 2, раздел 2.2 Темы:

Реализация потоков в пользовательском пространстве — «Когда потоки управляются в пользовательском пространстве, каждому процессу нужна собственная частная таблица потоков, чтобы отслеживать потоки в этом процессе».

Реализация потоков в ядре — «В ядре есть таблица потоков, которая отслеживает все потоки в системе».

Просто редактирование. Вы также можете прочитать "РАСПИСАНИЕ". В общем случае можно сказать, что ядро ​​решает, какой поток/процесс должен использовать ЦП. Таким образом, ядро ​​знает, какой поток/процесс выполнил системный вызов. Я не буду вдаваться в подробности, потому что это зависит от того, о какой ОС мы говорим.

person ero    schedule 22.03.2018

Я считаю, что это уже очень хорошо объяснено в этом вопросе: как ядро ​​различает поток и обработать

Если вы хотите узнать больше, вы также можете найти в Google структуру задач ядра и посмотрите, какая информация хранится о каждом типе процессов, запущенных в пользовательском пространстве.

person Alex C    schedule 22.03.2018
comment
Я проверил ответы на вопрос, который вы упомянули (а также аналогичные). Существует много жонглирования pid/tid и довольно сложная информация о том, как они хранятся в ядре. Однако ни один из них не отвечает на мой довольно конкретный вопрос. Попробуйте представить себя ядром — что бы вы сделали, если бы вас вызвал поток и вам нужно выяснить, кто вам звонит? :) - person raiks; 22.03.2018
comment
Этот вызов осуществляется с помощью программных прерываний или ловушек. Когда пользовательское приложение выполняет системный вызов, оно заполняет регистр номером системного вызова и выполняет мягкое прерывание. Затем процедура прерывания запускает код ядра для обработки системного вызова, который просматривает структуру task_struct, которая в настоящее время помечена как выполняемая, чтобы узнать pid/tid, правильность, приоритет и т. д. запущенного процесса. Текущая запущенная задача обновляется новой, когда происходит переключение контекста — указатель инструкции задачи передается для выполнения на процессор. - person Alex C; 22.03.2018
comment
Ну, я понимаю, как вообще делается системный вызов. Я также понимаю, как ОС определяет, какой процесс в данный момент активен (одновременно может быть активным только 1 адресное пространство — ОЗУ — это общий ресурс). Однако в N-ядерных системах может быть несколько потоков, принадлежащих одному процессу, выполняющемуся в одно и то же время. Теперь представьте, что один из них вызывает функцию gettid(). Получить PID легко, но как насчет TID? Пока я не вижу, как это можно сделать без использования внутреннего регистра ЦП для хранения идентификатора потока или вызова getcpu() из gettid() и обращения к планировщику. - person raiks; 23.03.2018
comment
В task_struct также есть поле, указывающее, на каком процессоре оно выполнялось в данный момент. Кроме того, у каждого процессора есть собственная очередь запуска, вы можете проверить реализацию в kernel/sched.c. - person Alex C; 23.03.2018

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

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

У вас будет какая-то структура данных для представления текущего процесса, а ядро ​​будет иметь некоторые средства идентификации текущего процесса (например, указатель в адресном пространстве ядра на этот процесс). На некоторых процессорах есть регистр текущей задачи, который указывает на структуру, определенную спецификацией процессора. Операционная система обычно может добавлять свои данные в конец этой структуры.

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

Затем получить идентификатор потока работает так же, как раньше работало получение идентификатора процесса. Но теперь у Get Process ID есть дополнительный шаг, который я должен перевести поток в процесс, чтобы получить его идентификатор (который может даже быть включен в блок потока).

person user3344003    schedule 22.03.2018