Un sistema operativo de juguete, 32 bits finalmente implementa un shell interactivo simple 
Lista de tareas pendientes:
Hay dos discos duros en total, el sistema en sí está instalado en el disco principal, y se adopta el modo de arranque de MBR, y se utiliza el proceso de MBR-> Boot Loader-> Kernnel.
MBR se encuentra en 1 sector que comienza LBA 0号扇区
Boot Loader está ubicado en 4 sectores que comienzan con LBA 2号扇区del disco
Kernel se encuentra dentro de 200 sectores que comienzan con el disco LBA 9号扇区
El sistema de archivos se implementa en el disco de esclavos. Esto puede no ser muy razonable. Si el sistema de archivos se basa en la lógica del sistema comercial, debe implementarse ahora, y luego el sistema operativo está instalado en la partición correspondiente.
Paging de memoria, una página es 4KB
La gestión de la memoria adopta la gestión de mapa de bits y se distingue por el tamaño al asignar memoria. Si es mayor de 1024 bytes, se asigna directamente por página.
Si es menos de 1024 bytes, sobre la base de la asignación de arena por página, use la cadena de bloques inactiva en Arena para la asignación y el control.
Por conveniencia, aunque el mecanismo de paginación está habilitado, no se implementa la función de conmutación de las páginas y discos de memoria.

PCB es de 1 página
La esencia central de la programación de subprocesos es cambiar PCB controlando la interrupción del puntero ESP por interrupción del reloj. La prioridad se refleja en la longitud de la porción de tiempo de ejecución de cada hilo.
La implementación del proceso se basa en subprocesos, y la selección de TSS se basa en Linux, utilizando TSS único para respaldar las pilas de nivel 0 y los punteros de la pila de nivel 0. La mayor diferencia de un hilo es que el proceso tiene una dirección de tabla de página en la PCB, que es la mayor diferencia entre un proceso y un hilo. El proceso realmente tiene su propio espacio de memoria virtual independiente.
No hay un algoritmo eficiente en la programación, solo use la programación de bucle de cola
Implementación del hilo inactivo
La implementación del hilo inactivo es muy simple. Cuando la primera vez que obtiene el horario, lo bloquea y lo entregue de la CPU. Cuando el planificador ejecuta el despacho nuevamente, si no se encuentra ningún hilo o proceso listo en la cola lista, el hilo inactivo se despertará. En este momento, el hilo inactivo suspende la CPU a través de hlt . Cuando se usa la rebanada de tiempo y la CPU no ha encontrado un proceso o hilo listo, continúe reemplazando el hilo inactivo con la CPU. En este momento, el inactivo continuará bloqueándose y luego comenzará a repetir el proceso de programación anterior.
// 空载任务
static void idle ( void * arg ) {
while ( 1 ) {
thread_block ( TASK_BLOCKED );
asm volatile ( "sti; hlt" : : : "memory" );
}
}Proceso de proceso
La bifurcación del proceso primero copia la PCB del proceso actual y luego crea una nueva tabla de páginas a través del mapa de bits de grupo virtual del proceso actual. La correspondencia de la dirección virtual es exactamente la misma que en el proceso original. Finalmente, se forja un sitio de interrupción y el proceso infantil se agrega a la cola de programación y espere a que se ejecute la programación. En el sitio de interrupción forjada, la EAX en la PCB del proceso infantil se modifica a 0, lo que significa que el valor de retorno de la bifurcación es 0 en el nuevo proceso, mientras que la EAX en la PCB del proceso principal permanece sin cambios, lo que representa el PID del proceso infantil. El proceso principal regresa a través del final de la llamada del sistema, mientras que el proceso del niño regresa directamente a través de la función de salida de interrupción.
Proceso exec
La implementación de EXEC primero carga el archivo ELF desde el disco a la memoria, luego cambia el nombre del proceso en la PCB del proceso actual y coloca los parámetros requeridos por el proceso que se ejecutará en el registro acordado y modifica EIP al punto de entrada de ELF, la falsificación del sitio de interrupción y el nuevo proceso inmediatamente llamando directamente a la función de salida de interrupción Intr_Exit.
Entre ellos, el punto de entrada de ELF se implementa mediante la implementación de un CRT extremadamente simple, que proporciona una entrada de _Start y empuja los registros de parámetros acordados a la pila de 3 niveles, y llama a la función principal del comando externo para realizar el paso de los parámetros.
[bits 32]
extern main
extern exit
; 这是一个简易版的CRT
; 如果链接时候ld不指定-e main的话,那ld默认会使用_start来充当入口
; 这里的_start的简陋实现,充当了exec调用的进程从伪造的中断中返回时的入口地址
; 通过这个_start, 压入了在execv中存放用户进程参数的两个寄存器。然后call 用户进程main来实现了向用户进程传递参数
section .text
global _start
_start:
;下面这两个要和 execv 中 load 之后指定的寄存器一致
push ebx ;压入 argv
push ecx ;压入 argc
call main
; 压入main的返回值
push eax
call exit ; 不再返回,直接调度别的进程了,这个进程直接被回收了Proceso de espera
Después de la bifurcación y ejecutar un comando local, para no aparecer, el proceso principal debe terminar en el proceso local de la espera del niño.
La implementación aquí es ingresar a la llamada del sistema SYS_WAIT, atravesar toda la cola de proceso, encontrar el proceso cuyo proceso principal es su propio estado suspendido, luego obtener el valor de retorno en su PCB, reciclar las tablas de directorio PCB y página, y eliminarlo de la cola de programación. Si no se encuentra ningún proceso infantil pendiente después del recorrido, bloquee y espere a que el proceso de los niños se despierte.
Salida de proceso
Durante la ejecución, los comandos externos en realidad están envueltos por un CRT simple creado por ellos mismos. La principal del comando externo CRT Call Call obtendrá su valor de retorno al final, lo pasará a salir y luego llame a Salir
La implementación aquí hace principalmente tres cosas:
Todo el proceso de carga de comandos externos y ejecutarlos
Primero, el comando externo debe proporcionar una función int main(int argc, char **argv) . Al vincular, debe traer un CRT start.o Homemade Simple Start.o y finalmente escribir los comandos externos compilados al sistema de archivos.
Cuando se ejecutará un comando externo, el proceso actual bifucea un proceso, execv en el nuevo proceso, el proceso actual ejecuta espera, pasa una dirección para aceptar el valor de retorno del proceso infantil y luego bloquea y espera que el nuevo proceso regrese. El nuevo proceso carga comandos externos desde el sistema de archivos a la memoria en exEVC, y cambia el contenido relevante en PCB a la información de los comandos externos, y modifica el EIP en la pila de interrupción de PCB a la entrada de _Start de CRT (en este momento el nuevo proceso se ha reemplazado por completo con el proceso de comando externo que se ejecuta), y finalmente utiliza la función de salida de intr_exit para que se introduzca el interruptor y luego se reemplazara el comando externo y luego se reemplazara el CRIRT de la transferencia. Cuando la ejecución principal del comando externo finaliza y regresa, CRT llama a salir a través del valor de retorno principal, coloque el valor de retorno principal en sys_exit en la posición correspondiente de la PCB del proceso, recicle todos los recursos, excepto PCB y la tabla de directorio de páginas, y luego despierta el proceso principal y bloquee el proceso infantil. Después de que se despierta el proceso principal, el sistema llame sys_wait obtiene el valor de retorno de la PCB de la PCB de la PCB de la PCB de la PCB de la PCB de la PCB de la PCB del PCB de la PCB del PCB de la PCB, limpia la tabla de directores de página de la PCB del proceso infantil, elimina el proceso infantil de la cola y la mierda global y devuelve el proceso global PID. En este punto, el proceso infantil se ha ejecutado y se recicla por completo.
La implementación del sistema de archivos imita el inodo de sistemas similares a unix
La partición limita el número de inodos 4096. La CPU opera el disco duro de acuerdo con el tamaño del bloque (clúster), y un bloque se establece en un sector, con 512 bytes.
Inode admite 12 bloques directos y 1 tabla indirecta de primer nivel. Un bloque es 512 bytes en un sector, por lo que un solo archivo admite hasta 140 * 512 bytes.
estructura de inodo

Diseño del sistema de archivos

La correspondencia entre el descriptor de archivo e inode

La implementación de la tubería depende de la estructura de archivos en el sistema de archivos. Su esencia es reemplazar el inodo al que la estructura del archivo debe corresponder originalmente con un espacio de búfer de anillo en el espacio del núcleo.
// 因为管道也是当作文件来对待,因此file结构体在针对真实文件和管道是有不同的意义
struct file {
// 文件操作的偏移指针, 当是管道是表示管道打开的次数
uint32_t fd_pos ;
// 文件的操作标志,当是管道是一个固定值0xFFFF
uint32_t fd_flag ;
// 对应的inode指针,当是管道时指向管道的环形缓冲区
struct inode * fd_inode ;
}; Debido a que el espacio del núcleo se comparte, la comunicación entre diferentes procesos se puede lograr a través de tuberías de lectura y escritura. La lectura y la escritura de la tubería se encapsula en sys_write y sys_read , por lo que no hay diferencia entre operar la tubería y operar archivos ordinarios.
La esencia de la redirección es cambiar la dirección de la tabla de descriptor global correspondiente en la tabla de descriptor del archivo PCB, y luego la operación de leer y escribir el descriptor del archivo correspondiente apunta al nuevo archivo.
La implementación del carácter de la tubería | En la cubierta se logra redirigiendo la entrada estándar y la salida estándar a la tubería.
int32_t sys_read ( int32_t fd , void * buf , uint32_t count ) {
if ( fd == stdin_no ) {
if ( is_pipe ( fd )) {
// 从已经重定向好管道中读
ret = pipe_read ( fd , buf , count );
} else {
// 从键盘获取输入
}
} else if ( is_pipe ( fd )) {
// 读管道
ret = pipe_read ( fd , buf , count );
} else {
// 读取普通文件
}
return ret ;
}
int32_t sys_write ( int32_t fd , const void * buf , uint32_t count ) {
if ( fd == stdout_no ) {
if ( is_pipe ( fd )) {
// 向已经重定向好管道中写入
return pipe_write ( fd , buf , count );
} else {
// 向控制台输出内容
}
} else if ( is_pipe ( fd )) {
// 写管道
return pipe_write ( fd , buf , count );
} else {
// 向普通文件写入
}
}