Linux-0.11
Curso de teoría: https://www.bilibili.com/video/bv1d4411v7u7
Referencias útiles (versión comentada de Linux-0.11 ): https://github.com/beride/linux0.11-1
La rama principal es el código fuente de Linux-0.11
Ver la diferencia de código: Pull Solicitud Seleccionar la rama correspondiente y compararla con
Boot del sistema operativo Lab0 ✔️ Lab0 (Branch Lab0)
Reescribir bootsect.s completa principalmente las siguientes funciones:
- Bootsect.s puede imprimir un mensaje "xxx está arrancando ..." en la pantalla.
Reescribir setup.s completa principalmente las siguientes funciones:
- Bootsect.s puede completar la carga de configuración y saltar a la configuración. Inicie la dirección de ejecución. y setup.s emite una línea "Ahora estamos en configuración" a la pantalla. Setup.s puede obtener al menos un parámetro de hardware básico (como parámetros de memoria, parámetros de tarjeta gráfica, parámetros de disco duro, etc.), almacenarlo en una dirección específica en la memoria y emitirlo a la pantalla. setup.s ya no carga el kernel de Linux, solo mantenga la información anterior que se muestra en la pantalla.
Libro de instrucciones experimentales: https://www.lanqiao.cn/courses/115/labs/568/document/
✔️ LAB1 implementa llamadas del sistema (Branch Lab1)
- El número total de llamadas del sistema modificadas en kernel/system_call.s es 74.
- Agregue una macro en incluir/unistd.h para indicar el posicionamiento de la tabla de funciones de llamada.
- Escriba dos funciones (SYS_IAM, SYS_WHOAMI) Definiciones de funciones en incluir/linux/sys.h. Y agregue dos nuevas funciones a la tabla de funciones después.
- Se agregó kernel/quién.c para implementar dos llamadas al sistema.
- Modificar makfile
- OBJS aumenta quién depende (quién.o)
- Agregue condiciones de generación de dependencia para quién y quién.
- hacer ejecutar ./run para ingresar al subsistema de Linux y agregue las definiciones macro de IAM y Whoami en /usr/include/unistd.h (igual que el punto 2).
- Escribir un programa de prueba en el estado del usuario prueba si es exitoso.
Libro de instrucciones experimentales: https://www.lanqiao.cn/courses/115/labs/569/document/
✔️ Lab2 implementa el seguimiento de la trayectoria de operación de proceso (Branch Lab2)
- Process.c implementa un escenario que simula una computación híbrida de CPU y computación IO, y lo ejecuta de manera múltiple.
- Imprima varios estados de conmutación de procesos del núcleo (tiempo de estado PID) a /var/process.log.
- Después de que la función init () en init/main ingresa a la declaración de estado del usuario, asociar el descriptor de archivo 3 a /var/process.log.
- Implemente la función fprintk () en kernel/printk para imprimir la salida en el registro Process.log.
- Encuentre el punto de conmutación de estado y agregue fprintk () para escribir registros.
- Process Start Kernel/System_call.s -> Sys_fork (llamar a Copy_Process)
- Ejecutar, Bloquear State Switch Kernel/Sched.C -> Programar SYS_PAUSE Sleep_on Interruptible_Sleep_on Wake_UP
- Salir kernel/exit.c -> do_exit sys_waitpid
- Nota: El CCC en Linux-0.11 no se puede compilar // comentado.
Libro de instrucciones experimentales: https://www.lanqiao.cn/courses/115/labs/570/document/
✔️ Lab3 reemplaza el proceso TSS original en Linux0.11 para cambiar a la conmutación de la pila (Branch Lab3)
El método de conmutación original es a través de TSS, que es equivalente a una instantánea del registro. Se reemplaza directamente en el sitio a través de las instrucciones proporcionadas por Intel, que es más lento. El cambio de pila es más eficiente.
kernel/sched.c
- El método Switch_TO original se basa en TSS Switch, y queremos comentarlo en el archivo de encabezado.
- New Switch_to, requiere dos parámetros (1: puntero a la siguiente PCB 2: La posición de la siguiente tarea en la matriz se usa para cambiar LDT).
kernel/system_call.s
- Escriba la implementación del ensamblaje de switch_to
- Cambio de PCB
- Reescribe la posición de la pila del núcleo TSS (TSS se conserva en este momento, pero solo hay un TSS en el mundo, y no se usa para cambiar de proceso).
- Cambiar la pila de núcleo
- Interruptor LDT
núcleo/bifurcación
- Sosten la sesión de la configuración de TSS en PCB.
- Agregue una nueva pila variable de miembros del miembro a PCB, que se utiliza para almacenar información de la pila del núcleo.
- Escriba información de pila aquí y deje que PCB la variable de miembro apunte al puntero.
Libro de instrucciones experimentales: https://www.lanqiao.cn/courses/115/labs/571/document/
Referencia: https://blog.csdn.net/qq_42518941/article/details/119182097
✔️ Lab4 Implementa llamadas al sistema Semaphore en Linux0.11 (Branch Lab4)
Puntos de conocimiento previos a la tecla
- kernel/sched.c sleep_on pase el encabezado de cola de espera entrante, establezca la corriente en el estado de bloqueo y ejecute activamente el horario (). TMP almacena la cola de bloqueo original. Cuando se despiertan, las colas de bloqueo están configuradas para Runnable.
- kernel/sched.c wake_up despierta todos los PCB bloqueados. Solo el líder del equipo de atención se puede ver en el programa, pero debe leerse en combinación con Sleep_on. Una vez combinado, encontrará que el cabezal de despertar se despertará posteriormente.
Escriba la aplicación "PC.C" para resolver el problema clásico del consumidor del productor y complete las siguientes funciones:
- Establecer un proceso de productor, n procesos de consumo (n> 1)
- Crear un búfer compartido con archivos
- El proceso del productor escribe enteros 0, 1, 2, ..., m, m> = 500 al búfer a su vez
- El proceso del consumidor lee del búfer, lee uno a la vez y elimina los números de lectura del búfer, y luego genera el ID de proceso y + números a la salida estándar.
- El búfer solo puede ahorrar hasta 10 números al mismo tiempo
Implementar semáforo
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 abre un semáforo
- Si el semáforo es actualmente igual a cero, bloqueando el proceso
- Agregar un semáforo
- Apagar un semáforo
El enfoque de Wait and Post
- esperar
- Llame a kernel/sched.c sleep_on para esperar el bloqueo
- Use la interrupción de Linux (Core único Linux0.11) para proteger la zona crítica
- menos un valor de SEM
- correo
- Agregue uno al valor de SEM. Si el valor es mayor que 0, llame al kernel/sched.c wake_up para despertar el proceso bloqueado por este semáforo.
- Use la interrupción de Linux (Core único Linux0.11) para proteger la zona crítica
Libro de instrucciones experimentales: https://www.lanqiao.cn/courses/115/labs/572/document/
Mapeo y intercambio de direcciones LAB5 (Branch Lab5)
Dirección lógica -> GDT -> LDT -> Tabla de página -> Dirección física
Conocimiento de la teoría de instrumentos
Selector de segmento
El acceso a la tabla de descriptor global de GDTR se realiza a través del "Selector de segmento" (registro de segmento en modo real)
15 3 2 1 0
| índice | | RPL |
- 3-15 es el índice de descriptor, que indica el descriptor del segmento requerido en la tabla de descriptor.
- 2 Para indicar si el selector se selecciona en GDT o en LDT (0 representa GDT 1 representa LDT).
- 0-1 es el nivel de privilegio para seleccionar.
El selector de segmento incluye tres partes: Índice de descriptor (índice), TI y nivel de privilegio de solicitud (RPL). Su pieza del índice (índice de descriptor) indica la posición descriptor del segmento requerido en la tabla de descriptor. Desde esta posición, el descriptor correspondiente se puede encontrar en función de la dirección base de la tabla descriptor almacenada en GDTR. Luego, la dirección base del segmento en la tabla de descriptor más la dirección lógica (SEL: Offset) se puede convertir en una dirección lineal. El valor TI en el selector de segmento es solo uno 0 o 1. 0 significa que el selector se selecciona en GDT, y 1 significa que el selector se selecciona en LDT. El nivel de privilegio de solicitud (RPL) representa el nivel de privilegio del selector, y hay 4 niveles de privilegio (Nivel 0, Nivel 1, Nivel 2 y Nivel 3).
Nota sobre el nivel de privilegio: cada segmento en la tarea tiene un nivel específico. Cada vez que un programa intenta acceder a un segmento, el nivel de privilegio que tiene el programa se compara con el nivel de privilegio a acceder para determinar si se puede acceder al segmento. La convención del sistema es que la CPU solo puede acceder a segmentos del mismo nivel de privilegio o en un nivel de privilegio más bajo.
Por ejemplo, dar la dirección lógica: 21h: 12345678H convertida en dirección lineal
a. Selector SEL = 21H = 000000000000100 0 01 (b) significa: el índice del selector = 4, es decir, 0100, y se selecciona el cuarto descriptor en GDT; Ti = 0 significa que el selector se selecciona en GDT; El último 01 significa nivel de privilegio rpl = 1.
b. Offset = 12345678H Si la dirección base del segmento descrita en el cuarto descriptor de GDT en este momento es 1111111h, entonces la dirección lineal = 111111h + 12345678H = 23456789H.
Visita GDT
- Primero obtenga la dirección base GDT del registro GDTR.
- Luego, en GDT, el descriptor del segmento se valora en el índice de posición de altura selector de segmento de 13 bits.
- Obtenga la dirección base, agregue el desplazamiento para obtener la dirección lineal.
Acceder a LDT
- Primero obtenga la dirección base GDT del registro GDTR.
- Obtenga el índice de posición del segmento donde el LDT se encuentra desde el registro LDTR (LDTR es 13 bits más alto).
- El descriptor del segmento LDT se obtiene en GDT con este índice de posición para obtener la dirección base del segmento LDT.
- Use el selector de segmento para obtener el descriptor del segmento del segmento LDT.
- Obtenga la dirección base, agregue el desplazamiento para obtener la dirección lineal.
Libro de instrucciones experimentales: https://www.lanqiao.cn/courses/115/labs/573/document/
✔️ Control del dispositivo de terminal LAB6 (Branch Lab6)
- Cuando se produce la interrupción del teclado, se elimina el código de escaneo del teclado y el código de escaneo se procesa de acuerdo con la tabla Key_Table.
- Complete la escritura de función correspondiente a la tecla F12.
- Después del procesamiento, coloque los caracteres con el código de escaneo correspondiente en put_queue.
- Llame a do_tty_interrupt para el procesamiento final, donde COPY_TO_COOKED realiza el preprocesamiento final y luego llame a Con_write para emitir a la tarjeta gráfica.
- escribir -> sys_write -> tty_write -> con_write.
Libro de instrucciones experimentales: https://www.lanqiao.cn/courses/115/labs/574/document/
✔️ Implementación del sistema de archivos PROC LAB7 (Branch Lab7)
Conocimiento teórico
disco
Leer y escribir discos mecánicos requiere tres parámetros para ubicar
- Cilindro (c)
- Cabeza (h)
- Sector (s)
block = C * ( H * S ) + H * S + S ;
Dividiendo varios sectores en un bloque para mejorar la eficiencia del disco IO (Linux0.11 Dividiendo 2 sectores en un solo bloque). Para un nivel superior, solo necesita ingresar el número de bloqueo de lectura y escritura para realizar el disco IO.
División: Bloque de arranque | Super Block | mapa de bits inode | Mapa de bits de datos | Bloque de inodo | Bloque de datos
documento
Use FCB (inode en Linux0.11) para almacenar información del archivo, incluidos diferentes tipos de archivos (por ejemplo: archivos de dispositivos, archivos de directorio ...).
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 ; // 更新标志。
}; El número de bloque del archivo en el disco se almacena en el inodo y en alguna otra información de descripción del archivo. Puede haber múltiples niveles de guía de posición de bloqueo de disco, que se dividen en índice directo e índice indirecto para obtener la posición de bloque.
Tabla de contenido
Encuentre los datos en el número de bloque de disco de datos correspondiente de acuerdo con el inodo del directorio, que contiene el número de bloque de disco de inodo del subdirectorio que existe en el directorio. Busque capa por capa y puede encontrar la ubicación del directorio de destino final.
Libro de instrucciones experimentales: https://www.lanqiao.cn/courses/115/labs/575/document/