Полазил по интернету. Скачял мануалы с Intel.com, но возможно знания аглийского до конца не хватает понять. Как связать TSS и CPU affinity mask? Каким образом PID процесса связывается с процесcором через Sheduler? Где хранится CPU affinity mask или в какой дескриптор входит? Перекопал исходники Линукса на предмет как это работает, но не нашел. sched_setaffinity, sched_getaffinity, CPU_CLR, CPU_ISSET, CPU_SET, CPU_ZERO Помогите разобраться.
они никак не связаны значение PID находится в дескрипторе процесса struct task_struct каждый CPU имеет ассоциированную с ним очередь выполнения runqueues типа struct rq (runqueus является per-cpu переменной) каждая очередь выполнения имеет связный список дескрипторов struct task_struct вот и вся связь struct task_struct плохо искал
Спасибо за ответ rei3er Я данную структуру нашел. Разбираться с sheduler-ом не простая задача. Вы правильно меня поняли. Хочу немного пояснить свой вопрос: Как указать процессу исполняться на определённом ядре процессора либо на определённом процессоре без вызова системных функций sched_getaffinity и sched_setaffinity? Если посмотреть здесь часть "Модуль управления сегментами в Linux" http://www.interface.ru/home.asp?artId=2814 Каждый дескриптор сегмента TSS указывает на отдельный процесс. TSS хранит информацию об аппаратном контексте для каждого CPU, который принимает участие в переключении контекста. Например, при переключении режимов U->K x86 CPU получает адрес стека режима ядра из TSS. Каждый процесс имеет свой собственный TSS-дескриптор, хранящийся в GDT. Идем по списку исходников вот здесь удобно http://lxr.linux.no/linux/include/linux/sched.h#L937 long sched_getaffinity(pid_t pid, cpumask_t *mask) 4497{ 4498 struct task_struct *p; 4499 int retval; 4500 4501 mutex_lock(&sched_hotcpu_mutex); 4502 read_lock(&tasklist_lock); 4503 4504 retval = -ESRCH; 4505 p = find_process_by_pid(pid); 4506 if (!p) 4507 goto out_unlock; 4508 4509 retval = security_task_getscheduler(p); 4510 if (retval) 4511 goto out_unlock; 4512 4513 cpus_and(*mask, p->cpus_allowed, cpu_online_map); 4514 4515out_unlock: 4516 read_unlock(&tasklist_lock); 4517 mutex_unlock(&sched_hotcpu_mutex); 4518 4519 return retval; 4520} long sched_setaffinity(pid_t pid, cpumask_t new_mask) 4406{ 4407 cpumask_t cpus_allowed; 4408 struct task_struct *p; 4409 int retval; 4410 4411 mutex_lock(&sched_hotcpu_mutex); 4412 read_lock(&tasklist_lock); 4413 4414 p = find_process_by_pid(pid); 4415 if (!p) { 4416 read_unlock(&tasklist_lock); 4417 mutex_unlock(&sched_hotcpu_mutex); 4418 return -ESRCH; 4419 } 4420 4421 /* 4422 * It is not safe to call set_cpus_allowed with the 4423 * tasklist_lock held. We will bump the task_struct's 4424 * usage count and then drop tasklist_lock. 4425 */ 4426 get_task_struct(p); 4427 read_unlock(&tasklist_lock); 4428 4429 retval = -EPERM; 4430 if ((current->euid != p->euid) && (current->euid != p->uid) && 4431 !capable(CAP_SYS_NICE)) 4432 goto out_unlock; 4433 4434 retval = security_task_setscheduler(p, 0, NULL); 4435 if (retval) 4436 goto out_unlock; 4437 4438 cpus_allowed = cpuset_cpus_allowed(p); 4439 cpus_and(new_mask, new_mask, cpus_allowed); 4440 retval = set_cpus_allowed(p, new_mask); 4441 4442out_unlock: 4443 put_task_struct(p); 4444 mutex_unlock(&sched_hotcpu_mutex); 4445 return retval; 4446} Это только верхушка айсберга и реализует Software affinity.
6.9.2 Сегменты в 80386 Сегментные регистры используются при трансляции адреса для генерации линейного адреса из логического (виртуального) адреса. linear_address = segment_base + logical_address Линейный адрес транслируется затем в физический адрес посредством аппаратуры страничной организации (paging) Каждый сегмент в системе описан 8-ми байтным дескриптором сегмента, в котором содержится вся необходимая информация (база, ограничение, тип, привилегии). Имеют место следующие сегменты: Обычные сегменты сегменты кода и данных Системные сегменты (TSS) cегменты состояния задачи (LDT) таблицы локальных дескрипторов Характеристики системных сегментов: * Системные сегменты являются спецификаторами задач * Имеется TSS, связанный с каждой задачей в системе. Он содержит tss_struct (sched.h). Размер этого сегмента соответствует размеру tss_struct, исключая i387_union (232 байта). Он содержит всю информацию, необходимую для перезапуска задачи. * Таблицы LDT содержат дескрипторы обычных сегментов, принадлежащих задаче. В Linux каждой задаче соответствует одна LDT. B Linux task_struct предусмотрено пространство для 32-х дескрипторов. Обычная LDT, созданная в Linux, имеет размер 24 байта и, следовательно, пространство для 3-х входов. Ее содержимое: * LDT[0] Null (принудительно) * LDT[1] дескриптора сегмента пользовательского кода * LDT[2] дескриптор сегмента пользовательских данных/стека Все сегменты пользователя имеют базу 0х00, так что линейный адрес тот же самый, что и логический. Для получения доступа ко всем этим сегментам 386 использует таблицу глобальных дескрипторов (GDT), которая устанавливается в памяти системой (местоположение задается регистром GDT). GDT содержит дескрипторы сегментов для каждого сегмента состоян ия задачи, каждого локального дескриптора и обычных сегментов. Linux GDT содержит входы двух обыкновенных сегментов: * GDT[0] нулевой дескриптор * GDT[1] дескриптор сегмента кода ядра * GDT[2] дескриптор сегмента данных/стека ядра Оставшаяся область GDT заполнена TSS и LDT дескрипторами системы. * GDT[3] ??? * GDT[4] = TSS0, GDT[5] = LDT0 * GDT[6] = TSS1, GDT[7] = LDT1 ..... и т.д...... Заметьте LDT[n] != LDTn * LDT[n] = n-й дескриптор в LDT текущей задачи * LDTn = дескриптор в GDT для LDT n-й задачи В данном случае GDT имеет 256 входов, пространство для 126 задач. Сегменты ядра имеют базу 0хс0000000, которая задает местонахождение ядра в линейном представлении. Прежде, чем сегмент может быть использован, содержимое дескриптора для этого сегмента должно быть загружено в сегментный регистр. 386 имеет множество сложных критериев, ограничивающих доступ к сегментам, так что вы не сможете просто загрузить дескриптор в сегментный регистр. Также эти сегментные регистры имеют невидимые для программиста участки. Видимые участки - это то, что обычно называется сегментными регистрами cs, ds, es, fs, gs и ss. Программист загружает один из этих регистров 16-ти битным значением, называемым селектором. Селектор однозначно идентифицирует дескриптор сегмента в одной из таблиц. Доступ подтверждается и соответствующий дескриптор загружается посредством аппар атных средств. Обычно в Linux игнорируется комплексная защита на уровне сегмента (чрезмерная?), предоставляемая 386. Она базируется на основе аппаратных средств страничной организации и объединенной защитой на уровне страницы. Правила на уровне сегмента, которые применяются к пользовательским процессам, состоят в следующем: 1. Процесс не может напрямую обращаться к данным ядра или сегментам кода. 2. Всегда имеет место контроль ограничения, однако, условие, что каждый Здесь уже ближе к процессору http://lxr.linux.no/linux/include/asm-i386/processor.h#L275
здесь под процессом понимается задача в контексте архитектуры IA-32, а не ОС по поводу исходников что непонятно?
Мне не понятно как на уровне ассемблера сделать тоже самое, а именно указать какая задача в контексте IA32 на каком процессорном ядре будет выполнятся? Как скажем в ассемблере указать что задача из данного TSS сегмента на ядро 1, а задача из того TSS сегмента на ядро 2?
Tracker тут уже правильно посоветовали, разберитесь с терминами если дело касается Linux, то TSS представлен per-cpu переменной init_tss она используется совместно всеми процессами, которые последовательно выполняются на соответствующем процессоре привязка процесса к процессорам (cpus_allowed в struct task_struct) происходит в трех случаях 1. создание процесса (cpus_allowed наследуется от родительского процесса) 2. баллансировка нагрузки (перераспределение процессов по очередям выполнения (а значит по процессорам) для равномерной загрузки каждого процессора) 3. sched_setaffinity() через миграционные процесс
Я вот не понимаю до конца как работает многоядерный процессор (Core2Duo,Core2Quad)? Каким образом достигается обработка отдельной задачи на разных ядрах или на одном? Linux не при чем. Это как пример был который легко посмотреть в исходниках. Вопрос звучит так: Я не пишу свою ОС, но хочу понимать каким образом я могу перейти в защищенный режим, создать несколько задачь и заставить их выполняться на раличных ядрах без загрузки сторонней ОС (Windows, Linux)? Простой примитивный пример.
5.1.32. В случае SMP запускаем другие процессоры Выполнение smp_init(), в зависимости от конфигурации ядра, возможно тремя способами. Для однопроцессорных (UP) систем без IO APIC (CONFIG_X86_IO_APIC не определена), smp_init() пуста - соответственно ничего не происходит. Для однопроцессорных UP систем с IO APIC для маршрутизации прерываний она вызывает IO_APIC_init_uniprocessor(). Для многопроцессорных (SMP) систем ее основная задача заключается в вызове архитектурно-специфичной функции "smp_boot_cpus()", которая выолняет следующее: - Для ядер с CONFIG_MTRR вызывает mtrr_init_boot_cpu(), которая должна отработать до того, как загрузятся другие процессоры. - Сохраняет и выводит информацию о BSP CPU. - Сохраняет ID BSP APIC и BSP ID логического CPU (последний равен 0). - Если не найдена таблица маршрутизации прерываний MP BIOS, то возвращается к использованию только одного CPU и завершается. - Проверяет существование локального APIC для BSP. - Если использована опция загрузки "maxcpus" со значением 1 (без SMP), то игнорирует таблицу маршрутизации прерываний MP BIOS. - Переключает систему из PIC-режима в режим прерывания симметричного ввода-вывода. - Устанавливает локальный APIC BSP. - Использует карту наличия (presence map) CPU для последовательной загрузки AP. Ожидает загрузки предыдущего AP перед запуском загрузки следующего. - В случае использования IO APIC {что справедливо всегда кроме случаев с опцией загрузки "noapic"} устанавливает IO APIC (или каждый, если их несколько). http://linuxportal.ru/entry.php/1506_0_3_0_C/
Докопался до сюда в исходниках. Есть вменяемые доки по IO APIC? http://www.cs.uwaterloo.ca/~brecht/servers/apic/SMP-affinity.txt static void set_ioapic_affinity_irq(unsigned int irq, cpumask_t mask) 293{ 294 struct irq_cfg *cfg = irq_cfg + irq; 295 unsigned long flags; 296 unsigned int dest; 297 cpumask_t tmp; 298 299 cpus_and(tmp, mask, cpu_online_map); 300 if (cpus_empty(tmp)) 301 return; 302 303 if (assign_irq_vector(irq, mask)) 304 return; 305 306 cpus_and(tmp, cfg->domain, mask); 307 dest = cpu_mask_to_apicid(tmp); 308 309 /* 310 * Only the high 8 bits are valid. 311 */ 312 dest = SET_APIC_LOGICAL_ID(dest); 313 314 spin_lock_irqsave(&ioapic_lock, flags); 315 __target_IO_APIC_irq(irq, dest, cfg->vector); 316 irq_desc[irq].affinity = mask; 317 spin_unlock_irqrestore(&ioapic_lock, flags); 318}