手搓OS-day9
重量级难度,分配内存与回收(内核开始了),终于快一半了(折磨)
要求:
边界对齐到\(2^i\)
不够分配时返回NULL(0)
拒绝超过16MiB的分配
不必初始化内存,可以全赋值为零
允许多处理器并行使用
C语言的面向对象
虽然很早之前老师就已经提过这个C语言的骚操作,嗯,还是有点难以接受
首先要介绍一个函数指针的概念,这个类似的概念实际上在Python里是接触过的,只是没有明确的提出来,当时令人感到很神奇(同时也感到很鸡肋)
函数指针
声明
1 return_type (*ptr)(parameter_type1,parameter_type2,……);
这样我们就声明了一个指向某个函数入口的一个指针,当然指针指向的函数需要初始化,一个小栗子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <stdio.h> int add (int a,int b) ;int main () { int (*ptr)(int ,int ); ptr=add; int result=ptr(10 ,20 ); printf ("result=%d\n" ,result); return 0 ; } int add (int a,int b) { return a+b; }
下面这个就是我们在Python中的常用方法了(回调函数):
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 #include <stdio.h> typedef int (*Operation) (int , int ) ; int performOperation (int a, int b, Operation op) { return op(a, b); } int add (int a, int b) { return a + b; } int subtract (int a, int b) { return a - b; } int main () { int result; result = performOperation(10 , 5 , add); printf ("Add Result: %d\n" , result); result = performOperation(10 , 5 , subtract); printf ("Subtract Result: %d\n" , result); return 0 ; }
C语言面向对象
虽然C++已经很好的做好了面向对象编程的,但是秉持着闲的蛋疼努力学习的精神,我们还是有必要了解如何用C语言实现面向对象编程的。面向对象三大特征:
封装对于C语言并不难实现,我们可以用结构体平替,那么怎么实现继承呢?鉴于接下来的文件结构比较抽象,请稍微记忆一下(写完发现并不复杂,没事了)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #ifndef SHAPE_H #define SHAPE_H #include <stdint.h> typedef struct { int16_t x; int16_t y; }Shape; void Shape_ctor (Shape * const me, int16_t x, int16_t y) ;void Shape_moveBy (Shape * const me, int16_t dx, int16_t dy) ;int16_t Shape_getX (Shape const * const me) ;int16_t Shape_getY (Shape const * const me) ; #endif
这里我们假设所有的接口函数都已经实现了(稍微好理解一点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #ifndef RECT_H #define RECT_H #include "shape.h" typedef struct { Shape super; uint16_t width; uint16_t height; }Rectangle; void Rectangle_ctor (Retangle *const me,int16_t x,int16_t y,uint16_t width,uint16_t height) ;#endif
然后就是关于多态的实现这就很麻烦了
在C++中,如果一个父类中定义了虚函数,那么编译器就会在这个内存中开辟一块空间放置虚表,这张表里的每一个item都是一个函数指针,然后在父类的内存模型中放一个虚表指针,指向上面这个虚表。
好消息:知道大概怎么回事了
坏消息:虚函数是什么玩意来着
哦,原来虚函数就是可重写的函数(还是Java事儿少),那没事了。来实现吧,首先重写一下shape类
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 #ifndef SHAPE_H #define SHAPE_H #include <stdint.h> struct ShapeVtbl ;typedef struct { struct ShapeVtbl const *vptr ; int16_t x; int16_t y; }Shape; struct ShapeVtbl { uint32_t (*area)(Shape const * const me); }; void Shape_ctor (Shape * const me, int16_t x, int16_t y) ;void Shape_moveBy (Shape * const me, int16_t dx, int16_t dy) ;int16_t Shape_getX (Shape const * const me) ;int16_t Shape_getY (Shape const * const me) ;static inline uint32_t Shape_area (Shape const * const me) { return (*me->vptr->area)(me); } #endif
ok,然后写一下这个rect.h
的实现
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 #include "rect.h" #include <stdio.h> static uint32_t Rectangle_area_ (Shape const * const me) ;void Rectangle_ctor (Rectangle * const me, int16_t x, int16_t y, uint16_t width, uint16_t height) { static struct ShapeVtbl const vtbl = { &Rectangle_area_ }; Shape_ctor(&me->super, x, y); me->super.vptr = &vtbl; me->width = width; me->height = height; } static uint32_t Rectangle_area_ (Shape const * const me) { Rectangle const * const me_ = (Rectangle const *)me; return (uint32_t )me_->width *(uint32_t )me_->height; }
记录
编译一个镜像要了半条命,缓缓
编译运行坑
qemu模拟器现在包已经变了,应该下载的是:
1 sudo apt-get install qemu-system-x86
要在各种乱七八糟的Makefile里边手动添加一个变量AM_HEMO_
,不过这个可能直接加在最外边的Makefile应该也是可以实现的
一些自己的写的头文件如果找不到就要用从根路径过来的全路径来引用,而且要是双引号
太感人了😭😭😭😭😭
printf
实现
不说很难,至少很恶心代码量很大,尝试分析:
还是能分析出来的,回忆一下这个函数的用法:
1 2 printf ("format" ,data, ...);printf ("string" );
嗯,很显然我们需要一个和Python中argv
类似性质的一个玩意来帮忙,然后要做的就是识别是否有后边的参数。其次我们还需要能够切分前面的字符串以识别其中的%d,%f
等参数,并获取参数对应的argv
中的参数。
果然,C语言中是有这种玩意的:<stdarg.h>
,抄一个用法示例():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <stdarg.h> int VarArgFunc (int dwFixedArg, ...) { va_list pArgs = NULL ; va_start(pArgs, dwFixedArg); int dwVarArg = va_arg(pArgs, int ); va_end(pArgs); }
开编,注意到我们现在实际上只有一个putch()
函数来输出单个字符
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 int printf (const char *fmt, ...) { if (fmt == NULL ) return -1 ; int ret_num = 0 ; va_list va_l; va_start(va_l, fmt); char *pstr = (char *)fmt; while (*pstr != '\0' ) { if (*pstr == '%' ) { pstr++; switch (*pstr) { case 'd' : int var1 = va_arg(va_l, int ); ret_num++; putch(var1); break ; case 's' : char * var2 = va_arg(va_l, char *); ret_num++; putch(*var2); break ; case '%' : putch('%' ); ret_num++; break ; default : putch(' ' ); ret_num++; break ; } } putch(*pstr); ret_num++; } return ret_num; }
写了一个简陋的,但是显然不是很成功(思想对了,嗯,想看正确的可以看参考链接)
另外由于在内核编译的Makefile中有这样一句话:
1 CFLAGS += -m64 -fPIC -mno -sse
因此我在编译内核时任何的浮点操作都是不被允许的。
kalloc、kfree
实现
实现内容今天貌似是写不完了,简单记录一下思路,就是和前边实现字符串的一些操作是一致的,但是会涉及一些内存的管理问题,用结构体就能解决,区别主要是实现策略问题,然后就是要对齐,如何对齐呢,写个循环算好阶乘就行了。然后kfree
的实现就比较简明了把维护的指针收回就行,剩下的明天续上。
参考链接
什么是函数指针?如何使用函数指针?-CSDN博客
C
语言实现面向对象编程_void rectangle_construct(rectangle_t* shape,
const-CSDN博客
手把手教你实现printf函数(C语言方式)_printf实现-CSDN博客
内存分配不再神秘:深入剖析malloc函数实现原理与机制
- 知乎 (zhihu.com)