Чтобы понять в недрах задач / потоков ... давайте посмотрим на этот игрушечный код ядра ...
struct regs{
int eax, ebx, ecx, edx, es, ds, gs, fs, cs, ip, flags;
struct tss *task_sel;
}
struct thread{
struct regs *regs;
int parent_id;
struct thread *next;
}
struct task{
struct regs *regs;
int *phys_mem_begin;
int *phys_mem_end;
int *filehandles;
int priority;
int *num_threads;
int quantum;
int duration;
int start_time, end_time;
int parent_id;
struct thread *task_thread;
/* ... */
struct task *next;
}
Представьте, что ядро выделяет память для этой структуры task
, которая является связанным списком, посмотрите внимательно на поле quantum
, которое является временным интервалом времени процессора на основе поля priority
. Всегда будет задание с идентификатором 0, которое никогда не спит, просто бездействует, возможно, выдает nops (No OPerationS) ... планировщик вращается вокруг тошноты до бесконечности (то есть когда питание отключается), если quantum
поле определяет, что задача выполняется в течение 20 мс, устанавливает значения start_time
и end_time
+ 20 мс, когда этот end_time
активен, ядро сохраняет состояние регистров процессора в указатель regs
. Переходит к следующей задаче в цепочке, загружает регистры процессора из указателя в regs
и переходит в инструкцию, устанавливает квантовую и временную длительности, когда длительность достигает нуля, переходит к следующей ... эффективно переключение ... это то, что создает иллюзию одновременной работы на одном процессоре.
Теперь посмотрите на структуру thread
, которая является связанным списком потоков ... внутри этой структуры task
. Ядро выделяет потоки для указанной задачи, устанавливает состояния процессора для этого потока и переходит в потоки ... теперь ядро должно управлять потоками, а также самими задачами ... опять переключение контекста между задачей и потоком ...
Перейдите к многопроцессорному процессору, ядро было бы настроено на масштабируемость, и что бы делал планировщик, загрузите один task
на один процессор, загрузите другой на другой процессор (двухъядерный), и оба перейдите туда, куда указывает указатель инструкции ... теперь ядро действительно выполняет обе задачи одновременно на обоих процессорах. Масштабирование до 4-х, то же самое, дополнительные задачи, загруженные на каждый процессор, масштабирование снова, до n-way ... вы получаете дрейф.
Как вы можете видеть, как потоки не будут восприниматься как масштабируемые, так как ядро откровенно выполняет гигантскую задачу по отслеживанию того, что выполняет процессор, и, кроме того, какая задача выполняется, какие потоки , что в основном объясняет, почему я думаю, что потоки не являются точно масштабируемыми ... Потоки потребляют много ресурсов ...
Если вы действительно хотите увидеть, что происходит, взгляните на исходный код для Linux, особенно в планировщике. Не зацикливайтесь, забудьте о выпусках ядра 2.6.x, посмотрите доисторическую версию 0.99, планировщик будет проще для понимания и легче для чтения, конечно, он немного устарел, но стоит посмотреть, это поможет вам понять почему и, надеюсь, мой ответ также: почему потоки не масштабируются ... и показывает, как игрушечная операционная система использует временное разделение на основе процессов. Я стремился не вдаваться в технические аспекты современных процессоров, которые могут сделать больше, чем я описал ...
Надеюсь, это поможет.