Как ядро знает, что представляет собой текущий поток?
Может кто-нибудь, пожалуйста, объясните мне этот фрагмент кода здесь, взятый из ядра linux?
/*
* how to get the thread information struct from C
*/
static inline struct thread_info *current_thread_info(void) __attribute_const__;
static inline struct thread_info *current_thread_info(void)
{
register unsigned long sp asm ("sp");
return (struct thread_info *)(sp & ~(THREAD_SIZE - 1));
}
Вопросы:
- что такое
__attribute_const__
?
- что это делает
register unsigned long sp asm ("sp");
- почему
(struct thread_info *)(sp & ~(THREAD_SIZE - 1));
возвращает
указатель на структуру?
Ответы
Ответ 1
-
Атрибут const
означает, что возвращаемый указатель останется неизменным в течение всего времени программы. На практике это верно только в области одного потока, но я не могу придумать никакой ситуации, когда компилятор даже попытается оптимизировать обращения между потоками.
-
Использование register
и asm("sp")
связывает переменную с аппаратным регистром с именем sp
, то есть текущим указателем стека. Таким образом, код не должен быть написан на ассемблере для прямого доступа к этому регистру.
-
THREAD_SIZE - это константа, которая дает объем памяти, выделенный для стека потоков. Я предполагаю, что он всегда должен быть силой 2, например. 8 килобайт может быть типичным значением.
Выражение ~(THREAD_SIZE - 1)
затем дает битмаску для избавления от фактического адреса стека. Для стека 8 КБ это будет 0xffffe000
.
Принимая поразрядное и со значением указателя стека, мы получаем самый низкий адрес, выделенный для стека. В этой архитектуре информация о потоке хранится там. Это просто дизайнерское решение, они могли бы использовать другое место для хранения информации.
Указатель стека полезен для получения информации о потоке, поскольку каждый поток всегда имеет свой собственный стек.
Ответ 2
Стеки ядра в Linux имеют фиксированный размер (THREAD_SIZE
- 2 страницы или 8 Кбайт на x86). struct thread_info
для потока хранится в нижней части блока памяти стека. Имейте в виду, что стек работает вниз, поэтому указатель стека изначально указывает на конец блока памяти и по мере того, как данные вставляются в стек, указатель стека перемещается к нижней части блока памяти. Конечно, другие архитектуры процессоров могут использовать другие методы.
Итак, если вы берете текущее значение указателя стека и маскируете биты более низкого порядка, вы получаете указатель на struct thread_info
для потока с использованием текущего стека.
Строка:
register unsigned long sp asm ("sp");
указывает GCC сопоставить переменную sp
в регистре CPU sp
(мне кажется странным, что здесь используется 16-битное имя регистра - это из фактического дерева исходных файлов Linux?).
__attribute_const__
обычно определяется как __attribute__((__const__))
, когда GCC является компилятором (это когда-либо еще для ядра linux?). Это говорит GCC, что функция не имеет побочных эффектов - на самом деле она немного сильнее: функция использует только аргументы и возвращает значение, основанное только на этих аргументах. Это может позволить себе некоторые возможности оптимизации для компилятора - он может предположить, что никакие глобальные переменные не изменяются или даже не читаются (поэтому компилятор мог бы отложить обновление памяти, которое может потребоваться обновить для "нормальных" вызовов функций).