노트 (6) - 프로 세 스

15514 단어 운영 체제
우 리 는 하나의 단독 임무 에 사용 되 는 모든 것 을 하나의 LDT 에 밀봉 할 수 있다. 이런 사상 은 다 중 임무 처리 의 초기 형태 이다.다 중 작업 에 사용 되 는 세그먼트 유형 은 다음 과 같 습 니 다. LDT 를 사용 하여 모든 프로그램 작업 을 격 리 하 는 방법 은 바로 관건 적 인 보호 수요 중 하나 입 니 다.
프로 세 스 설명:
프로 세 스 가 걸 릴 때 프로 세 스 정보 가 이 데이터 구조 에 기록 되 고 프로 세 스 가 다시 시 작 될 때 이 정 보 를 다시 읽 을 수 있 는 데이터 구조 가 필요 합 니 다.
가장 간단 한 프로 세 스
프로 세 스 전환 과정: - 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;
            }
        }
    }
}

좋은 웹페이지 즐겨찾기