Как ядро ​​знает, что представляет собой текущий поток?

Может кто-нибудь, пожалуйста, объясните мне этот фрагмент кода здесь, взятый из ядра 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, что функция не имеет побочных эффектов - на самом деле она немного сильнее: функция использует только аргументы и возвращает значение, основанное только на этих аргументах. Это может позволить себе некоторые возможности оптимизации для компилятора - он может предположить, что никакие глобальные переменные не изменяются или даже не читаются (поэтому компилятор мог бы отложить обновление памяти, которое может потребоваться обновить для "нормальных" вызовов функций).