리눅스 -0.11
이론 과정 : https://www.bilibili.com/video/bv1d4411v7u7
유용한 참조 ( Linux-0.11 주석 버전) : https://github.com/beride/linux0.11-1
메인 브랜치 는 Linux-0.11 소스 코드입니다
코드 차이보기 차이 : 당기 요청 요청 해당 브랜치를 선택하고 메인과 비교하십시오.
✔️ Lab0 운영 체제 부트 (Branch Lab0)
bootsect.s는 주로 다음 기능을 완료합니다.
- bootsect.s는 화면에서 "xxx가 부팅 중입니다 ..."메시지를 인쇄 할 수 있습니다.
Setup.s는 주로 다음 기능을 완료합니다.
- bootsect.s는 Setup.s의로드를 완료하고 설정으로 점프 할 수 있습니다. 실행 주소를 시작하십시오. SETUP.S는 "이제 우리는 설정 중입니다"라인을 화면에 출력합니다. Setup.s는 하나 이상의 기본 하드웨어 매개 변수 (예 : 메모리 매개 변수, 그래픽 카드 매개 변수, 하드 디스크 매개 변수 등)를 얻고 메모리의 특정 주소에 저장하고 화면에 출력 할 수 있습니다. Setup.s는 더 이상 Linux 커널을로드하지 않고 위의 정보를 화면에 표시하십시오.
실험 지시서 : https://www.lanqiao.cn/courses/115/labs/568/document/
✔️ lab1은 시스템 호출을 구현합니다 (Branch Lab1)
- Kernel/System_Call.s로 수정 된 총 시스템 통화 수는 74입니다.
- 콜링 함수 테이블 위치를 나타내려면 포함/unistd.h에 매크로를 추가하십시오.
- 포함/linux/sys.h에서 두 가지 함수 (sys_iam, sys_whoami) 함수 정의를 작성하십시오. 기능 테이블에 두 가지 새로운 기능을 추가하십시오.
- 두 개의 시스템 호출을 구현하기 위해 커널/WHO.C를 추가했습니다.
- makefile을 수정하십시오
- OBJS는 WHO 의존성을 증가시킵니다 (WHO.O)
- 종속성에서 WHO 및 WHO.O에 대한 종속성 생성 조건 추가
- Linux 서브 시스템을 입력하려면 ./run을 실행하고 /usr/include/unistd.h에서 iam과 whoami의 거시 정의를 추가하십시오 (지점 2와 동일).
- 사용자 상태에서 테스트 프로그램을 작성하면 성공 여부를 테스트합니다.
실험 지시서 : https://www.lanqiao.cn/courses/115/labs/569/document/
✔️ lab2는 프로세스 작동 궤적 추적 (Branch Lab2)의 추적을 구현합니다.
- Process.c는 하이브리드 CPU 컴퓨팅 및 IO 컴퓨팅을 시뮬레이션하는 시나리오를 구현하고 다중 프로세스 방식으로 실행됩니다.
- 커널 프로세스 전환 (PID 상태 시간)의 여러 상태를 /var/process.log로 인쇄하십시오.
- init/main에서 init () 함수가 사용자 상태 명령문을 입력 한 후 파일 디스크립터를 연결하여 /var/process.log로 연결합니다.
- Kernel/Printk에서 fprintk () 함수를 구현하여 출력을 프로세스 .log log에 인쇄하십시오.
- 상태 스위칭 지점을 찾아서 fprintk ()를 추가하여 로그를 작성하십시오.
- 프로세스 시작 kernel/system_call.s-> sys_fork (copy_process에 전화)
- 실행, 차단 상태 스위치 커널/스케줄 -> 스케줄 Sys_Pause sleep_on interprupible_sleep_on wake_up
- exit 커널/exit.c-> do_exit sys_waitpid
- 참고 : Linux-0.11의 GCC는 // 댓글을 작성할 수 없습니다.
실험 지시서 : https://www.lanqiao.cn/courses/115/labs/570/document/
✔️ Lab3는 Linux0.11의 원래 TSS 프로세스를 대체하여 스택 스위칭으로 전환합니다 (Branch Lab3)
원래 스위칭 방법은 TSS를 통해서 레지스터의 스냅 샷에 해당합니다. 인텔이 제공 한 지침을 통해 현장으로 직접 대체됩니다. 스택 스위칭이 더 효율적입니다.
커널/스케줄
- 원래 switch_to 메소드는 TSS 스위치를 기반으로하며 헤더 파일에 댓글을 달고 싶습니다.
- New Switch_to에는 두 개의 매개 변수가 필요합니다 (1 : 다음 PCB에 대한 포인터 2 : 배열에서 다음 작업의 위치는 LDT를 전환하는 데 사용됩니다).
커널/system_call.s
- Switch_to 어셈블리 구현을 작성하십시오
- PCB 전환
- TSS 커널 스택 위치를 다시 작성하십시오 (TSS는 현재 유지되지만 세계에는 하나의 TS가 있으며 프로세스 전환을 수행하는 데 사용되지 않습니다.)
- 커널 스택을 전환합니다
- LDT 스위치
커널/포크
- PCB의 TSS 설정에서 로그 아웃하십시오.
- 커널 스택 정보를 저장하는 데 사용되는 새 멤버 변수 -커널 스택을 PCB에 추가합니다.
- 여기에 스택 정보를 작성하고 PCB가 멤버 변수가 포인터를 가리 키게하십시오.
실험 지시서 : https://www.lanqiao.cn/courses/115/labs/571/document/
참조 : https://blog.csdn.net/qq_42518941/article/details/119182097
lab4는 linux0.11 (Branch Lab4)에서 세마포어 시스템 호출을 구현합니다.
키 프리 키 지식 포인트
- Kernel/Sched.c Sleep_on 들어가는 대기 대기 큐 헤더를 통과하고, 현재를 차단 상태로 설정하고, Schedule ()를 적극적으로 실행합니다. TMP는 원래 차단 큐를 저장합니다. 깨어 났을 때 차단 대기열은 모두 실행 가능하도록 설정됩니다.
- Kernel/Sched.c Wake_up은 차단 된 모든 PCB를 깨우고 있습니다. 이 프로그램에서 모닝 팀 리더 만 볼 수 있지만 Sleep_on과 함께 읽어야합니다. 일단 결합되면, 당신은 웨이크 업 헤드가 이후에 깨어날 것임을 알게 될 것입니다.
클래식 프로듀서 소비자 문제를 해결하려면 응용 프로그램 "PC.C"를 작성하고 다음 기능을 작성하십시오.
- 생산자 프로세스 설정, N 소비자 프로세스 (N> 1)
- 파일로 공유 버퍼를 만듭니다
- 생산자 프로세스는 정수 0, 1, 2,…, m, m> = 500을 차례로 버퍼에 씁니다.
- 소비자 프로세스는 버퍼에서 읽히고 한 번에 하나씩 읽은 다음 버퍼에서 읽기 숫자를 삭제 한 다음 프로세스 ID와 + 숫자를 표준 출력으로 출력합니다.
- 버퍼는 동시에 최대 10 개의 숫자 만 절약 할 수 있습니다.
세마포어를 구현하십시오
sem_t * sem_open ( const char * name , unsigned int value );
int sem_wait ( sem_t * sem );
int sem_post ( sem_t * sem );
int sem_unlink ( const char * name );
- SEM_OPEN은 세마포어를 엽니 다
- 세마포어가 현재 0 인 경우 프로세스를 차단합니다.
- 세마포어를 추가하십시오
- 세마포어를 끄십시오
대기 및 게시의 초점
- 기다리다
- Kernel/Sched.c Sleep_on에 전화하여 차단을 기다리십시오
- 리눅스 인터럽트 (Linux0.11 단일 코어)를 사용하여 임계 영역을 보호하십시오.
- SEM의 하나의 값을 뺀 값
- 우편
- SEM 값에 하나를 추가하십시오. 값이 0보다 큰 경우 Kernel/Sched.c Wake_up에 전화 하여이 세마포어에 의해 차단 된 프로세스를 깨우십시오.
- 리눅스 인터럽트 (Linux0.11 단일 코어)를 사용하여 임계 영역을 보호하십시오.
실험 지시서 : https://www.lanqiao.cn/courses/115/labs/572/document/
lab5 주소의 매핑 및 공유 (Branch Lab5)
논리 주소 -> gdt-> ldt-> 페이지 테이블 -> 물리적 주소
도구 이론 지식
세그먼트 선택기
GDTR의 글로벌 디스크립터 테이블에 대한 액세스는 "세그먼트 선택기"(실제 모드의 세그먼트 레지스터)를 통해 수행됩니다.
15 3 2 1 0
| 인덱스 | | rpl |
- 3-15는 디스크립터 테이블에서 필요한 세그먼트의 설명자를 나타내는 디스크립터 인덱스입니다.
- 2는 선택기가 GDT 또는 LDT에서 선택되는지 여부를 나타냅니다 (0은 GDT 1을 나타냅니다. LDT를 나타냅니다).
- 0-1은 선택할 권한 수준입니다.
세그먼트 선택기에는 디스크립터 인덱스 (인덱스), TI 및 요청 권한 레벨 (RPL)의 세 부분이 포함됩니다. 인덱스 (디스크립터 인덱스) 부분은 디스크립터 테이블에서 필요한 세그먼트의 설명자 위치를 나타냅니다. 이 위치에서 해당 설명자는 GDTR에 저장된 디스크립터 테이블 기본 주소를 기반으로 찾을 수 있습니다. 그런 다음 디스크립터 테이블의 세그먼트베이스 주소와 논리 주소 (SEL : 오프셋)를 선형 주소로 변환 할 수 있습니다. 세그먼트 선택기의 TI 값은 하나의 0 또는 1. 0에 불과한다. 0은 선택기가 GDT에서 선택되었음을 의미하고 1은 선택기가 LDT에서 선택되었음을 의미한다. 요청 권한 레벨 (RPL)은 선택기의 권한 레벨을 나타내며 4 개의 권한 레벨 (레벨 0, 레벨 1, 레벨 2 및 레벨 3)이 있습니다.
권한 수준에 대한 참고 사항 : 작업의 각 세그먼트에는 특정 수준이 있습니다. 프로그램이 세그먼트에 액세스하려고 할 때마다, 프로그램에 의한 권한 수준은 액세스 할 권한 레벨과 비교하여 세그먼트에 액세스 할 수 있는지 여부를 결정합니다. 시스템 컨벤션은 CPU가 동일한 권한 수준 또는 더 낮은 권한 수준의 세그먼트 만 액세스 할 수 있다는 것입니다.
예를 들어, 논리 주소를 제공합니다 : 21H : 12345678H 선형 주소로 변환
에이. 선택기 SEL = 21H = 0000000000100 0 01 (b)는 다음을 의미합니다. 선택기 = 4, 즉 0100 및 GDT의 네 번째 설명자가 선택됩니다. Ti = 0은 선택기가 GDT에서 선택되었음을 의미합니다. 마지막 01은 권한 레벨 RPL = 1을 의미합니다.
비. 오프셋 = 12345678H 이시기에 GDT의 네 번째 설명자에 설명 된 세그먼트 기본 주소가 1111111H 인 경우 선형 주소 = 111111H + 12345678H = 23456789H입니다.
GDT를 방문하십시오
- 먼저 GDTR 레지스터에서 GDT 기본 주소를 얻으십시오.
- 그런 다음 GDT에서 세그먼트 디스크립터는 세그먼트 선택기 높은 13 비트 위치 지수에 가치가 있습니다.
- 기본 주소를 가져 와서 오프셋을 추가하여 선형 주소를 얻으십시오.
액세스 ldt
- 먼저 GDTR 레지스터에서 GDT 기본 주소를 얻으십시오.
- LDT가 LDTR 레지스터에서 LDT가있는 세그먼트의 위치 색인을 얻으십시오 (LDTR은 13 비트 더 높습니다).
- LDT 세그먼트 디스크립터는 LDT 세그먼트 기본 주소를 얻기 위해이 위치 지수와 함께 GDT에서 얻습니다.
- 세그먼트 선택기를 사용하여 LDT 세그먼트에서 세그먼트 디스크립터를 얻으십시오.
- 기본 주소를 가져 와서 오프셋을 추가하여 선형 주소를 얻으십시오.
실험 지시서 : https://www.lanqiao.cn/courses/115/labs/573/document/
✔️ Lab6 터미널 장치 제어 (Branch Lab6)
- 키보드 인터럽트가 발생하면 키보드 스캔 코드가 제거되고 key_table 테이블에 따라 스캔 코드가 처리됩니다.
- F12 키에 해당하는 기능을 작성하십시오.
- 처리 후 해당 스캔 코드가있는 문자를 put_queue에 넣으십시오.
- 최종 프로세싱에 대해서는 do_tty_interrupt에 전화를 걸어 Copy_to_cooked가 최종 전처리를 수행 한 다음 CON_WRITE를 호출하여 그래픽 카드로 출력하십시오.
- 쓰기 -> sys_write-> tty_write-> con_write.
실험 지시서 : https://www.lanqiao.cn/courses/115/labs/574/document/
Lab7 Proc 파일 시스템의 구현 (Branch Lab7)
이론적 지식
디스크
기계 디스크를 읽고 쓰기 위해서는 세 가지 매개 변수가 필요합니다.
- 실린더 (C)
- 머리 (H)
- 부문
block = C * ( H * S ) + H * S + S ;
디스크 IO 효율을 향상시키기 위해 여러 섹터를 하나의 블록으로 나눕니다 (Linux0.11 2 개 섹터를 하나의 블록으로 나누기). 더 높은 레벨의 경우 디스크 IO를 수행하려면 읽기 및 쓰기 블록 번호 만 입력하면됩니다.
부서 : 부트 블록 | 슈퍼 블록 | inode bitmap | 데이터 비트 맵 | inode 블록 | 데이터 블록
문서
FCB (Linux0.11의 Inode)를 사용하여 다양한 유형의 파일 (예 : 장치 파일, 디렉토리 파일)을 포함하여 파일 정보를 저장하십시오.
struct m_inode
{
unsigned short i_mode ; // 文件类型和属性(rwx 位)。
unsigned short i_uid ; // 用户id(文件拥有者标识符)。
unsigned long i_size ; // 文件大小(字节数)。
unsigned long i_mtime ; // 修改时间(自1970.1.1:0 算起,秒)。
unsigned char i_gid ; // 组id(文件拥有者所在的组)。
unsigned char i_nlinks ; // 文件目录项链接数。
unsigned short i_zone [ 9 ]; // 直接(0-6)、间接(7)或双重间接(8)逻辑块号。
/* these are in memory also */
struct task_struct * i_wait ; // 等待该i 节点的进程。
unsigned long i_atime ; // 最后访问时间。
unsigned long i_ctime ; // i 节点自身修改时间。
unsigned short i_dev ; // i 节点所在的设备号。
unsigned short i_num ; // i 节点号。
unsigned short i_count ; // i 节点被使用的次数,0 表示该i 节点空闲。
unsigned char i_lock ; // 锁定标志。
unsigned char i_dirt ; // 已修改(脏)标志。
unsigned char i_pipe ; // 管道标志。
unsigned char i_mount ; // 安装标志。
unsigned char i_seek ; // 搜寻标志(lseek 时)。
unsigned char i_update ; // 更新标志。
}; 디스크 파일의 블록 번호는 inode 및 기타 파일 설명 정보에 저장됩니다. 블록 위치를 얻기 위해 직접 인덱스 및 간접 인덱스로 나뉘어져있는 여러 수준의 디스크 블록 위치 안내가있을 수 있습니다.
목차
디렉토리 inode에 따라 해당 데이터 디스크 블록 번호에서 데이터를 찾으십시오. 디렉토리에 존재하는 서브 디렉토리의 inode 디스크 블록 번호가 포함되어 있습니다. 계층 별 검색 계층과 최종 대상 디렉토리의 위치를 찾을 수 있습니다.
실험 지시서 : https://www.lanqiao.cn/courses/115/labs/575/document/