Shaos wrote:Теперь про переключение контекстов. На каждом проце у нас будет свой планировщик, каждый из которых будет переключать задачи по прерыванию 10 Гц (если они не запрещены). Каждый проц знает свой номер (скажем IN 4 будет давать общее кол-во рабочих процов в системе, а IN 5 - номер текущего проца, где была вызвана эта команда) - в обработчике прерываний смотрится количество оставшихся тактов процесса, если не 0, то уменьшаемых на 1 с+ отпусканием прерывания, а если 0 (процесс исчерпал свой лимит), то происходит переключение контекста - планировщик, чтобы найти следующий процесс который надо запустить, просто идёт по таблице всех возможных процессов (D_PROC) начиная от текущего, ища следующий ненулевой описатель, соответствующий которому проц не входит в список активных процессоров (D_ACTP) - в этом случае происходит переключение контекста на этот процесс и отпускание прерывания.
Хотя наверное с нашим небольшим количеством процов можно оба значения утолкать в один порт, т.е. IN 4 может возвращать в младших 4 битах порядковый номер процессора (1,2,3,4), а в старших - общее их количество в системе (4). В будущем совершенно прозрачно с точки зрения софта можно воткнуть 8 или даже больше процессоров (до 15).
Shaos wrote:
После некоторых раздумий решил, что возможность явного вызова планировщика (без прерывания) также нужна - например когда процесс решил, что ему надо повиснуть на мьютексе, он может немедленно отдать управление следущей задаче, а для этого надо запустить планировщик...
Другой вопрос как проще это сделать - сначала я думал про программный вызов, а сейчас вот подумал о возможности внеочередного вызова прерывания планировщика через запись некоего бита в какой-то порт, хотя с другой стороны непосредственный вызов RST 7 сделает тоже самое, что и аппаратное прерывание...
Code: Select all
; GRAB_MUTEX
; B - process id
; C - mutex id
GRAB_MUTEX:
DI
PUSH PSW
MVI A,#10 ; access to semaphore that controls programming mutexes
OUT 7
GRAB_MUTEX_LOOP:
IN 7
ANI #10
JZ GRAB_MUTEX_LOOP ; wait for ready
; then hold or go further (if nobody else waits on the same semaphore)
PUSH B
PUSH H
MOV A,B ; save process id
MVI B,0 ; BC = mutex id
LXI H,MUTEXES ; HL = address of table of mutexes
DAD B ; HL = address of our mutex
MOV B,A ; B = process id again
MOV A,M
ORA A
JZ GRAB_MUTEX_FREE
; here we know that our mutex is grabbed by other process
XRA A
OUT 7 ; signal to mutex semaphore that it's free now (send 0 to port 7)
GRAB_MUTEX_WAIT:
; HL = address of our mutex
RST 7 ; program call of scheduler (?)
; scheduler will return here after resume of this process
MOV A,M
ORA A
JNZ GRAB_MUTEX_WAIT ; mutex is still occupied so go back
; it looks like mutex is free - try to securely grab mutex again
POP H
POP B
POP PSW
JMP GRAB_MUTEX
GRAB_MUTEX_FREE:
; mutex if free to grab
MOV M,B ; store porcess id in our mutex to grab it
; A = 0
OUT 7 ; signal to mutex semaphore that it's free now (send 0 to port 7)
POP H
POP B
POP PSW
EI
RET
Такой код не будет ставить 0 в качестве приоритета ожидающего процесса, как я предполагал в начале, т.е. процесс всё равно будет вызываться планировщиком (разве что можно сделать какой-то другой вызов вместо RST 7, чтобы не отрабатывался приоритет для ожидающего треда), но при вызове он только будет проверять мьютекс на свободность (такая проверка не требует хардверного семафора) и если он свободен, то пробовать его опять захватить прикрывшись семафором, а если занят, то управление опять будет передано другому треду через непосредственный вызов планировщика.
P.S. В этом коде я вижу несколько потенциальных проблем: первая - это то что ожидающий процесс всё равно будет получать управление чтобы проверить, что он всё ещё должен ждать, тратя на это циклы ЦПУ; вторая - возможны гонки из-за того, что освободившийся мьютекс будет захвачен первым попавшимся процессом, а не тем, кто стал ждать его раньше (очереди нету), т.е. я вполне могу представить себе ситуацию, когда один из ожидающих тредов не сможет получить мьютекс никогда, если его достаточно активно будут юзать другие более удачливые треды - чтобы это дело улучшить можно добавить некую случайность в длительности ожидания; третья - надо поставить проверку на то, что мьютекс уже занят тем же самым процессом, что по хорошему должно приводить к фатальной ошибке и остановке системы (сейчас это приведёт к дед-локу). А вообще наверное для первого приближения код вполне приемлимый - можно будет погонять на эмуле ставя разное количество процессоров и наблюдая за масштабируемостью разных задач...