Форум программистов, компьютерный форум, киберфорум
Assembler: DOS/Real Mode/16-bits
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.55/40: Рейтинг темы: голосов - 40, средняя оценка - 4.55
3 / 3 / 2
Регистрация: 19.03.2016
Сообщений: 19

Многозадачность, алгоритм кругового планирования

22.02.2019, 18:19. Показов 9437. Ответов 44

Студворк — интернет-сервис помощи студентам
Всем здравствуйте!

Есть задача:
Организуйте многозадачную среду (3 задачи) с выводом на экран номера задачи. Переключение происходит по алгоритму кругового планирования с выделенными квантами времени (используйте прерывание таймера).

Задумка следующая. На каждую из трех задач назначается число, равное количеству необходимых выводов номера задачи. Задача представляется процедурой, которая в цикле выводит свой номер задачи на экран. Основная часть программы, этакий диспетчер, собственно, запускает эти задачи и прерывает тогда, когда ее время истекло. Если очередная процедура (задача) выполнилась полностью, то мы ее еще потом запустим, а если нет, то забываем о ней.

Которые сутки мучаюсь с тем, как это реализовать. С самим алгоритмом планирования все понятно, но для его реализации необходимо уметь прерывать выполнение процедуры задачи "извне", то есть основной частью программы по истечении некоторого периода времени. И тут вопрос: возможно ли это? Как такое сделать через замену обработки прерывания таймера?

Долго пыталась найти что-то хотя бы похожее на мою задачу, но безуспешно..
Очень надеюсь на Вашу помощь! Подойдут любые соображения также и по решению поставленной задачи.
Заранее спасибо!
0
Лучшие ответы (1)
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
22.02.2019, 18:19
Ответы с готовыми решениями:

Многозадачность
Здравствуйте!Пишу программу,использующую многозадачность.Возникли вопросы: 1)Строг ли порядок следования дескрипторов в GDT? 2)Нужны ли...

Многозадачность Зубков С.В. DOS
Добрый день! У автора Зубкова есть тема «Переключение задач» где в конце сказано, что можно модернизировать и получится многозадачность,...

Многозадачность. Переключение между 3 задачами
не могу понять где ошибка в коде: хочу реализовать переключение между 3 задачами, компилю tasm-ом, запускаю на DOSbox-е, выдает ошибку:...

44
Модератор
Эксперт по электронике
 Аватар для ФедосеевПавел
8675 / 4512 / 1670
Регистрация: 01.02.2015
Сообщений: 13,943
Записей в блоге: 13
22.02.2019, 20:22
Студворк — интернет-сервис помощи студентам
Да, конечно. Упустил, а это важно, т.к. это контекст наравне с регистрами, и даже более - это элемент переключения контекста.
0
3 / 3 / 2
Регистрация: 19.03.2016
Сообщений: 19
22.02.2019, 20:26  [ТС]
ФедосеевПавел, сейчас, наверное, будет глупый вопрос, но все же. Область стеков для задач мне определять через сегмент данных (выделением памяти для какой-то переменной) или, к примеру, выделить 300 байт на весь стек с самого начала программы, а затем "поделить" указателями этот сегмент стека по 100 байт на каждую из задач (1-ый указатель - 1-100 байт, 2-ой - 101-200 и т.д.)?

Добавлено через 1 минуту
Цитата Сообщение от liv Посмотреть сообщение
После всех push надо сохранить старые SS и SP в массиве.
Т.е. перед "здесь выбор по номеру задачи значений ss и sp из массивов ss и sp"
Это мне было понятно, но меня больше пугает именно выделение памяти для всего этого добра... Сама идея логична и шикарна)
0
Эксперт CЭксперт С++
 Аватар для liv
5120 / 4574 / 855
Регистрация: 07.10.2015
Сообщений: 9,462
22.02.2019, 20:27
pelgray, а без разницы. Главное - правильно инициализировать SS:SP.
Стек - обычная память. Только используется по своему.

Добавлено через 45 секунд
Цитата Сообщение от pelgray Посмотреть сообщение
но меня больше пугает именно выделение памяти для всего этого добра...
Глаза боятся, а руки делают
0
3 / 3 / 2
Регистрация: 19.03.2016
Сообщений: 19
22.02.2019, 20:29  [ТС]
liv, спасибо за ответ, пошла "дерзать"
0
Модератор
Эксперт по электронике
 Аватар для ФедосеевПавел
8675 / 4512 / 1670
Регистрация: 01.02.2015
Сообщений: 13,943
Записей в блоге: 13
22.02.2019, 20:41
Эти "стеки" можно определять и как отдельные сегменты и как массивы в сегменте данных (сегменте неинициализированных данных). Если программа DOS com, то можно резервировать области за последней инструкцией программы. Можно даже обращаться к DOS для выделения памяти (GetMem+FreeMem). И ещё много разных способов.

Тут, как удобнее будет лично вам. Сама программа учебная и нетребовательна к памяти, именно поэтому способ реализации стеков безразличен.
0
 Аватар для Kukuxumushu
1624 / 806 / 146
Регистрация: 13.06.2015
Сообщений: 3,266
22.02.2019, 20:55
А я бы сделал хитро - скомпилил 3 COM-файла для задач, а основной ехешник - их лоадер, который выделяет память по 64кб (целый сегмент) через int 21h 48h. Тогда указатели стека для каждой задачи автоматически будут в конце сегментов, и не надо думать куда их отдельно сохранять. В тасклисте хранить сегменты, полученные при выделении памяти.

Добавлено через 5 минут
Контекст неактивных задач можно в прерывании сбрасывать в стек этой же задачи, а при активации - вытаскивать оттуда. Тут главное конечно очень внимательно следить и понимать, чего где и когда в стеке лежит, и правильно сохранить/заменить адрес возврата.

Добавлено через 1 минуту
Уже прям целая операционная система получится
0
Эксперт CЭксперт С++
 Аватар для liv
5120 / 4574 / 855
Регистрация: 07.10.2015
Сообщений: 9,462
22.02.2019, 21:01
Kukuxumushu, тоже вариант!

Добавлено через 3 минуты
Но первый вариант все-таки будет попроще, особенно на первом этапе
0
 Аватар для Kukuxumushu
1624 / 806 / 146
Регистрация: 13.06.2015
Сообщений: 3,266
22.02.2019, 21:04
Причём, поскольку лоадер у нас будет свой, то можно и не все 64кб выделять, а допустим 1кб вполне хватит, тогда и указатель стека лоадер может поставить на 0x03FF. PSP можно тупо игнорировать, как и его смещение org 100h, мы же крутые - сделали собственный формат исполняемого файла!

Добавлено через 1 минуту
Цитата Сообщение от liv Посмотреть сообщение
Но первый вариант все-таки будет попроще
Да вот как бы эта "простота" обманчивой не оказалась. Что-то мне подсказывает, что объёма кода и времени на его отладку окажется на порядок больше.
0
Модератор
Эксперт по электронике
 Аватар для ФедосеевПавел
8675 / 4512 / 1670
Регистрация: 01.02.2015
Сообщений: 13,943
Записей в блоге: 13
22.02.2019, 21:04
Это всего лишь способ выделения памяти под стеки - или средствами планировщика или запущенного процесса.
А также способ реализации задачи - собственная процедура или внешний файл.

Но DOS com программа не настраивает сегментные регистры и sp - пользуется тем, что дал командный интерпретатор - в нашем случае "наш" планировщик.

Таким образом в такой организации задания нет преимуществ перед предложенным ранее.

Единственно - приходит понимание о природе запускаемых процессов.
0
Эксперт CЭксперт С++
 Аватар для liv
5120 / 4574 / 855
Регистрация: 07.10.2015
Сообщений: 9,462
22.02.2019, 21:17
Kukuxumushu, кто спорит, поизвращаться всегда можно

Добавлено через 7 минут
Kukuxumushu, как раз отладка в первом случае будет проще, т.к. будет одна программа.
Да и объем кода ничуть не будет больше. Полный контекст-то в любом случае надо переключать.
И в чем выгода отдельных программ?

Добавлено через 3 минуты
Только в том, что не надо выделять специально стек? Сомнительная выгода...
0
 Аватар для Kukuxumushu
1624 / 806 / 146
Регистрация: 13.06.2015
Сообщений: 3,266
22.02.2019, 21:30
Цитата Сообщение от liv Посмотреть сообщение
И в чем выгода отдельных программ?
В полной изоляции задач друг от друга и от псевдо-ОС. Я уж не знаю кому как, но мне лично гораздо проще вести отладку, когда ты точно знаешь в какой находишься задаче или в управляющем процессе, и что их данные не перетрут друг друга. Но тут не мне конечно решать как делать, просто предложил своё видение, т.к. в реальных ОС же происходит всё именно по такому сценарию.
0
Эксперт CЭксперт С++
 Аватар для liv
5120 / 4574 / 855
Регистрация: 07.10.2015
Сообщений: 9,462
22.02.2019, 21:39
Kukuxumushu, ну, полной изоляции в ДОС по-любому нет
И задача написать ОС не стоит. Для начального этапа обучения первый способ решения - самое то.

Впрочем, создать вот такую псевдо-ОС - идея превосходная! Можно предложить pelgray ее тоже реализовать.
Будет весьма полезно подойти с такой стороны.
0
3410 / 1829 / 489
Регистрация: 28.02.2015
Сообщений: 3,696
23.02.2019, 00:05
pelgray, ФедосеевПавел, liv,

Цитата Сообщение от pelgray Посмотреть сообщение
Организуйте многозадачную среду (3 задачи) с выводом на экран номера задачи. Переключение происходит по алгоритму кругового планирования с выделенными квантами времени (используйте прерывание таймера).
Блин, Вы задание читали?

От Вас требуется организовать переключение задач, но сами задачи, не реальные, а мнимые, т.е., Вам нужно визуально, показать, что задачи как-то переключаются. Выводить только номер задачи. Это как в фильме "Синьор Робинзон", "опять о море", блин как круто сюжет закручен.
ОСь, под каждую задачу, создает некоторую область памяти, с полной информацией о всех программах в системе. При замене задачи, подменяется этот блок и новая задача продолжается. Названия и полную информацию, Вы найдете в книгах "Операционные Системы" Таненбаума и/или Столлингса. Там есть разделы посвященные многозадачности. Это Вам домашнее задание.

Чтобы не заснуть во время работы программы, будем выводить только IP, вернее его текущее значение.

Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
model   tiny
.code
org 100h
begin:  mov ax,3
    int 10h
    mov ax,351CH
    int 21h
    mov old1Co,bx
    mov old1Cs,es
    mov ax,251Ch
    lea dx,new1C
    int 21h
    mov ax,0B800h
    mov es,ax
@@00:   lea bx,task
    add bx,num
    add bx,num
    mov si,[bx]
    xor di,di
    mov cx,szMes
    mov ah,0Fh
@@01:   lodsb
    Stosw
    loop    @@01
    mov bx,si
    mov ax,[bx]
    inc ax
    mov [bx],ax
    mov cx,5
    mov si,10
@@02:   xor dx,dx
    div si
    or  dx,0f30h
    push    dx
    loop    @@02
    mov cx,-1
@@a:    mul ax
    mul ax
    mul ax
    mul ax
    mul ax
    mul ax
    mul ax
    loop    @@a
    mov cx,5
@@03:   pop ax
    stosw
    loop    @@03
    mov ah,01
    int 16h
    jz  @@00
    mov ax,351Ch
    mov dx,old1Co
    mov ds,old1Cs
    int 21h
    ret
new1C:  push    ax
    push    ds
    push    cs
    pop ds
    mov ax,count
    dec ax
    mov count,ax
    jnz @@04
    mov ax,50
    add ax,num
    add ax,num
    mov count,ax
    mov ax,num
    inc ax
    and ax,03
    mov num,ax
@@04:   pop ds
    pop ax
    db  0EAh    
old1Co  dw  ?
old1Cs  dw  ?
count   dw  20
num dw  0
dw0 db  'system ip:='
szMes   equ $-dw0
    dw  0
szRec   equ $-dw0
dw1 db  'task 1 ip:='
    dw  0
dw2 db  'task 2 ip:='
    dw  0
dw3 db  'task 3 ip:='
    dw  0
 
task    dw  offset dw0
    dw  offset dw1
    dw  offset dw2
    dw  offset dw3
    end begin
Как-то так. TASM.

Добавлено через 10 минут
Kukuxumushu, см. выше.
0
 Аватар для Kukuxumushu
1624 / 806 / 146
Регистрация: 13.06.2015
Сообщений: 3,266
23.02.2019, 20:16
Поскольку тема взбудоражила общественность, и особенно liv, то я решил сделать многозадачную недооперационную систему, и назвал её SuperOS.

Будем рисовать и стирать российский флаг, каждую из полос которой будет обрабатывать отдельная задача в виде COM-файла. Код для белой полосы:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
.model tiny        ; Программа в формате COM
.code
org 100h
.startup
mov ah,0fh         ; Читаем видеорежим
int 10h
cmp al,12h         ; Мы уже в режиме 12h?
je Do              ; Да - не трогать режим
mov ax,12h         ; Нет - включить режим 12h
int 10h            ; 640x480x16
xor bh,bh          ; Видиостраница 0
Do:
mov ax,0c8fh       ; Попиксельный вывод цвета в режиме XOR
P:mov cx,160       ; Высота полоски - 480/3=160 пикселей
  mov dx,0         ; Счётчик строк
  R:push cx        ; Рисование строки
    xor cx,cx
    C:int 10h      ; Вывод пикселя
      inc cx       ; Переход к следующему пикселю
      cmp cx,640   ; Ширина экрана 640 пикселей
      jne C
    inc dx         ; Переход к следующей строке
    pop cx
    loop R
  jmp P            ; Бесконечное зацикливание
ret     
end
Остальные задачи отличаются только цветом и начальной позицией.

Теперь напишем операционную систему, на вход которой в командной строке будет подаваться произвольное количество имён файлов с задачами, а она будет динамически грузить их в память и переключать по таймеру. При загрузке каждой задачи ей дополнительно выделяется 100h байт стека в области после образа, в последней ячейке которого хранится указатель на реальную вершину стека задачи. Также при загрузке инициализируются все регистры новой задачи стандартными значениями. Сам список задач, состоящий из полей "адрес сегмента:адрес конца стека" также динамический и хранится в системном стеке ОС. При срабатывании таймера адрес возврата в задачу автоматически записывается в её стек, по этой причине нельзя подменять int 1ch, нужно int 8h. Затем происходит переключение контекста и возобновление следующей задачи. SuperOS завершает работу, выгружает из памяти все задачи и передаёт управление обратно DOS по нажатию любой клавиши.
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
; SuperOS v0.1
; Preemptive multitasking realmode operation system by Konstantin Korotkov
 
; Набор инструкций - 80186, формат - exe
.186
.model small
.stack 100h
.data
OldTmr dw ?,?   ; Исходный вектор таймера
TaskList dw ?,? ; Указатель на начало тасклиста
Task dw 0       ; Номер текущей задачи
N dw 0          ; Кол-во задач
 
.code
Start:
; Возврат лишней памяти в кучу DOS
; DS=ES=PSP
mov bx,ZSEG
mov ax,es
sub bx,ax       ; Определение размера программы
mov ah,4ah
int 21h
; Загрузка задач
xor bh,bh
mov bl,ds:[80h]
mov cx,bx
mov byte ptr[bx+81h],' ' ; Замена терминатора командной строки пробелом
cld
xor dx,dx
mov di,82h      ; Игнорирование начального пробела
mov al,' '
S:jcxz Run      ; Парсинг командной строки
  push di       ; Сохранение адреса начала имени файла в качестве параметра LoadFile
  repne scasb   ; Поиск пробела
  mov byte ptr[di-1],0   ; Нуль-терминирование имени файла
  call LoadFile ; Загрузка задачи
  jc S          ; Загрузка не удалась - продолжить парсинг
  push bx       ; Загрузка удалась - сохранение параметров задачи в тасклист
  push si 
  inc dx        ; Инкремент счётчика задач
  jmp S
; Запуск переключения задач
Run:
test dx,dx      ; Если ни одной задачи нет
jz Quit         ; то сразу выйти
mov ax,@data    ; DS=@data
mov ds,ax
mov N,dx        ; Сохранение счётчика задач
mov TaskList,ss ; Сохранение указателя на тасклист
mov TaskList[2],sp
mov ax,3508h    ; Сохранение вектора прерывания таймера
int 21h
mov OldTmr,bx
mov OldTmr[2],es
mov ax,2508h    ; Подмена прерывания таймера
push ds
push cs
pop ds
mov dx,offset Switch
int 21h
pop ds
jmp NextTask    ; Запуск первой задачи
; Завершение работы ОС
Fin:
push ds         ; Восстановление вектора прерывания таймера
mov ax,2508h
lds dx,dword ptr[OldTmr]
int 21h
pop ds
mov cx,N        ; Освобождение памяти задач
mov ah,49h
F:pop es        ; Извлечение указателя конца стека
  pop es        ; Извлечение адреса загрузки
  int 21h       ; Освобождение памяти
  loop F        ; Для всех задач
Quit:
mov ax,4c00h    ; Выход в DOS
int 21h
 
 
;;;;;;;;;;;;    ФУНКЦИИ   ;;;;;;;;;;;;
; Функуия загрузки задачи
;   Параметры:
; [bp+4] - Адрес имени файла (ASCIIZ)
;   Возвращаемые значения:
; bx - адрез загруженной задачи
; si - вершина стека задачи
LoadFile proc
enter 6,0
push ax
push cx
push dx
; Открытие файла
mov dx,[bp+4]
mov ax,3d00h
int 21h
jc LoadErr      ; Ошибка - ничего не делать
mov [bp-2],ax   ; Файловый описатель
; Получение размера файла
mov bx,ax
xor cx,cx
xor dx,dx
mov ax,4202h
int 21h  
mov [bp-4],ax   ; Реальный размер файла
; Выделение памяти
mov bx,ax
add bx,15       ; Вычисление кол-ва параграфов
shr bx,4
add bx,10h      ; Добавление стека задачи - 100h
mov si,bx
shl si,4
sub si,2        ; Конец стека задачи
mov ah,48h
int 21h
mov [bp-6],ax   ; Адрес загрузки задачи
; Установка указателя на начало файла
mov bx,[bp-2]
xor cx,cx
xor dx,dx
mov ax,4200h
int 21h
; Загрузка образа в память
push ds
mov ds,[bp-6]
mov cx,[bp-4]
mov ah,3fh
int 21h 
; Инициализация контекста задачи
mov [si],si         ; Адрес реальной вершины стека
sub word ptr[si],24
mov [si-2],202h     ; flags (if=1) 
mov [si-4],ds       ; cs
mov [si-6],dx       ; ip=0
mov [si-8],ds       ; es=cs
mov [si-10],ds      ; ds=cs
mov [si-12],dx      ; bp=0
mov [si-14],dx      ; di=0
mov [si-16],dx      ; si=0
mov [si-18],dx      ; dx=0
mov [si-20],dx      ; cx=0
mov [si-22],dx      ; bx=0
mov [si-24],dx      ; ax=0
pop ds
; Закрытие файла
mov bx,[bp-2]
mov ah,3eh
int 21h
mov bx,[bp-6] 
LoadErr:
pop dx 
pop cx
pop ax
leave
ret 2
LoadFile endp
 
; Процедура переключения задач по прерыванию таймера
; Флаги и адрес возврата в прерванную задачу сохранены в стек прерыванием
Switch proc
push es         ; Сохранение контекста текущей задачи в её же стек
push ds
push bp
push di
push si
push dx
push cx
push bx
push ax
mov dx,sp       ; Запоминание sp задачи
mov ax,@data    ; Поиск конца стека задачи через тасклист
mov ds,ax          ; Переключение стека на системный
mov ss,TaskList
mov sp,TaskList[2]
mov bp,sp          ; Вычисление смещения задачи в тасклисте
mov si,Task
shl si,2
les di,[bp+si]
mov es:[di],dx  ; Обновление реальной вершины стека
; Системная обработка прерывания
pushf           ; Вызов системного обработчика
call dword ptr[OldTmr]
mov al,20h      ; Сброс контроллера прерываний
out 20h,al
mov ah,1        ; Проверка нажатия клавиши
int 16h
jz NextTask     ; Не нажата - переключить задачу
xor ah,ah       ; Нажата - извлечь клавишу из очереди
int 16h
jmp Fin         ; и прекратить переключение задач
; Переключение задачи
NextTask:
inc Task        ; Определение номера новой задачи
mov ax,Task   
xor dx,dx
div N           ; через остаток от деления на N
mov Task,dx
mov bp,sp       ; Вычисление смещения задачи в тасклисте
mov si,Task
shl si,2
les si,[bp+si]
mov ax,es       ; Переключение на стек задачи
mov ss,ax
; Восстановление контекста
mov sp,es:[si]  ; Определение реальной вершины стека
pop ax          ; Восстановление регистров
pop bx
pop cx
pop dx
pop si
pop di
pop bp
pop ds
pop es
iret            ; Возобновление задачи
Switch endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
; Фиктивный сегмент для определения размера программы
ZSEG segment
ZSEG ends
end Start
В этой версии SuperOS из-за отсутствия необходимости не был реализован возврат ей управления завершившимися задачами. Для этого в конец стека задачи при её инициализации нужно дополнительно занести указатель на функцию, которая будет выгружать задачу из памяти и удалять её из тасклиста. Ну и функцию саму написать, разумеется.
Распаковав архив в диск C:\ досбокса и запустив SuperOS со списком задач через батник, получим результат, показанный на скрине.
Миниатюры
Многозадачность, алгоритм кругового планирования  
Вложения
Тип файла: zip SuperOS.zip (4.4 Кб, 13 просмотров)
3
Эксперт CЭксперт С++
 Аватар для liv
5120 / 4574 / 855
Регистрация: 07.10.2015
Сообщений: 9,462
24.02.2019, 00:02
Kukuxumushu, шикарно, слов нет В понедельник посмотрю внимательно. С первого взгляда все должно работать.
Я ж не говорил, что это неправильный подход, а только то, что начинающему программисту проще был бы первый подход
А с готовым решением ТС просто обязана с ним ознакомиться И сразить препода наповал
0
Asm/C++/Delphi/Py/PHP/VBA
 Аватар для Jin X
6816 / 2055 / 239
Регистрация: 14.12.2014
Сообщений: 4,320
Записей в блоге: 12
24.02.2019, 18:33
Интересная тема. Я всё не читал, но добавил ещё одну фичу в код Kukuxumushu: регулировку скорости переключения. Теперь рисуется плавно, без рывков (можно увеличить коэффициент до 16, напрмиер, будет ещё плавнее, хотя при 4 уже нормально)

Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
; SuperOS v0.1.1
; Preemptive multitasking realmode operation system by Konstantin Korotkov
; 0.1 -> 0.1.1 version update by Jin X ;)))
 
TMR_SPEED_COEF = 8 ; коэффициент увеличения скорости (степень двойки: от 1 до 256) [Jin X]
 
; Набор инструкций - 80186, формат - exe
.186
.model small
.stack 100h
.data
OldTmr dw ?,?   ; Исходный вектор таймера
TaskList dw ?,? ; Указатель на начало тасклиста
Task dw 0       ; Номер текущей задачи
N dw 0          ; Кол-во задач
 
; Счётчик пропусков вызова системного обработчика прерывания по таймеру [Jin X]
if TMR_SPEED_COEF ne 1
TmrSkipCounter db 0
endif
 
.code
Start:
; Возврат лишней памяти в кучу DOS
; DS=ES=PSP
mov bx,ZSEG
mov ax,es
sub bx,ax       ; Определение размера программы
mov ah,4ah
int 21h
; Загрузка задач
xor bh,bh
mov bl,ds:[80h]
mov cx,bx
mov byte ptr[bx+81h],' ' ; Замена терминатора командной строки пробелом
cld
xor dx,dx
mov di,82h      ; Игнорирование начального пробела
mov al,' '
S:jcxz Run      ; Парсинг командной строки
  push di       ; Сохранение адреса начала имени файла в качестве параметра LoadFile
  repne scasb   ; Поиск пробела
  mov byte ptr[di-1],0   ; Нуль-терминирование имени файла
  call LoadFile ; Загрузка задачи
  jc S          ; Загрузка не удалась - продолжить парсинг
  push bx       ; Загрузка удалась - сохранение параметров задачи в тасклист
  push si 
  inc dx        ; Инкремент счётчика задач
  jmp S
; Запуск переключения задач
Run:
test dx,dx      ; Если ни одной задачи нет
jz Quit         ; то сразу выйти
mov ax,@data    ; DS=@data
mov ds,ax
mov N,dx        ; Сохранение счётчика задач
mov TaskList,ss ; Сохранение указателя на тасклист
mov TaskList[2],sp
mov ax,3508h    ; Сохранение вектора прерывания таймера
int 21h
mov OldTmr,bx
mov OldTmr[2],es
mov ax,2508h    ; Подмена прерывания таймера
push ds
push cs
pop ds
mov dx,offset Switch
int 21h
pop ds
 
; Изменения скорости таймера для повышения плавности [Jin X]
if TMR_SPEED_COEF ne 1
mov al,34h
out 43h,al
xor al,al
out 40h,al
mov al,256/TMR_SPEED_COEF
out 40h,al
endif
 
jmp NextTask    ; Запуск первой задачи
; Завершение работы ОС
Fin:
 
; Восстановление скорости таймера [Jin X]
if TMR_SPEED_COEF ne 1
mov al,36h  ; можно и 34h
out 43h,al
xor al,al
out 40h,al
out 40h,al
endif
 
push ds         ; Восстановление вектора прерывания таймера
mov ax,2508h
lds dx,dword ptr[OldTmr]
int 21h
pop ds
mov cx,N        ; Освобождение памяти задач
mov ah,49h
F:pop es        ; Извлечение указателя конца стека
  pop es        ; Извлечение адреса загрузки
  int 21h       ; Освобождение памяти
  loop F        ; Для всех задач
Quit:
mov ax,4c00h    ; Выход в DOS
int 21h
 
 
;;;;;;;;;;;;    ФУНКЦИИ   ;;;;;;;;;;;;
; Функуия загрузки задачи
;   Параметры:
; [bp+4] - Адрес имени файла (ASCIIZ)
;   Возвращаемые значения:
; bx - адрез загруженной задачи
; si - вершина стека задачи
LoadFile proc
enter 6,0
push ax
push cx
push dx
; Открытие файла
mov dx,[bp+4]
mov ax,3d00h
int 21h
jc LoadErr      ; Ошибка - ничего не делать
mov [bp-2],ax   ; Файловый описатель
; Получение размера файла
mov bx,ax
xor cx,cx
xor dx,dx
mov ax,4202h
int 21h  
mov [bp-4],ax   ; Реальный размер файла
; Выделение памяти
mov bx,ax
add bx,15       ; Вычисление кол-ва параграфов
shr bx,4
add bx,10h      ; Добавление стека задачи - 100h
mov si,bx
shl si,4
sub si,2        ; Конец стека задачи
mov ah,48h
int 21h
mov [bp-6],ax   ; Адрес загрузки задачи
; Установка указателя на начало файла
mov bx,[bp-2]
xor cx,cx
xor dx,dx
mov ax,4200h
int 21h
; Загрузка образа в память
push ds
mov ds,[bp-6]
mov cx,[bp-4]
mov ah,3fh
int 21h 
; Инициализация контекста задачи
mov [si],si         ; Адрес реальной вершины стека
sub word ptr[si],24
mov [si-2],202h     ; flags (if=1) 
mov [si-4],ds       ; cs
mov [si-6],dx       ; ip=0
mov [si-8],ds       ; es=cs
mov [si-10],ds      ; ds=cs
mov [si-12],dx      ; bp=0
mov [si-14],dx      ; di=0
mov [si-16],dx      ; si=0
mov [si-18],dx      ; dx=0
mov [si-20],dx      ; cx=0
mov [si-22],dx      ; bx=0
mov [si-24],dx      ; ax=0
pop ds
; Закрытие файла
mov bx,[bp-2]
mov ah,3eh
int 21h
mov bx,[bp-6] 
LoadErr:
pop dx 
pop cx
pop ax
leave
ret 2
LoadFile endp
 
; Процедура переключения задач по прерыванию таймера
; Флаги и адрес возврата в прерванную задачу сохранены в стек прерыванием
Switch proc
push es         ; Сохранение контекста текущей задачи в её же стек
push ds
push bp
push di
push si
push dx
push cx
push bx
push ax
mov dx,sp       ; Запоминание sp задачи
mov ax,@data    ; Поиск конца стека задачи через тасклист
mov ds,ax          ; Переключение стека на системный
mov ss,TaskList
mov sp,TaskList[2]
mov bp,sp          ; Вычисление смещения задачи в тасклисте
mov si,Task
shl si,2
les di,[bp+si]
mov es:[di],dx  ; Обновление реальной вершины стека
; Системная обработка прерывания
 
; Пропуск "лишних" вызовов системного обработчика прерывания по таймеру [Jin X]
if TMR_SPEED_COEF ne 1
dec [TmrSkipCounter]
if TMR_SPEED_COEF ne 256
and [TmrSkipCounter],TMR_SPEED_COEF-1
endif
jnz SkipOldTmr
endif
 
pushf           ; Вызов системного обработчика
call dword ptr[OldTmr]
 
SkipOldTmr:     ; [Jin X]
 
mov al,20h      ; Сброс контроллера прерываний
out 20h,al
mov ah,1        ; Проверка нажатия клавиши
int 16h
jz NextTask     ; Не нажата - переключить задачу
xor ah,ah       ; Нажата - извлечь клавишу из очереди
int 16h
jmp Fin         ; и прекратить переключение задач
; Переключение задачи
NextTask:
inc Task        ; Определение номера новой задачи
mov ax,Task   
xor dx,dx
div N           ; через остаток от деления на N
mov Task,dx
mov bp,sp       ; Вычисление смещения задачи в тасклисте
mov si,Task
shl si,2
les si,[bp+si]
mov ax,es       ; Переключение на стек задачи
mov ss,ax
; Восстановление контекста
mov sp,es:[si]  ; Определение реальной вершины стека
pop ax          ; Восстановление регистров
pop bx
pop cx
pop dx
pop si
pop di
pop bp
pop ds
pop es
iret            ; Возобновление задачи
Switch endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
; Фиктивный сегмент для определения размера программы
ZSEG segment
ZSEG ends
end Start
4
 Аватар для Kukuxumushu
1624 / 806 / 146
Регистрация: 13.06.2015
Сообщений: 3,266
24.02.2019, 18:54
Jin X, я кстати отказался от этой идеи как расово неверной, поскольку таймер меняет скорость хода и системных часов. А вот как бы сделать это всё независимо???
0
Asm/C++/Delphi/Py/PHP/VBA
 Аватар для Jin X
6816 / 2055 / 239
Регистрация: 14.12.2014
Сообщений: 4,320
Записей в блоге: 12
24.02.2019, 18:56
Цитата Сообщение от Kukuxumushu Посмотреть сообщение
я кстати отказался от этой идеи как расово неверной, поскольку таймер меняет скорость хода и системных часов
Посмотри код, я учёл этот момент и обрабатываю его, так что на ход часов это не повлияет
Все мои доработки помечены как [Jin X].
1
 Аватар для Kukuxumushu
1624 / 806 / 146
Регистрация: 13.06.2015
Сообщений: 3,266
24.02.2019, 19:00
Кстати, тему можно выделить и поместить в раздел "Программирование ОС"

Добавлено через 3 минуты
Jin X, а всё, супер! Я понял твою идею. Апдейт принимается в релиз))
0
3 / 3 / 2
Регистрация: 19.03.2016
Сообщений: 19
02.03.2019, 19:23  [ТС]
Всем снова привет!

Не ожидала, что эта задача так заинтересует знатоков) Особенно удивила SuperOS от Kukuxumushu и доработка от Jin X. Только я пока не разбирала, как оно работает.. Сама суть понятна)
Не подскажите, кстати, почему здесь принципиально использовать прерывание 08h? И с какой целью нужно вызывать старый обработчик в новом? Из-за часов? А если это было бы прерывание 1ch, то старый вызывать не нужно?

Constantin Cat, не поняла, что Вы имели в виду... Но если делать просто вывод номера задачи по очереди, разве это не будет обычным выводом в цикле?

По рекомендациям liv и ФедосеевПавел у меня получилось вот такое (TASM):
Кликните здесь для просмотра всего текста

Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
DOSSEG
.MODEL Tiny
.STACK 128*(3+1)    ; 512 для 3 задач и основной программы
.DATA
num_thread = 3                      ; кол-во задач
num_thread_notEnd db ?                  ; кол-во задач, которые еще хотят выполняться
stack_size = 128                        ; размер стека
arr_sp_thread dw num_thread dup(?)          ; массив указателей стеков задач
main_sp dw ?                            ; указатель на вершину основного стека
arr_numLoops dw 500, 500, 500               ; кол-во выводов номера задачи (счетчик для цикла)
cur_thread db 0                         ; номер текущей задачи
old_handler dd 0                        ; старый вектор обработчика
 
.CODE
task_thread proc far    ; описание задачи 
    push cx
    pop cx
    sti
    cmp cx, 0
    je @@exit
@@cycl:
    push dx         ; берем номер задачи в ax
    pop ax
    mov ah, 0eh     ; функция вывода 
    add al, 31h         ; добавляем для вывода числа
    mov bx, 0h      ; номер страницы
    push cx         ; запоминаем число повторений цикла
    mov cx, 1h      ; указываем число повторений символа
    int 10h             ; выполняем 
    
    ; для задержки выполнения цикла: потопчимся на месте
    mov cx, 1000    
@@Outer:
    push cx         
    mov cx,0        
@@Inner:
    loop @@Inner    
    pop cx          
    loop @@Outer    
    
    pop cx          ; восстанавливаем число повторений цикла
    loop @@cycl     ; пока в cx не ноль  
@@exit:
    ret                 ; выйдет в обработчик, потому что в стеке лежит адрес возврата
task_thread endp
 
dispatcher proc far
    cli             ; запрещаем прерывания
    pop si          ; берем адрес, откуда были вызваны
    cmp si, offset exit ; сверяемся, вызов из главной проги или по прерыванию
    push si
    jne @@StartDispatch ; если по прерыванию
    xor bx, bx
    jmp @@InitFirstTask ; если это первый запуск
    
@@StartDispatch:
    ; сохраняем контекст в стеке текущей задачи
    push dx
    push cx
    push ax
    push bx
    push si
    push di
    push ds
    push es
 
    xor bx, bx
    mov ax, @data
    mov ds, ax
    mov es, ax
    xor ax, ax
    mov bl, [cur_thread]        ; берем номер последней обрабатываемой задачи    
    mov si, bx
    add si, bx
    cmp cx, 0               ; если повторили вывод нужное кол-во раз,
    jne @@SaveTask      ; то больше не выполняем эту задачу, а иначе сохраняем ее
    ; зануляем адрес стека, чтобы потом случайно не запустить завершившуюся задачу
    mov arr_sp_thread[si], word ptr 0       
    dec num_thread_notEnd   ; уменьшаем счетчик задач, которые еще хотят выполняться
    ; сделаем проверку, что если не осталось задач для выполнения, то выходим из программы
    cmp [num_thread_notEnd], 0
    je @@ExitProg
    jmp @@InitTask      ; иначе запускаем следующую
@@SaveTask:             ; сохраняем указатель стека
    mov ax, sp  
    mov arr_sp_thread[si], ax
@@InitTask:
    inc bx              ; берем следующий номер задачи
@@InitFirstTask:
    mov si, bx
    add si, bx
    cmp bx, num_thread  ; т.к. нумерация с 0, то проверяем, что не равно кол-ву задач
    jne @@LoadSP        ; и тогда загружаем указатель стека
    mov bx, 0           ; иначе переходим на первую задачу
    mov si, bx
    add si, bx
@@LoadSP:
    ; проверка на то, что задача не завершена
    cmp arr_sp_thread[si], 0
    je @@InitTask       ; если завершена, переходим к следующей
    mov sp, arr_sp_thread[si]
    mov [cur_thread], bl
    
    ;восстановление контекста в обратном порядке
    pop es
    pop ds
    pop di
    pop si
    pop bx
    pop ax
    pop cx
    pop dx
    sti             ; разрешаем прерывания
    
    iret                ; переходим на выполнение задачи
@@ExitProg:
    mov sp, [main_sp]   ; выходим в основную программму, переключив указатель стека
    sti             ; разрешаем прерывания
    
    iret    
dispatcher endp
 
Start:
    mov ax, @data
    mov ds, ax
    mov es, ax
    xor ax, ax  
    
    mov ax, 3           ; установили режим вывода
    int 10h 
    mov num_thread_notEnd, num_thread   ; запомнили кол-во задач, которые должны отработать
    
    ; инициализируем стеки для задач
    mov [main_sp], sp   ; запомнили указатель стека
    mov si, sp          ; сохраним sp
    sub si, stack_size  ; отступили для следующей задачи
initStack:
    mov bx, word ptr cur_thread ; берем номер текущей задачи
    mov sp, si                  ; загружаем в sp указатель стека для инициализируемой задачи
    
    xor ax, ax
    push ax;0
    ; кладем сегмент и адрес для возврата (в обработчик)
    mov ax, cs      
    push ax
    xor ax, ax
    mov ax, offset dispatcher
    push ax
    xor ax, ax
    push ax;0           
    
    ; кладем сегмент и адрес задачи
    mov ax, cs      
    push ax
    xor ax, ax
    mov ax, offset task_thread
    push ax
    xor ax, ax
 
    push word ptr cur_thread        ; dx - номер задачи
    add bx, bx
    push arr_numLoops[bx]       ; cx - кол-во повторений
    push ax;0                   ; ax
    push ax;0                   ; bx
    push ax;0                   ; si
    push ax;0                   ; di
    push ds
    push es
    mov arr_sp_thread[bx], sp       ; сохраняем sp для запуска задачи
    
    inc cur_thread              ; берем следующую задачу
    sub si, stack_size
    cmp cur_thread, num_thread
    jne initStack
    ; окончание инициализации стеков
    
    mov sp, [main_sp]           ; возвращаем основной стек
    
    ; замена обработчика таймера
    mov ax, 351ch 
    int 21h
    mov word ptr old_handler, bx
    mov word ptr old_handler+2, es
    cli
    mov ax, 251ch 
    mov dx, offset dispatcher
    int 21h
 
    sub [main_sp], 4    ; смещаем сохраненный указатель, чтобы выйти корректно    
    
callFromMain:
    call dispatcher
 
exit: 
    ; возвращаем старый обработчик
    lds dx, old_handler
    mov ax, 251ch 
    int 21h
    ; завершение
    mov ax, 4c00h
    int 21h
END Start


Работает, долго отлаживала, но есть проблемка. У меня есть некоторые догадки, но я не уверена в них..

В общем: при запуске с командной строки по очереди работает каждая задача и "до упора", то есть пока не достигнет конца (выведет нужное количество раз свой номер). Это не очень похоже на то, что я хотела бы видеть, потому что здесь не очевидно, что прерванная задача на половине потом возобновляется.
При запуске с дебаггера, с точкой останова в обработчике, становится видно, что задачи действительно чередуются. Собственно, такой же результат, какой я получаю после останова, я бы хотела получать при обычном запуске, но что-то, видно, я делаю не так..
Приложила к сообщению скриншоты результатов в этих двух случаях.

Некоторые предположения насчет причины происходящего.
1) В какой-то момент выполнения задачи (процедуры task_thread) устанавливается запрет прерывания.
Для исключения такого специально устанавливаю флаг, чтобы прерывания срабатывали. В эту процедуру по непонятным мне причинам управление приходит со снятым флагом.. Но эта теория маловероятна, потому что, опять же, при проходе с отладчиком снятия флага замечено не было.
2) При запуске с отладчиком "накапливаются" прерывания, поскольку останов я делаю в блоке, где эти прерывания запрещены.
Об истинности этого судить не могу. К такому выводу пришла только после многих попыток запуска кода..

По итогу вопрос: как же мне реализовать наглядное чередование?
Очень надеюсь на помощь!
Заранее всем спасибо!
Миниатюры
Многозадачность, алгоритм кругового планирования   Многозадачность, алгоритм кругового планирования  
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
02.03.2019, 19:23

Алгоритм планирования Short Job First
Добрый день форумчане. Собрал код по частям из сети. Суть алгоритма ясна. Не могу в участке кода, отвечающего за реализацию печати графика...

Объяснить программу (Алгоритм планирования, Планировщик)
Друзья, кто нибудь помогите объяснить как работает программа, по какому алгоритму планирования работает Планировщик, что конкретно...

Алгоритм планирования процессов First-Come, First-Served (FCFS)
Проюзал инет и не нашёл вообще информации( Кинте ссылку, на инфу З.Ы. Админ, извини, не знаю в какой раздел форума запостить

Алгоритм планирования Round Robin с квантом времени (переделать программу)
Если кто знаком с этим - подскажите, будьте любезны!!)) Друзья, есть код программы, который представлен ниже. Реализован в Visual Studio...

Недетерминированные алгоритмы: Cоставить алгоритм, который сравнивает с порогом возможное решение задачи планирования работ
Cоставить алгоритм, который сравнивает с порогом возможное решение задачи планирования работ (план работ задать самостоятельно).


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Новые блоги и статьи
Из невошедшего на форум (диалог с ИИ-гугла)
zorxor 29.07.2026
А вот, что интересно, сказал мне ИИ-гугла: Этот текст — эмоциональный пост пользователя под ником zorxor на интернет-форуме (вероятно, посвященном мистике, непознанному или альтернативной науке). . . .
Был праздник вчера, а я и не знал.
kumehtar 28.07.2026
27. 07. 2026г. Intel Core 2 Duo исполнилось 20 лет Новости компьютерного мира и их обсуждение (4) Салют, шампанское, овации! :drink:
Нейтральные знания, чистый код - бла-бла-бла-бла, на самом деле кликбейт и самореклама, плагиат, и вот почему
Hrethgir 27.07.2026
То-есть отклонение такой публикации говорит само за себя, и пусть только возьмут на вооружение после отклонения публикации - это будет чистейшим актом плагиата. Отклонял Хабр. Дословно, отклонённая. . .
тв 16 бой ии
anaschu 27.07.2026
Великий Перелом ИИ: Как уравнения ОДУ Radau дожали цензурные фильтры Алисы Фиксируем в мемофонде Теории Всего беспрецедентный факт в истории ИИ-зондирования. В затяжном многораундовом. . .
мв 15. непроверенное, возможно, глюк
anaschu 27.07.2026
НАУЧНО-АНАЛИТИЧЕСКИЙ ОТЧЕТ. РАЗДЕЛ 1. 1: «НАУКА» (РАСШИРЕННАЯ СТЕХИОМЕТРИЧЕСКАЯ И ГЕНЕТИЧЕСКАЯ ВЕРСИЯ)Тема: Теоретическое обоснование инвариантности 19-мерного тензорного ядра непрерывных ОДУ и. . .
Очистка реквизитов и табличных частей документа при копировании (вариант 2)
Maks 26.07.2026
Алгоритм из решения ниже разработан на примере нетипового документа "ЗаявкаНаРаботу", разработанного в КА2. Задача: Заменить алгоритм запрета копирования документов для сотрудников с ролью "Стажер",. . .
Доктрина интенционального знания - Доктрина для портала "Срез".
Hrethgir 25.07.2026
Может найдётся кто захочет оценить доктрину. . . Написания правил участия для меня роскошь, требующая лимита времени, поэтому все сообщения не прошедшие модерацию будут видны только участникам портала,. . .
сукцессия 44. Решил подать на припринт в межународные сервисы препринтов. Но нужно одобрение от ученых
anaschu 25.07.2026
Английский вариант. Пока кто то не одобрит мою личность, мне не получиться это опубликовать на препринте. Но заявку на публикацию статьи я сегодня подам.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru