Я не могу однозначно ответить на вопрос "темы ядра" для Linux. Что касается Windows, я могу вам сказать, что "потоки ядра" - это просто потоки, созданные из некоторой другой подпрограммы режима ядра, выполняющие процедуры, которые никогда не переходят в режим пользователя. Когда планировщик выбирает поток для выполнения, он возобновляет свое предыдущее состояние (пользователь или ядро, что бы это ни было); ЦПУ не нужно "различать". Поток выполняется в режиме ядра, потому что это то, что он делал в прошлый раз.
В Windows они, как правило, создаются с помощью так называемого "системного" процесса в качестве родителя, но на самом деле они могут быть созданы в любом процессе. Итак, в Unix они могут иметь родительский идентификатор, равный нулю? то есть принадлежность к процессу? Это на самом деле не имеет значения, если поток не пытается использовать ресурсы уровня процесса.
Что касается адресов, назначенных компилятором ... Есть несколько возможных способов обдумать это. Одна часть этого - то, что компилятор действительно не выбирает адреса для чего-либо; почти все, что производит компилятор (в современной среде), с точки зрения смещений. Заданная локальная переменная находится в некотором смещении от того места, где будет находиться указатель стека при создании экземпляра подпрограммы. (Обратите внимание, что сами стеки находятся по динамически назначенным адресам, точно так же, как и при распределении кучи.) Обычная точка входа находится в некотором смещении от начала секции кода, в которой она находится. И т.п.
Вторая часть ответа заключается в том, что адреса, такие как они, назначаются компоновщиком, а не компилятором. Что на самом деле просто откладывает вопрос - как это может сделать это? Под чем, я думаю, вы подразумеваете, как он узнает, какие адреса будут доступны во время выполнения? Ответ «практически все».
Помните, что каждый процесс начинается практически с чистого листа, с новым созданием адресного пространства в режиме пользователя. например, каждый процесс имеет свой экземпляр 0x10000. Таким образом, помимо необходимости избегать нескольких вещей, которые находятся в хорошо известных (в любом случае, компоновщике) местоположениях внутри каждого процесса на платформе, компоновщик может свободно размещать вещи там, где он хочет, в адресном пространстве процесса. Это не должно знать или заботиться, где что-нибудь еще уже находится.
Третья часть заключается в том, что почти все (кроме тех вещей, которые определены в ОС и имеют известные адреса) во время выполнения можно перемещать по разным адресам из-за рандомизации расположения адресного пространства, которая существует как в Windows, так и в Linux (Linux выпустила ее во-первых, по сути). Так что на самом деле не имеет значения, куда компоновщик помещает вещи.