标签:断言 bin 连续 condition 索引数组 next end clu source
目录结构:
└── bochs
├── 02.tar.gz
├── 03.tar.gz
├── 04.tar.gz
├── 05a.tar.gz
├── 05b.tar.gz
├── 06a.tar.gz
├── 07a.tar.gz
├── 07b.tar.gz
├── 07c.tar.gz
├── 08a
│?? ├── boot
│?? │?? ├── include
│?? │?? │?? └── boot.inc
│?? │?? ├── loader.asm
│?? │?? └── mbr.asm
│?? ├── build
│?? ├── device
│?? │?? ├── timer.c
│?? │?? └── timer.h
│?? ├── kernel
│?? │?? ├── debug.c
│?? │?? ├── debug.h
│?? │?? ├── global.h
│?? │?? ├── idt.asm
│?? │?? ├── init.c
│?? │?? ├── init.h
│?? │?? ├── interrupt.c
│?? │?? ├── interrupt.h
│?? │?? └── main.c
│?? ├── lib
│?? │?? ├── kernel
│?? │?? │?? ├── bitmap.c
│?? │?? │?? ├── bitmap.h
│?? │?? │?? ├── io.h
│?? │?? │?? ├── print.asm
│?? │?? │?? ├── print.h
│?? │?? │?? ├── string.c
│?? │?? │?? └── string.h
│?? │?? └── libint.h
│?? ├── makefile
│?? └── start.sh
└── hd60m.img
随着模块越来越多,程序出错的概率越来越大,为了方便调试,一个好的细观是在程序的关键部分设置哨兵,监督数据的正确定
断言是就是断定的意思,程序员断定在程序执行到此处的时候,某项数据的值应该是多少。
assert断言是一个宏,而不是一个函数,因此当程序正式发布的时候,可以方便的将assert的语句通过宏的形式去掉。
就是:
#ifdef NODEBUG
#define assert(cond) ((void)0)
#else
#define assert(cond) \
if(cond) \
{ \
\
}else{\
\
}\
#endif
在编译中如果定义了NODEBUG
宏那么编译的时候assert
会被替换为((void)0)
然后,在assert
在开Debug的时候,如果出错,应该能够打印出错代码所在的文件名,行号,和函数。这些使用gcc的预处理的语句即可。另外还希望打印出错的值。在不适用预处理的时候,无法实现这个功能,但是使用预处理,可以将一个变量变为字符串。
然后考虑,出错时候打印的信息:
void panic(char* filename,int line,char* func,char* cond)
{
intr_disable();
put_str("\n\n\n error:");
put_str("filename: ");put_str(filename);put_str("\n");
put_str("line:");put_int(line);put_str("\n");
put_str("function: ");put_str(func);put_str("\n");
put_str("condition: ");put_str(cond);put_str("\n");
while(1){} // 用于停住程序
}
#define PANIC(cond) panic(__FILE__,__LINE__,__func__,cond)
#ifdef NODEBUG
#define assert(cond) ((void)0)
#else
#define assert(cond) \
if(cond) \
{ \
\
}else{\
PANIC(#cond)
}#endif
然后,intr_disable()
关中断是因为,当断言为假,应该停住程序的时候,程序应该完全不执行,只执行循环,但是因为中断的存在,所以即使停在最后的while(1)
循环,还是会产生中断,所以关了中断,这样程序完全的停住了。
#ifndef __KERNEL_DEBUG_H
#define __KERNEL_DEBUG_H
void panic( char *filename, int line, const char *func, const char *cond );
#define PANIC( cond ) panic( __FILE__, __LINE__, __func__, cond )
#ifdef NDEBUG
#define ASSERT( cond ) ( (void) 0 )
#define BREAK() ( (void) 0 );
#else
#define ASSERT( cond ) \
if ( cond ) { \
} else { \
PANIC( #cond ); \
}
// 魔数断点
#define BREAK() __asm__( "xchg %%bx,%%bx" :: );
#endif /*__NDEBUG */
#endif /*__KERNEL_DEBUG_H*/
就是封装了操作内存的函数,memset
等
#ifndef __LIB_STRING_H
#define __LIB_STRING_H
#include "libint.h"
void memset( void* dst_, uint8_t value, uint32_t size );
void memcpy( void* dst_, const void* src_, uint32_t size );
int memcmp( const void* a_, const void* b_, uint32_t size );
char* strcpy( char* dst_, const char* src_ );
uint32_t strlen( const char* str );
int8_t strcmp( const char* a, const char* b );
char* strchr( const char* string, const uint8_t ch );
char* strrchr( const char* string, const uint8_t ch );
char* strcat( char* dst_, const char* src_ );
uint32_t strchrs( const char* filename, uint8_t ch );
#endif
#include "string.h"
#include "debug.h"
#include "global.h"
#include "print.h"
/* 将dst_起始的size个字节置为value */
void memset( void* dst_, uint8_t value, uint32_t size )
{
ASSERT( dst_ != NULL );
uint8_t* dst = ( uint8_t* )dst_;
while ( size-- > 0 )
*dst++ = value;
}
/* 将src_起始的size个字节复制到dst_ */
void memcpy( void* dst_, const void* src_, uint32_t size )
{
ASSERT( dst_ != NULL && src_ != NULL );
uint8_t* dst = dst_;
const uint8_t* src = src_;
while ( size-- > 0 )
*dst++ = *src++;
}
/* 连续比较以地址a_和地址b_开头的size个字节,若相等则返回0,若a_大于b_返回+1,否则返回-1
*/
int memcmp( const void* a_, const void* b_, uint32_t size )
{
const char* a = a_;
const char* b = b_;
ASSERT( a != NULL || b != NULL );
while ( size-- > 0 )
{
if ( *a != *b )
{
return *a > *b ? 1 : -1;
}
a++;
b++;
}
return 0;
}
/* 将字符串从src_复制到dst_ */
char* strcpy( char* dst_, const char* src_ )
{
ASSERT( dst_ != NULL && src_ != NULL );
char* r = dst_; // 用来返回目的字符串起始地址
while ( ( *dst_++ = *src_++ ) )
;
return r;
}
/* 返回字符串长度 */
uint32_t strlen( const char* str )
{
ASSERT( str != NULL );
const char* p = str;
while ( *p++ )
;
return ( p - str - 1 );
}
/* 比较两个字符串,若a_中的字符大于b_中的字符返回1,相等时返回0,否则返回-1. */
int8_t strcmp( const char* a, const char* b )
{
ASSERT( a != NULL && b != NULL );
while ( *a != 0 && *a == *b )
{
a++;
b++;
}
/* 如果*a小于*b就返回-1,否则就属于*a大于等于*b的情况。在后面的布尔表达式"*a
* > *b"中,
* 若*a大于*b,表达式就等于1,否则就表达式不成立,也就是布尔值为0,恰恰表示*a等于*b
*/
return *a < *b ? -1 : *a > *b;
}
/* 从左到右查找字符串str中首次出现字符ch的地址(不是下标,是地址) */
char* strchr( const char* str, const uint8_t ch )
{
ASSERT( str != NULL );
while ( *str != 0 )
{
if ( *str == ch )
{
return (
char* )str; // 需要强制转化成和返回值类型一样,否则编译器会报const属性丢失,下同.
}
str++;
}
return NULL;
}
/* 从后往前查找字符串str中首次出现字符ch的地址(不是下标,是地址) */
char* strrchr( const char* str, const uint8_t ch )
{
ASSERT( str != NULL );
const char* last_char = NULL;
/* 从头到尾遍历一次,若存在ch字符,last_char总是该字符最后一次出现在串中的地址(不是下标,是地址)*/
while ( *str != 0 )
{
if ( *str == ch )
{
last_char = str;
}
str++;
}
return ( char* )last_char;
}
/* 将字符串src_拼接到dst_后,将回拼接的串地址 */
char* strcat( char* dst_, const char* src_ )
{
ASSERT( dst_ != NULL && src_ != NULL );
char* str = dst_;
while ( *str++ )
;
--str; // 别看错了,--str是独立的一句,并不是while的循环体
while ( ( *str++ = *src_++ ) )
; // 当*str被赋值为0时,此时表达式不成立,正好添加了字符串结尾的0.
return dst_;
}
/* 在字符串str中查找指定字符ch出现的次数 */
uint32_t strchrs( const char* str, uint8_t ch )
{
ASSERT( str != NULL );
uint32_t ch_cnt = 0;
const char* p = str;
while ( *p != 0 )
{
if ( *p == ch )
{
ch_cnt++;
}
p++;
}
return ch_cnt;
}
位图是一种资源管理方式。
位是指bit,一个字节有8个bit。位图则是每一个实物对应一个位的意思。这是一种数据结构
我们将来使用位图来管理内存,每一个4K的页对应位图上的一个位,其1,0则表示该页是否被使用了。
首先,需要一个定义一个结构用来记录一个位图。
struct Bitmap
{
uint32_t len;
uint8_t bits;// 用指针的原因是,并不知道需要多少的空间
};
初始化位图结构的函数bitmap_init()
的函数,主要是将len
长度的字节,全部清零。
一个判断某个位是否被用了的函数bitmap_test(struct Bitmap*,uint32_t idx)
:工作原理是先计算idx
所在的字节,然后计算idx
在所在字节的位,其实就是除法和取模,按照数组访问元素的方式定位到元素,然后和1做左移后的结果进行&操作
一个连续分配cnt
个位的函数bitmap_get(struct Bitmap*,unint32_t cnt)
:这个函数比较复杂:首先从头开始,对每个字节进行和0xFF
的&操作,找到有空余位的字节。这过程中要判断越界。然后如果是取1位,则直接返回该位,如果取多位,那么需要从该位开始依次向下,去寻找能够分配这么多位的第一个位,并返回。
还有一个用于设置位使用和回收的函数bitmap_set(struct Bitmap,uint32_t idx,int_t v)
:和bitmap_test()
差不多
#ifndef __LIB_KERNEL_BITMAP_H
#define __LIB_KERNEL_BITMAP_H
#include "global.h"
#define BITMAP_MASK 1
struct Bitmap
{
uint32_t len;
uint8_t* bits;
};
void bitmap_init( struct Bitmap* btmp );
int bitmap_test( struct Bitmap* btmp, uint32_t bit_idx );
int bitmap_get( struct Bitmap* btmp, uint32_t cnt );
void bitmap_set( struct Bitmap* btmp, uint32_t bit_idx, int8_t value );
#endif
#include "bitmap.h"
#include "debug.h"
#include "interrupt.h"
#include "libint.h"
#include "print.h"
#include "string.h"
/* 将位图btmp初始化 */
void bitmap_init( struct Bitmap* btmp )
{
memset( btmp->bits, 0, btmp->len );
}
/* 判断bit_idx位是否为1,若为1则返回true,否则返回false */
bool bitmap_test( struct Bitmap* btmp, uint32_t bit_idx )
{
uint32_t byte_idx = bit_idx / 8; // 向下取整用于索引数组下标
uint32_t bit_odd = bit_idx % 8; // 取余用于索引数组内的位
return ( btmp->bits[ byte_idx ] & ( BITMAP_MASK << bit_odd ) );
}
/* 在位图中申请连续cnt个位,成功则返回其起始位下标,失败返回-1 */
int bitmap_get( struct Bitmap* btmp, uint32_t cnt )
{
uint32_t idx_byte = 0; // 用于记录空闲位所在的字节
/* 先逐字节比较,蛮力法 */
while ( ( 0xff == btmp->bits[ idx_byte ] ) && ( idx_byte < btmp->len ) )
{
/* 1表示该位已分配,所以若为0xff,则表示该字节内已无空闲位,向下一字节继续找
*/
idx_byte++;
}
ASSERT( idx_byte < btmp->len );
if ( idx_byte == btmp->len )
{ // 若该内存池找不到可用空间
return -1;
}
/* 若在位图数组范围内的某字节内找到了空闲位,
* 在该字节内逐位比对,返回空闲位的索引。*/
int idx_bit = 0;
/* 和btmp->bits[idx_byte]这个字节逐位对比 */
while ( ( uint8_t )( BITMAP_MASK << idx_bit ) & btmp->bits[ idx_byte ] )
{
idx_bit++;
}
int bit_idx_start = idx_byte * 8 + idx_bit; // 空闲位在位图内的下标
if ( cnt == 1 )
{
return bit_idx_start;
}
uint32_t bit_left = ( btmp->len * 8 - bit_idx_start ); // 记录还有多少位可以判断
uint32_t next_bit = bit_idx_start + 1;
uint32_t count = 1; // 用于记录找到的空闲位的个数
bit_idx_start = -1; // 先将其置为-1,若找不到连续的位就直接返回
while ( bit_left-- > 0 )
{
if ( !( bitmap_test( btmp, next_bit ) ) )
{ // 若next_bit为0
count++;
}
else
{
count = 0;
}
if ( count == cnt )
{ // 若找到连续的cnt个空位
bit_idx_start = next_bit - cnt + 1;
break;
}
next_bit++;
}
return bit_idx_start;
}
/* 将位图btmp的bit_idx位设置为value */
void bitmap_set( struct Bitmap* btmp, uint32_t bit_idx, int8_t value )
{
ASSERT( ( value == 0 ) || ( value == 1 ) );
uint32_t byte_idx = bit_idx / 8; // 向下取整用于索引数组下标
uint32_t bit_odd = bit_idx % 8; // 取余用于索引数组内的位
/* 一般都会用个0x1这样的数对字节中的位操作,
* 将1任意移动后再取反,或者先取反再移位,可用来对位置0操作。*/
if ( value )
{ // 如果value为1
btmp->bits[ byte_idx ] |= ( BITMAP_MASK << bit_odd );
}
else
{ // 若为0
btmp->bits[ byte_idx ] &= ~( BITMAP_MASK << bit_odd );
}
}
标签:断言 bin 连续 condition 索引数组 next end clu source
原文地址:https://www.cnblogs.com/perfy576/p/9139135.html