sherecho的个人博客

弱小和无知不是生存的障碍,傲慢才是

linux 0.11 文件系统总结

文件系统

详见:操作系统文件系统。

操作系统中的文件系统可以分为两部分:操作系统内核中或者在硬盘软盘虚拟盘中。一个物理设备可以分为多个逻辑设备,比如一个物理硬盘可以分为多个逻辑硬盘。而一个逻辑设备只有一个文件系统,一个文件系统只包含一个i结点的树结构。一个逻辑设备只能有一个根i结点。

image-20231203234247319

未安装文件系统的磁盘称之为生磁盘,生磁盘也可以作为文件读写,linux中一切皆文件。

生磁盘可以被分区,分区中可以安装文件系统,常见的文件系统有fat32、ext2、ext4等。

MINIX 文件系统与标准 UNIX 的文件系统基本相同。它由 6 个部分组成。分区内可以安装指定文件系统,同一磁盘多个分区文件系统不要求相同。MINIX文件系统布局如下:(下述部分是在磁盘上的)

MINIX文件系统布局
  • 引导块:若作为引导分区,将存放操作系统的引导程序代码,否则空置。

  • 超级块:用于存放磁盘设备上文件系统结构的信息,说明各部分的大小。

  • i节点位图:标记i节点数据元素是否被使用

  • 逻辑块位图:标记磁盘数据块是否被使用

  • i节点区:用于存放inode节点数据,一个文件对应一个inode节点,inode节点存储文件属性数据。

  • 数据区:以固定大小盘块(1k)为单位进行动态分配和回收,用于存储数据,类似内存分页。

    位图:一个比特对应一个逻辑块,0,1代表是否被占用

    删除文件:清理数据块关系清掉,对应逻辑块位图清0,清理i结点和i结点对应位图。

    如果一个物理块有多个逻辑块,上述就罗列着摆放:

    image-20231211195002220

    超级块结构:

阅读全文 »

Linux 0.11 里面的 inline 问题

问题描述

如下所示linux 0.11中main函数关于fork和pause的定义如下:

1
2
3
static inline _syscall0(int,fork) 
static inline _syscall0(int,pause)

本篇文章讨论这里的inline是否有存在的必要,如果去掉会引发什么后果

c 程序运行结构分析

函数调用与执行

为了更好的解决该问题,我们首先需要分析一下c程序运行的结构

以如下一段简单的代码为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
int fun(int a,int b);
int m=10;
int main(){
int i=4;
int j=5;
m=fun(i,j);
return 0;
}
int fun(int a,int b){
int c=0;
c=a+b;
return c;
}
阅读全文 »

第四章笔记

打开终端设备文件及复制文件句柄

1
2
3
4
5
6
7
8
9
10
void init(void)
{
int pid,i;

setup((void *) &drive_info);
(void) open("/dev/tty0",O_RDWR,0);//创建标准输入设备
(void) dup(0);//创建标准输出设备
(void) dup(0);
...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int open(const char * filename, int flag, ...)
{
register int res;
va_list arg;

va_start(arg,flag);
__asm__("int $0x80"
:"=a" (res)
:"0" (__NR_open),"b" (filename),"c" (flag),
"d" (va_arg(arg,int)));
if (res>=0)
return res;
errno = -res;
return -1;
}

在GCC中的内联汇编中,数值操作数约束(numeric operand constraints)如"0"用于指定汇编代码的输入和输出寄存器。该数字指的是操作数在操作数列表中的位置。

在这个上下文中,"0"特指第一个操作数约束,也被称为“约束0”(constraint 0)。在x86调用约定中,EAX寄存器通常用于从函数中返回值。通过将"0"指定为输出操作数的约束("=a" (res)),编译器得知在内联汇编块之后,EAX寄存器中的值应该赋给变量res

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
int sys_open(const char * filename,int flag,int mode) //filename "/dev/tty0"
{
struct m_inode * inode;
struct file * f;
int i,fd;

mode &= 0777 & ~current->umask;
//打开文件的进程是当前进程,在filep里面找一个空闲项
for(fd=0 ; fd<NR_OPEN ; fd++) // #define NR_OPEN 20
if (!current->filp[fd])
break;
if (fd>=NR_OPEN)
return -EINVAL;
current->close_on_exec &= ~(1<<fd);
f=0+file_table;
//找完找file table,引用计数为0的空闲项
for (i=0 ; i<NR_FILE ; i++,f++)//#define NR_FILE 64
if (!f->f_count) break;
if (i>=NR_FILE)
return -EINVAL;
(current->filp[fd]=f)->f_count++;//当前进程的空闲的filep的哪一项指向空闲的file table里面的项,引用计数++
//读i结点,filename:sysopen的参数const char*类型,返回给inode
//如果open_namei成功返回0,失败返回1
if ((i=open_namei(filename,flag,mode,&inode))<0) {
current->filp[fd]=NULL;
f->f_count=0;
return i;
}
/* ttys are somewhat special (ttyxx major==4, tty major==5) */
if (S_ISCHR(inode->i_mode))//tty0是设备文件
//设备文件的设备号放在i_zone[0]里面
if (MAJOR(inode->i_zone[0])==4) {
if (current->leader && current->tty<0) {
current->tty = MINOR(inode->i_zone[0]);
tty_table[current->tty].pgrp = current->pgrp;
}
} else if (MAJOR(inode->i_zone[0])==5)
if (current->tty<0) {
iput(inode);
current->filp[fd]=NULL;
f->f_count=0;
return -EPERM;
}
/* Likewise with block-devices: check for floppy_change */
if (S_ISBLK(inode->i_mode))
check_disk_change(inode->i_zone[0]);
f->f_mode = inode->i_mode;
f->f_flags = flag;
f->f_count = 1;
f->f_inode = inode;
f->f_pos = 0;
return (fd);
}

进程2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
void init(void)
{
...
printf("%d buffers = %d bytes buffer space\n\r",NR_BUFFERS,
NR_BUFFERS*BLOCK_SIZE);
printf("Free mem: %d bytes\n\r",memory_end-main_memory_start);
if (!(pid=fork())) {
// 进程2:
close(0);//关闭设备标准输入
if (open("/etc/rc",O_RDONLY,0))
_exit(1);
execve("/bin/sh",argv_rc,envp_rc); //走syscall3,需要打开的执行文件和环境变量
_exit(2);
}
if (pid>0)
//进程1等待子进程退出
while (pid != wait(&i))
/* nothing */;
while (1) {
if ((pid=fork())<0) {
printf("Fork failed in init\r\n");
continue;
}
if (!pid) {
close(0);close(1);close(2);
setsid();
(void) open("/dev/tty0",O_RDWR,0);
(void) dup(0);
(void) dup(0);
_exit(execve("/bin/sh",argv,envp));
}
...
}

sys_waitpid

阅读全文 »

进程1的创建和运行与缓冲区相关操作

进程0创建进程1

在linux系统中所有进程都是基于父子进程创建机制,由父进程创建的。通过父进程调用fork函数实现

1
2
3
4
5
6
7
8
static inline _syscall0(int,fork) //定义了fork函数

void main(void){
...
if(!fork())
init();
...
}

syscall0

执行fork函数实际是执行到unistd.h的syscall0():

image-20231021140609566

**_syscall0** 是一个宏定义,其实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
#define _syscall0(type,name) \
type name(void) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
: "=a" (__res) \
: "0" (__NR_##name)); \
if (__res >= 0) \
return (type) __res; \
errno = -__res; \
return -1; \
}

因此这里_syscall0(int,fork) 展开后是这样的:.

阅读全文 »

ldt中断函数挂接以及进程及相关设备初始化

操作系统中心思想:管理所有的硬件资源为软件资源提供服务

main.c

1
2
3
4
//之前把0x9000-0x901F部分用来存储机器系统信息
#define EXT_MEM_K (*(unsigned short *)0x90002)
#define DRIVE_INFO (*(struct drive_info *)0x90080)//90080是硬盘参数表
#define ORIG_ROOT_DEV (*(unsigned short *)0x901FC)//901fc是跟设备号

根文件系统设备

Linux0.11要求系统必须存在一个跟文件系统,其他文件系统挂载在上面,这里指的是文件系统格式化设备例如软盘。

image-20231007004249231

规划物理内存

image-20231005095121645
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 	ROOT_DEV = ORIG_ROOT_DEV;
drive_info = DRIVE_INFO;
//内存大小=1M+扩展内存
memory_end = (1<<20) + (EXT_MEM_K<<10);//左移20位:1M EXT_MEM_K机器设备信息,0x90002扩展内存kb数 左移10位kb->mb
//忽略不到4KB(一页)的内存页
memory_end &= 0xfffff000;
if (memory_end > 16*1024*1024)
memory_end = 16*1024*1024;//如果内存大于16M按照16M计算
if (memory_end > 12*1024*1024)
buffer_memory_end = 4*1024*1024;
else if (memory_end > 6*1024*1024)
buffer_memory_end = 2*1024*1024;
else
buffer_memory_end = 1*1024*1024;//设置缓冲区
main_memory_start = buffer_memory_end;
//如果在Makefile里面指定了RAMDISK则建立虚拟盘
#ifdef RAMDISK
main_memory_start += rd_init(main_memory_start, RAMDISK*1024);
//虚拟盘设置
阅读全文 »

系统调用实验

实验内容

在 Linux 0.11 上添加两个系统调用,并编写两个简单的应用程序测试它们。

iam()

第一个系统调用是 iam(),其原型为:

1
int iam(const char * name);

完成的功能是将字符串参数 name 的内容拷贝到内核中保存下来。要求 name 的长度不能超过 23 个字符。返回值是拷贝的字符数。如果 name 的字符个数超过了 23,则返回 “-1”,并置 errno 为 EINVAL。

kernal/mywho.c 中实现此系统调用。

whoami()

第二个系统调用是 whoami(),其原型为:

阅读全文 »

16位实模式 -> 32位保护模式

EP/EIP 相当于pc指针,从实模式16位->保护模式32位

BIOS

加电后进入实模式运行,16位。

上电后CS(代码段寄存器)设置为0XF000(纯硬件完成),因此第一条程序跳到0XF000执行。0XF000是BIOS的程序入口地址,因此此时主动权交到了BIOS手上

image-20230926154112305

如图可以看出BIOS的中断向量表有0x400大小->1024->1KB

BIOS 的中断向量表256个中断向量:cs+ip

bios 执行int0x19中断,将磁盘的第一个扇区(bootsect.s的程序)复制到0x07C00处

image-20230927080114009
阅读全文 »

openmp

OpenMP: Syntax in C/C++

包含头文件:#include <omp.h>

编译制导

编译制导指令以#pragma omp 开始,后边跟具体的功能指令,格式如:#pragma omp 指令[子句[,子句] …]

测试小案例1:openmp的简单使用

1
2
3
4
5
6
7
8
9
#include <stdio.h>
#include <omp.h>
int main(int argc, char* argv[]){
#pragma omp parallel
{
printf( "Hello wrold from %d!\n", omp_get_thread_num());
}
return 0;
}

编译:

1
g++ hello.cpp -fopenmp -o hello

OPENMP中设置线程数量的方法:

阅读全文 »

并行并发

并行和并发的区别

  1. 并行 (Parallelism):
    • 并行指的是同时执行多个操作,它们在同一时刻发生,可以是在多个处理单元(如多核处理器)上同时执行,也可以是在多台计算机上同时执行。
    • 在并行中,多个任务被同时处理,它们的执行时间是重叠的。这意味着这些任务同时在不同的处理单元上进行,可以显著地提高整体性能。
    • 典型的例子包括多线程并行执行、多进程并行执行以及分布式系统中的并行计算。
  2. 并发 (Concurrency):
    • 并发指的是在同一时间段内处理多个任务,但并不一定是同时执行。任务可能交替执行,每个任务在一段时间内执行一部分,然后切换到另一个任务继续执行。
    • 在并发中,任务可能通过时间片轮转或事件驱动等方式交替执行,以便利用系统资源,同时让多个任务看起来好像是同时在运行。
    • 典型的例子包括操作系统中的多任务处理、网络服务器同时处理多个客户端请求以及图形用户界面(GUI)程序中的事件处理。

Type of Parallelism

Job Level Parallelism

  1. Inter-Job Parallelism(任务间并行性):
    • Inter-Job Parallelism 涉及多个独立任务或作业之间的并行执行。
    • 在这种并行性中,每个任务都是独立的,彼此之间没有直接的依赖关系,因此可以同时执行。
    • 典型的例子包括批处理系统中的并行作业,或者在云计算环境中同时运行的多个虚拟机实例。
  2. Intra-Job Parallelism(任务内部并行性):
    • Intra-Job Parallelism 指的是在单个任务或作业内部的并行执行。
    • 在这种并行性中,一个单独的任务被分解为多个子任务,这些子任务可以并行执行以提高整体性能。
    • 典型的例子包括在一个大型计算任务中使用多线程或多进程并行处理数据,或者使用向量化指令集来加速数值计算。

task level

程序级别的并行

阅读全文 »

绑定自己的仓库

1
2
3
git remote -v
origin git://g.csail.mit.edu/xv6-labs-2021 (fetch)
origin git://g.csail.mit.edu/xv6-labs-2021 (push)

使用git remote remove命令移除远程仓库的关联。请将`替换为要移除的远程仓库的名称,一般为origin`。

然后绑定自己的仓库:git remote add origin git@github.com:sherecho/MyOS_Prj.git

gdb 调试

设置.gdbinit 文件

1
2
3
4
5
6
7
8
9
10
11
add-auto-load-safe-path /xv6-labs-2021/.gdbinit
set confirm off
set architecture riscv:rv64
target remote 127.0.0.1:25000
symbol-file kernel/kernel
set disassemble-next-line auto
set riscv use-compressed-breakpoints yes
file user/_primes
layout src
b main
c

运行make qemu-gdb

在另一个shell里面运行

gdb-multiarch -x .gdbinit

阅读全文 »
0%