노트 (6) - 프로 세 스
15514 단어 운영 체제
프로 세 스 설명:
프로 세 스 가 걸 릴 때 프로 세 스 정보 가 이 데이터 구조 에 기록 되 고 프로 세 스 가 다시 시 작 될 때 이 정 보 를 다시 읽 을 수 있 는 데이터 구조 가 필요 합 니 다.
가장 간단 한 프로 세 스
프로 세 스 전환 과정: - 1. 프로 세 스 A 실행 중 - 2. 시계 중단 발생, ring 1 - > ring 0, 시계 중단 프로세서 시작 - 3. 프로 세 스 스케줄 링, 다음 실행 할 프로 세 스 B 지정 - 4. 프로 세 스 B 복구, ring 0 - > ring 1 - 5. 프로 세 스 B 실행 중
프로 세 스 테이블
프로 세 스 정 보 를 저장 하 는 데이터 구 조 를 프로 세 스 시트 또는 프로 세 스 제어 블록, 즉 PCB 라 고 합 니 다.
프로 세 스 스 스 택 과 커 널 스 택
esp 의 위 치 는 3 개의 서로 다른 영역 에 나타 납 니 다. - 프로 세 스 스 택 – 프로 세 스 가 실 행 될 때 자신의 스 택 - 프로 세 스 표 – 프로 세 스 상태 정 보 를 저장 하 는 데이터 구조 - 커 널 스 택 – 프로 세 스 스케줄 링 모듈 이 실 행 될 때 사용 하 는 스 택 입 니 다.
1 단계 - 링 0 - > 링 1
첫 번 째 프로 세 스 를 시작 합 니 다. 우 리 는 링 0 에서 링 1 로 이동 하 는 명령 을 사용 합 니 다. 이동 에 성공 하면 A 프로 세 스 가 실행 되 고 있다 고 볼 수 있 습 니 다.
프로 세 스 테이블 데이터 구조
typedef struct s_stackframe { /* proc_ptr points here ↑ Low */
u32 gs; /* ┓ │ */
u32 fs; /* ┃ │ */
u32 es; /* ┃ │ */
u32 ds; /* ┃ │ */
u32 edi; /* ┃ │ */
u32 esi; /* ┣ pushed by save() │ */
u32 ebp; /* ┃ │ */
u32 kernel_esp; /*
u32 ebx; /* ┃ ↑ */
u32 edx; /* ┃ │ */
u32 ecx; /* ┃ │ */
u32 eax; /* ┛ │ */
u32 retaddr; /* return address for assembly code save() │ */
u32 eip; /* ┓ │ */
u32 cs; /* ┃ │ */
u32 eflags; /* ┣ these are pushed by CPU during interrupt │ */
u32 esp; /* ┃ │ */
u32 ss; /* ┛ ┷High */
}STACK_FRAME;
typedef struct s_proc {
STACK_FRAME regs; /* process registers saved in stack frame */
u16 ldt_sel; /* gdt selector giving ldt base and limit */
DESCRIPTOR ldts[LDT_SIZE]; /* local descriptors for code and data */
int ticks; /* remained ticks */
int priority;
u32 pid; /* process id passed in from MM */
char p_name[16]; /* name of the process */
}PROCESS;
프로 세 스 를 복구 하려 면 esp 를 이 구조 체 의 시작 부분 으로 가리 키 고 일련의 pop 명령 을 실행 하여 레지스터 값 을 팝 업 합 니 다.
ring 0 - > ring 1 을 실현 하고 스 택 의 정보 도 ss 와 esp 두 개의 레지스터 밖 에 없다.다음 링 1 - > 링 0 을 준비 해 야 하기 때문에 iretd 로 돌아 가기 전에 tss. esp 0 이 정확 하 다 는 것 을 보증 해 야 합 니 다.
restart:
mov esp, [p_proc_ready]
lldt [esp + P_LDT_SEL]
lea eax, [esp + P_STACKTOP]
mov dword [tss + TSS3_S_SP0], eax
restart_reenter:
dec dword [k_reenter]
pop gs
pop fs
pop es
pop ds
popad
add esp, 4
iretd
프로 세 스 테이블 및 관련 데이터 구조 대응 관계:
첫 번 째 프로 세 스 의 시작 과정:
2 단계 - 풍부 한 인 터 럽 트 처리 프로그램
할당 tss. esp 0
링 0 에서 링 1 까지 추진 하 는 전환 은 명령 iretd 가 실 행 될 때 바로 완 성 됩 니 다. 목표 코드 의 cs, eip, ss, esp 등 은 모두 스 택 에서 얻 은 것 입 니 다. 이것 은 간단 합 니 다.하지만 링 1 에서 링 0 으로 전환 할 때 는 TSS 를 사용 하지 않 을 수 없다.스 택 의 정보 도 ss 와 esp 두 개의 레지스터 밖 에 없다.다음 링 1 - > 링 0 을 준비 해 야 하기 때문에 iretd 로 돌아 가기 전에 tss. esp 0 이 정확 하 다 는 것 을 보증 해 야 합 니 다.
현재 인 터 럽 트 루틴: 인 터 럽 트 가 발생 하기 시 작 했 을 때 esp 의 값 은 방금 TSS 에서 채널 의 프로 세 스 표 A 의 regs 의 최고 주 소 였 습 니 다. 그리고 레지스터 의 값 이 프로 세 스 표 에 압축 되 었 습 니 다. 그리고 esp 는 regs 의 최저 주 소 를 가리 키 고 tss. esp0 의 값 을 설정 하여 다음 프로 세 스 가 중단 되 었 을 때 사용 할 준 비 를 합 니 다.
내부 창고
mov esp, StackTop ;
;...
mov esp, [p_proc_ready] ;
중단 재 입
프로그램 을 중단 하 는 것 은 수 동적 이다.이러한 끼 워 넣 기 현상 을 피하 기 위해 서, 우 리 는 인 터 럽 트 프로그램 이 자신 이 끼 워 넣 고 실행 하고 있 는 지 아 닌 지 를 알 수 있 는 방법 을 생각해 야 한다.전역 변 수 를 설정 하면 됩 니 다.현재 우리 의 처 리 는 현재 끼 워 넣 은 것 을 발견 하면 마지막 으로 뛰 어 내 려 처리 프로그램의 실행 을 중단 합 니 다.
다 중 프로 세 스
프로 세 스 A 에서 프로 세 스 B 로 전환 하기 전에 현장 을 어떻게 보존 하고 복원 합 니까? (즉, 각 레지스터 의 값)나중에 얘 기 할 게 요.
하나의 프로 세 스 는 하나의 프로 세 스 와 스 택 만 있 으 면 실행 할 수 있 습 니 다.
typedef void (*task_f) ();
typedef struct s_task {
task_f initial_eip;
int stacksize;
char name[32];
}TASK;
TASK task_table[NR_TASKS] =
{{TestA, STACK_SIZE_TESTA, "TestA"},
{TestB, STACK_SIZE_TESTB, "TestB"}};
void TestA()
{
//...
}
void TestB()
{
//...
}
초기 화
iretd
에서 TASK 구조 에서 서로 다른 작업 의 입구 주소, 스 택 지붕 과 프로 세 스 이름 을 읽 은 다음 해당 프로 세 스 표 항목 에 값 을 부여 합 니 다. for(i=0;istrcpy(p_proc->p_name, p_task->name); // name of the process
p_proc->pid = i; // pid
p_proc->ldt_sel = selector_ldt;
memcpy(&p_proc->ldts[0], &gdt[SELECTOR_KERNEL_CS >> 3],
sizeof(DESCRIPTOR));
p_proc->ldts[0].attr1 = DA_C | PRIVILEGE_TASK << 5;
memcpy(&p_proc->ldts[1], &gdt[SELECTOR_KERNEL_DS >> 3],
sizeof(DESCRIPTOR));
p_proc->ldts[1].attr1 = DA_DRW | PRIVILEGE_TASK << 5;
p_proc->regs.cs = ((8 * 0) & SA_RPL_MASK & SA_TI_MASK)
| SA_TIL | RPL_TASK;
//...
p_proc->regs.eip = (u32)p_task->initial_eip;
p_proc->regs.esp = (u32)p_task_stack;
p_task_stack -= p_task->stacksize;
p_proc++;
p_task++;
selector_ldt += 1 << 3;
}
LDT
GDT 프로 세 스 의 LDT 설명 자 를 채 웁 니 다.
int i;
PROCESS* p_proc = proc_table;
u16 selector_ldt = INDEX_LDT_FIRST << 3;
for(i=0;i>3],
vir2phys(seg2phys(SELECTOR_KERNEL_DS),
proc_table[i].ldts),
LDT_SIZE * sizeof(DESCRIPTOR) - 1,
DA_LDT);
p_proc++;
selector_ldt += 1 << 3;
}
모든 프로 세 스 는 GDT 에 LDT 설명 자 를 대응 합 니 다.프로 세 스 마다 LDT 가 있 습 니 다. 따라서 프로 세 스 를 전환 할 때 ldtr 를 다시 불 러 와 야 합 니 다.
다 중 프로 세 스 구현 - A 와 B 프로 세 스 교체 실행
하나의 프로 세 스 가 어떻게 '수면' 상태 에서 '운행' 상태 로 변 합 니까?
esp 를 프로 세 스 표 항목 의 시작 부분 에 가리 키 고 실행
proc_table
한 후에 일련의 lldt
명령 을 거 쳐 각 레지스터 의 값 을 복원 하 는 것 이 아 닙 니 다.모든 정 보 는 프로 세 스 표 에 포함 되 어 있 습 니 다.따라서 서로 다른 프로 세 스 를 복원 하려 면 esp 를 다른 프로 세 스 표 로 가리 키 기만 하면 됩 니 다.커 널 스 택 을 떠 날 때 esp 에 값 을 부여 합 니 다. ;...
mov esp, StackTop ;
;...
call clock_handler
;...
mov esp, [p_proc_ready] ;
lldt [esp + P_LDT_SEL]
lea eax, [esp + P_STACKTOP]
mov dword [tss + TSS3_S_SP0], eax
;...
프로 세 스 가 어떻게 '실행' 상태 에서 '수면' 상태 로 변 합 니까?
CPU 가 이 프로 세 스 의 코드 명령 을 실행 하지 않 을 때 이 프로 세 스 가 잠 들 었 다 고 볼 수 있 습 니 다.그럼 이 레지스터 의 값 은 어떻게 저장 합 니까?이 프로 세 스 의 프로 세 스 표 에 저 장 된 것 이 분명 합 니 다. '수면' 상태 에서 '실행' 상태 로 바 뀌 는 것 이 바로 여기에서 얻 은 정보 이기 때 문 입 니 다.보호 할 시 기 는 프로 세 스 스케줄 이 바 뀌 기 전에 있 습 니 다.우 리 는 시계 가 프로 세 스 전환 을 중단 할 때 이렇게 씁 니 다.
hwint00: ; Interrupt routine for irq 0 (the clock).
sub esp, 4
pushad ; `.
push ds ; |
push es ; |
push fs ; |
push gs ; /
mov dx, ss
mov ds, dx
mov es, dx
inc byte [gs:0] ; 0 , 0
;...
mov esp, StackTop ;
;...
call clock_handler
;...
mov esp, [p_proc_ready] ;
lldt [esp + P_LDT_SEL]
lea eax, [esp + P_STACKTOP]
mov dword [tss + TSS3_S_SP0], eax
;...
pop gs ; `.
pop fs ; |
pop es ; |
pop ds ; |
popad ; /
add esp, 4
iretd
쉽게 말 하면 호출
pop
전에 저 희 는 프로 세 스 A 의 레지스터 에서 esp 가 가리 키 는 스 택, 즉 프로 세 스 표 A (ring 1 에서 ring 0 으로 뛰 고 esp 의 값 은 TSS 에서 밤 이 적은 ring 0 의 esp 값 으로 변 합 니 다) 를 저장 합 니 다.이후 esp 는 프로 세 스 B 의 스 택 으로 전환 되 었 기 때문에 clock_handler
프로 세 스 표 B 에 저 장 된 레지스터 값 이 나 왔 습 니 다.시스템 호출
사용자 프로 세 스 는 특권 급 관계 로 일부 권한 이 높 은 메모리 영역 에 접근 할 수 없습니다. 시스템 호출 을 통 해 만 이 루어 집 니 다. 프로그램 과 운영 체제 간 의 다리 입 니 다.인 터 럽 트 로 시스템 호출 을 편리 하 게 실현 할 수 있다.
간단 한 시스템 호출 을 실현 하 다.
운영 체 제 는 응용 프로그램 에
pop
시스템 호출 을 제공 하여 현재 총 몇 번 의 시계 가 중단 되 었 는 지 얻 을 수 있 습 니 다.시스템 호출 과정: - 1. "질문", 운영 체제 에 자신 이 원 하 는 것 을 알려 줍 니 다. -2. 운영 체제 "찾기", 즉 처리; -3. '대답', 즉 결 과 를 프로 세 스에 되 돌려 주 는 것 이다.;syscall.asm
_NR_get_ticks equ 0 ; global.c sys_call_table !
INT_VECTOR_SYS_CALL equ 0x90
global get_ticks ;
bits 32
[section .text]
get_ticks:
mov eax, _NR_get_ticks
int INT_VECTOR_SYS_CALL
ret
get_ticks()
함수 포인터 배열 로 모든 구성원 이 하나의 함 수 를 가리 키 며 해당 하 는 시스템 호출 을 처리 합 니 다.메모: sys_call_table
커 널 호출 입 니 다. 반환 값 을 응용 프로 세 스에 알려 주 려 면 프로 세 스 표 eax 의 위치 에 함수 의 반환 값 을 두 어야 합 니 다. 프로 세 스 P 가 실 행 될 때 eax 에 올 바른 반환 값 을 설치 할 수 있 습 니 다.;kernel.asm
sys_call:
call save
sti
call [sys_call_table + eax * 4]
mov [esi + EAXREG - P_STACKBASE], eax ; eax , P eax 。
cli
ret
프로 세 스 스케줄 링
프로 세 스 우선 순위 스케줄 링
중단 이 발생 했 을 때 다음 실행 할 프로 세 스 를 우선 순위 로 선택해 야 합 니 다.
PUBLIC void schedule()
{
PROCESS* p;
int greatest_ticks = 0;
while (!greatest_ticks) {
for (p = proc_table; p < proc_table+NR_TASKS; p++) {
if (p->ticks > greatest_ticks) {
greatest_ticks = p->ticks;
p_proc_ready = p;
}
}
if (!greatest_ticks) {
for (p = proc_table; p < proc_table+NR_TASKS; p++) {
p->ticks = p->priority;
}
}
}
}
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
독서 노트문제1: 한 파일에 10000000개의 기록이 포함되어 있으며, 각 기록의 내용은 7자리의 정수이다.기록은 중복되지 않는다.파일 내용을 읽는 프로그램이 필요하고, 이 기록을 정렬한 후 파일을 출력해야 하며, 메모리는...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.