Хорошо, чтобы ответить на этот вопрос, пожалуйста, обратитесь к моему ответу на другой вопрос здесь, в SO, относительно «Процессы или потоки с разделением времени в Linux?», Давайте продолжим из этой воображаемой структуры Ядро здесь с использованием процессора x86 и кода C ... Я изменил эту структуру, чтобы учесть семафоры
struct regs{
int eax, ebx, ecx, edx, es, ds, gs, fs, cs, ip, flags;
struct tss *task_sel;
}
struct mem_loc{
int *phys_mem_begin;
int *phys_mem_end;
int *semaphores;
}
struct thread{
struct regs *regs;
struct mem_loc *memlocTracker;
int parent_id;
struct thread *next;
}
struct task{
struct regs *regs;
struct mem_loc *task_mem;
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;
}
struct queue_ready{
struct task *task_id;
struct queue_ready *next;
}
Там, я думаю, это выглядит лучше, верно, в отношении продолжения моего предыдущего ответа, связанного выше, давайте посмотрим, что происходит, когда есть очереди, теперь, давайте посмотрим на эту queue_ready
структуру, теперь, предположим, что есть задача в этой структуре, установленной ядром, точнее, связанный список queue_ready
, основанный на поле priority
самой структуры task
, и приоритет равен, например, 1 готов к исполнению.
Давайте посмотрим на воображаемый планировщик, основанный на C, как показано ниже, хорошо, он может быть дрянным и уязвимым для привередливых, но отложим это в сторону, чтобы сконцентрироваться на аспекте вопроса ...
static void scheduler(void){
struct queue_ready *rdy_sched;
while(1){
for (rdy_sched = head_queue_ready;
rdy_sched != NULL;
rdy_sched = rdy_sched->next){
if (time >= rdy_sched->task_id->start_time + rdy_sched->task_id->quantum){
save_cpu_task_state(&task_reg->regs);
save_mem_task_state(&rdy_sched->task_id->task_mem);
}else{
struct regs *task_reg = rdy_sched->task_id->regs;
struct mem_loc *mem_task = rdy_sched->task_id->task_mem;
load_cpu_task_state(task_reg);
load_mem_task_state(mem_task);
jmp_and_exec_cpu_task(task_reg->cs, task_reg->ip);
save_cpu_task_state(&rdy_sched->task_id->regs);
if (rdy_sched->task_id->num_threads > 0){
struct thread *threads = rdy_sched->task_id->task_thread;
while (threads->next != NULL){
struct regs *thread_reg = threads->regs;
load_cpu_thread_state(thread_reg);
if (threads->memlocTracker->semaphores){
/* House keeping in relation to semaphores */
lock_memory(threads->memlocTracker);
jmp_and_exec_cpu_thread(thread_reg->cs, thread_reg->ip);
save_cpu_thread_state(&thread->regs);
unlock_memory(threads->memlocTracker);
}else{
jmp_and_exec_cpu_thread(thread_reg->cs, thread_reg->ip);
save_cpu_thread_state(&thread->regs);
}
}
}
}
}
}
}
Планировщик ядра выглядит слишком сложным, но на самом деле это не так, единственное упущение, которое я пропустил в коде, - это приоритеты, которые теперь можно игнорировать при обсуждении вопроса ОП.
Давайте разберем эту функцию планировщика scheduler
вниз ... но сначала быстрый взгляд и объяснение того, какие функции используются в функции scheduler
:
- 'load_cpu_task_state' и 'load_cpu_thread_state' - это загружает состояние регистров ЦП, для краткости они для задачи и потока соответственно.
- 'load_mem_task_state' и 'save_mem_task_state' - загружает и сохраняет соответственно состояние памяти, возможно, на / со страницы на диске, заданной полями
phys_mem_begin
и phys_mem_end
структуры mem_loc
для указанного задача. Для краткости это загрузит всю память, принадлежащую указанной задаче, включая потоки.
- 'jmp_and_exec_cpu_task' и 'jmp_and_exec_cpu_task' - это заставляет ядро выдавать магию при переходе к указанным
cs
и ip
регистрам структуры reg
указанной задачи, также то же самое для потока соответственно.
- 'save_cpu_task_state' и 'save_cpu_thread_state' - это заставляет ядро сохранять состояние регистров ЦП как для задачи, так и для потока соответственно.
- 'lock_memory' и 'unlock_memory' - это для блокировки области памяти с помощью семафоров, которые вскоре вступят в игру ...
Теперь scheduler
выполняется вечно и выполняет итерацию по связанному списку задач; требуется проверка, чтобы убедиться, что задача выполнялась в течение определенного периода и превысила сумму quantum
, например, 20 мс. Затем он сохраняет состояние процессора и состояние памяти (возможно, сохраняется в файле подкачки на диске), готовый перейти к следующей задаче в списке.
Если остается еще время, он загружает регистры ЦП задачи и загружает память (возможно, со страницы) и переходит туда, где был последний указатель инструкции и сегмент кода (ip и cs соответственно), и задача возобновляется. ,
Если в указанной задаче есть потоки, она будет перебирать связанный список потоков, загружать регистры ЦП и переходить в код, чтобы возобновить выполнение потоков, а затем сохранять регистры ЦП.
Теперь, когда все становится немного странно, особенно в этой игрушечной ОС, как управлять семафорами! э-э, похоже, большая головная боль нависла над головой разработчика ОС ....
Я бы предположил, что если часть памяти заблокирована, semaphores
будет отличным от NULL и будет получен менеджером среды выполнения в коде пользовательской земли, который запрашивает семафор, он заблокирует память, чтобы предотвратить потоптывая данные, переходя к коду для указанного потока, разблокирует его, когда продолжительность этого потока завершится, и сохранит это состояние регистров ЦП потока ...
Вывод:
Это мой вывод, основанный на этом, что касается фактов, подтверждающих меня, я не могу, так как эта теория была подробно прочитана, изучена под микроскопом в бесчисленных книгах, начиная от Университета в США, вплоть до самого конца. Что касается университета в Новой Зеландии, то это из моих наблюдений и моего понимания, как вы можете видеть из этого кода, сложности и бремени, налагаемого на разработчика при кодировании этого, а не только этого, наложенных ограничений, проектных решений - оба железо и софт.
Управлять семафорами в этом контексте гораздо сложнее, если ответить, если было доступно больше очередей, как я стремился сохранить в одной очереди queue_ready
, фактор стоимости с точки зрения использования памяти, использования диска и выше все время использования растет по мере того, как ядро должно будет отслеживать те смягчающие факторы, которые вступают в игру, еще один не менее важный фактор - это то, сколько приоритетов будет, поэтому, по моему мнению, наличие очереди для разных семафоров не является Жизнеспособный и выполнимый вариант.