/
Task.c
executable file
·303 lines (254 loc) · 8.72 KB
/
Task.c
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
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
#include "Task.h"
#include "Descriptor.h"
// 스케줄러 관련 자료구조
static SCHEDULER gs_stScheduler;
static TCBPOOLMANAGER gs_stTCBPoolManager;
//==============================================================================
// 태스크 풀과 태스크 관련
//==============================================================================
/**
* 태스크 풀 초기화
*/
void kInitializeTCBPool( void )
{
int i;
kMemSet( &( gs_stTCBPoolManager ), 0, sizeof( gs_stTCBPoolManager ) );
// 태스크 풀의 어드레스를 지정하고 초기화
gs_stTCBPoolManager.pstStartAddress = ( TCB* ) TASK_TCBPOOLADDRESS;
kMemSet( TASK_TCBPOOLADDRESS, 0, sizeof( TCB ) * TASK_MAXCOUNT );
// TCB에 ID 할당
for( i = 0 ; i < TASK_MAXCOUNT ; i++ )
{
gs_stTCBPoolManager.pstStartAddress[ i ].stLink.qwID = i;
}
// TCB의 최대 개수와 할당된 횟수를 초기화
gs_stTCBPoolManager.iMaxCount = TASK_MAXCOUNT;
gs_stTCBPoolManager.iAllocatedCount = 1;
}
/**
* TCB를 할당 받음
*/
TCB* kAllocateTCB( void )
{
TCB* pstEmptyTCB;
int i;
if( gs_stTCBPoolManager.iUseCount == gs_stTCBPoolManager.iMaxCount )
{
return NULL;
}
for( i = 0 ; i < gs_stTCBPoolManager.iMaxCount ; i++ )
{
// ID의 상위 32비트가 0이면 할당되지 않은 TCB
if( ( gs_stTCBPoolManager.pstStartAddress[ i ].stLink.qwID >> 32 ) == 0 )
{
pstEmptyTCB = &( gs_stTCBPoolManager.pstStartAddress[ i ] );
break;
}
}
// 상위 32비트를 0이 아닌 값으로 설정해서 할당된 TCB로 설정
pstEmptyTCB->stLink.qwID = ( ( QWORD ) gs_stTCBPoolManager.iAllocatedCount << 32 ) | i;
gs_stTCBPoolManager.iUseCount++;
gs_stTCBPoolManager.iAllocatedCount++;
if( gs_stTCBPoolManager.iAllocatedCount == 0 )
{
gs_stTCBPoolManager.iAllocatedCount = 1;
}
return pstEmptyTCB;
}
/**
* TCB를 해제함
*/
void kFreeTCB( QWORD qwID )
{
int i;
// 태스크 ID의 하위 32비트가 인덱스 역할을 함
i = qwID & 0xFFFFFFFF;
// TCB를 초기화하고 ID 설정
kMemSet( &( gs_stTCBPoolManager.pstStartAddress[ i ].stContext ), 0, sizeof( CONTEXT ) );
gs_stTCBPoolManager.pstStartAddress[ i ].stLink.qwID = i;
gs_stTCBPoolManager.iUseCount--;
}
/**
* 태스크를 생성
* 태스크 ID에 따라서 스택 풀에서 스택 자동 할당
*/
TCB* kCreateTask( QWORD qwFlags, QWORD qwEntryPointAddress )
{
TCB* pstTask;
void* pvStackAddress;
pstTask = kAllocateTCB();
if( pstTask == NULL )
{
return NULL;
}
// 태스크 ID로 스택 어드레스 계산, 하위 32비트가 스택 풀의 오프셋 역할 수행
pvStackAddress = ( void* ) ( TASK_STACKPOOLADDRESS + ( TASK_STACKSIZE *
( pstTask->stLink.qwID & 0xFFFFFFFF ) ) );
// TCB를 설정한 후 준비 리스트에 삽입하여 스케줄링될 수 있도록 함
kSetUpTask( pstTask, qwFlags, qwEntryPointAddress, pvStackAddress,
TASK_STACKSIZE );
kAddTaskToReadyList( pstTask );
return pstTask;
}
/**
* 파라미터를 이용해서 TCB를 설정
*/
void kSetUpTask( TCB* pstTCB, QWORD qwFlags, QWORD qwEntryPointAddress,
void* pvStackAddress, QWORD qwStackSize )
{
// 콘텍스트 초기화
kMemSet( pstTCB->stContext.vqRegister, 0, sizeof( pstTCB->stContext.vqRegister ) );
// 스택에 관련된 RSP, RBP 레지스터 설정
pstTCB->stContext.vqRegister[ TASK_RSPOFFSET ] = ( QWORD ) pvStackAddress +
qwStackSize;
pstTCB->stContext.vqRegister[ TASK_RBPOFFSET ] = ( QWORD ) pvStackAddress +
qwStackSize;
// 세그먼트 셀렉터 설정
pstTCB->stContext.vqRegister[ TASK_CSOFFSET ] = GDT_KERNELCODESEGMENT;
pstTCB->stContext.vqRegister[ TASK_DSOFFSET ] = GDT_KERNELDATASEGMENT;
pstTCB->stContext.vqRegister[ TASK_ESOFFSET ] = GDT_KERNELDATASEGMENT;
pstTCB->stContext.vqRegister[ TASK_FSOFFSET ] = GDT_KERNELDATASEGMENT;
pstTCB->stContext.vqRegister[ TASK_GSOFFSET ] = GDT_KERNELDATASEGMENT;
pstTCB->stContext.vqRegister[ TASK_SSOFFSET ] = GDT_KERNELDATASEGMENT;
// RIP 레지스터와 인터럽트 플래그 설정
pstTCB->stContext.vqRegister[ TASK_RIPOFFSET ] = qwEntryPointAddress;
// RFLAGS 레지스터의 IF 비트(비트 9)를 1로 설정하여 인터럽트 활성화
pstTCB->stContext.vqRegister[ TASK_RFLAGSOFFSET ] |= 0x0200;
// 스택과 플래그 저장
pstTCB->pvStackAddress = pvStackAddress;
pstTCB->qwStackSize = qwStackSize;
pstTCB->qwFlags = qwFlags;
}
//==============================================================================
// 스케줄러 관련
//==============================================================================
/**
* 스케줄러를 초기화
* 스케줄러를 초기화하는데 필요한 TCB 풀과 init 태스크도 같이 초기화
*/
void kInitializeScheduler( void )
{
// 태스크 풀 초기화
kInitializeTCBPool();
// 준비 리스트 초기화
kInitializeList( &( gs_stScheduler.stReadyList ) );
// TCB를 할당 받아 실행 중인 태스크로 설정하여, 부팅을 수행한 태스크를 저장할 TCB를 준비
gs_stScheduler.pstRunningTask = kAllocateTCB();
}
/**
* 현재 수행 중인 태스크를 설정
*/
void kSetRunningTask( TCB* pstTask )
{
gs_stScheduler.pstRunningTask = pstTask;
}
/**
* 현재 수행 중인 태스크를 반환
*/
TCB* kGetRunningTask( void )
{
return gs_stScheduler.pstRunningTask;
}
/**
* 태스크 리스트에서 다음으로 실행할 태스크를 얻음
*/
TCB* kGetNextTaskToRun( void )
{
if( kGetListCount( &( gs_stScheduler.stReadyList ) ) == 0 )
{
return NULL;
}
return ( TCB* ) kRemoveListFromHeader( &( gs_stScheduler.stReadyList ) );
}
/**
* 태스크를 스케줄러의 준비 리스트에 삽입
*/
void kAddTaskToReadyList( TCB* pstTask )
{
kAddListToTail( &( gs_stScheduler.stReadyList ), pstTask );
}
/**
* 다른 태스크를 찾아서 전환
* 인터럽트나 예외가 발생했을 때 호출하면 안됨
*/
void kSchedule( void )
{
TCB* pstRunningTask, * pstNextTask;
BOOL bPreviousFlag;
// 전환할 태스크가 있어야 함
if( kGetListCount( &( gs_stScheduler.stReadyList ) ) == 0 )
{
return ;
}
// 전환하는 도중 인터럽트가 발생하여 태스크 전환이 또 일어나면 곤란하므로 전환하는
// 동안 인터럽트가 발생하지 못하도록 설정
bPreviousFlag = kSetInterruptFlag( FALSE );
// 실행할 다음 태스크를 얻음
pstNextTask = kGetNextTaskToRun();
if( pstNextTask == NULL )
{
kSetInterruptFlag( bPreviousFlag );
return ;
}
pstRunningTask = gs_stScheduler.pstRunningTask;
kAddTaskToReadyList( pstRunningTask );
// 다음 태스크를 현재 수행 중인 태스크로 설정한 후 콘텍스트 전환
gs_stScheduler.pstRunningTask = pstNextTask;
kSwitchContext( &( pstRunningTask->stContext ), &( pstNextTask->stContext ) );
// 프로세서 사용 시간을 업데이트
gs_stScheduler.iProcessorTime = TASK_PROCESSORTIME;
kSetInterruptFlag( bPreviousFlag );
}
/**
* 인터럽트가 발생했을 때, 다른 태스크를 찾아 전환
* 반드시 인터럽트나 예외가 발생했을 때 호출해야 함
*/
BOOL kScheduleInInterrupt( void )
{
TCB* pstRunningTask, * pstNextTask;
char* pcContextAddress;
// 전환할 태스크가 없으면 종료
pstNextTask = kGetNextTaskToRun();
if( pstNextTask == NULL )
{
return FALSE;
}
//==========================================================================
// 태스크 전환 처리
// 인터럽트 핸들러에서 저장한 콘텍스트를 다른 콘텍스트로 덮어쓰는 방법으로 처리
//==========================================================================
pcContextAddress = ( char* ) IST_STARTADDRESS + IST_SIZE - sizeof( CONTEXT );
// 현재 태스크를 얻어서 IST에 있는 콘텍스트를 복사하고, 현재 태스크를 준비 리스트로
// 옮김
pstRunningTask = gs_stScheduler.pstRunningTask;
kMemCpy( &( pstRunningTask->stContext ), pcContextAddress, sizeof( CONTEXT ) );
kAddTaskToReadyList( pstRunningTask );
// 전환해서 실행할 태스크를 Running Task로 설정하고 콘텍스트를 IST에 복사해서
// 자동으로 태스크 전환이 일어나도록 함
gs_stScheduler.pstRunningTask = pstNextTask;
kMemCpy( pcContextAddress, &( pstNextTask->stContext ), sizeof( CONTEXT ) );
// 프로세서 사용 시간을 업데이트
gs_stScheduler.iProcessorTime = TASK_PROCESSORTIME;
return TRUE;
}
/**
* 프로세서를 사용할 수 있는 시간을 하나 줄임
*/
void kDecreaseProcessorTime( void )
{
if( gs_stScheduler.iProcessorTime > 0 )
{
gs_stScheduler.iProcessorTime--;
}
}
/**
* 프로세서를 사용할 수 있는 시간이 다 되었는지 여부를 반환
*/
BOOL kIsProcessorTimeExpired( void )
{
if( gs_stScheduler.iProcessorTime <= 0 )
{
return TRUE;
}
return FALSE;
}