[读书笔记]程序员的自我修养 chp13 一个简单的CRT 运行库实现

0. 前言

  1. 目标:实现一个小型的 CRT 运行库
  2. 本质: 利用系统提供的api接口, 实现一个通用的 CRT 函数接口, 使得 C 语言程序可以自由的运行在各个不同的系统上
  3. 项目工程地址: https://github.com/zhyh2010/miniCRT

1. 遇到的一些问题

  1. windows.h 不包含路径集
    这里写图片描述
    解决方法:
    http://www.unjeep.com/q/869954156.htm

    先执行C:/Program Files/Microsoft Visual Studio 9.0/Common7/Tools/vsvars32.bat
    再cl task.cpp

  2. 命令行使用
    参考文章: http://www.360doc.com/content/10/0228/10/111369_17077729.shtml

@REM @Author: anchen
@REM @Date:   2016-12-21 16:38:42
@REM @Last Modified by:   anchen
@REM Modified time: 2016-12-22 15:57:18

@echo off
if "%1%"=="genlib" goto genlib
if "%1%"=="test" goto test
if "%1%"=="clean" goto clean

:help
    echo "Usage: make [genlib | test]"
    goto end

:genlib
    call vsvars32
    cl /c /DWIN32 /GS- entry.c malloc.c printf.c stdio.c string.c
    lib entry.obj malloc.obj printf.obj stdio.obj string.obj /OUT:minicrt.lib
    goto end

:test
    call vsvars32
    cl /c /DWIN32 test.c
    link test.obj minicrt.lib kernel32.lib /NODEFAULTLIB /entry:mini_crt_entry
    test arg1 arg2 123
    goto end

:clean
    echo "cleaning project"
    for %%i in (*.obj, *.lib, *.txt) do del /f /q %%i
    goto end

:end

2. 实现

我们的这个CRT 本质上只是实现一些非常简单的功能, 命令行参数解析与设置, 字符串打印, 堆内存分配,文件读写 etc

2.1 入口函数

  1. 程序运行的起点并不是main, 而是由运行库提供的入口函数, 主要负责:
    1. 准备程序运行环境以及初始化运行库
    2. 调用 main 函数执行程序主体
    3. 清理程序运行后的各种资源
  2. code
#include "minicrt.h"

#ifdef WIN32
#include <Windows.h>
#endif

extern int main(int argc, char * argv[]);
void exit(int);

static void crt_fatal_error(const char * msg){
    // printf("fatal error: %s", msg);
    exit(1);
}

void mini_crt_entry(void){
    int ret;

#ifdef WIN32
    int flag = 0;
    int argc = 0;
    char * argv[16];
    char * cl = GetCommandLineA();

    printf("origin commandline: %s\n", cl);

    // parse the parameters
    argv[0] = cl;
    argc++;
    while (*cl){
        if (*cl == '\"'){
            if (flag == 0)
                flag = 1;
            else
                flag = 0;
        }
        else if (*cl == ' ' && flag == 0){
            if (*(cl + 1)){
                argv[argc] = cl + 1;
                argc++;
                //printf("%d %s\n", argc - 1, argv[argc - 1]);
            }
            *cl = '\0';
        }
        cl++;
    }

#else
    int argc;
    char ** argv;
    char * ebp_reg = 0;
    // ebp_reg = %ebp
    asm("movl %%ebp,%0 n":"=r"(ebp_reg));

    argc = *(int *)(ebp_reg + 4);
    argv = (char **)(ebp_reg + 8);

#endif
    if (!mini_crt_heap_init())
        crt_fatal_error("heap initialize failed");
    if (!mini_crt_io_init())
        crt_fatal_error("IO initialize failed");

    ret = main(argc, argv);
    exit(ret);
}

void exit(int exitCode){
    // min_crt_call_exit_routine();
#ifdef WIN32
    ExitProcess(exitCode);
#else
    asm("movl %0, %%ebx \n\t"
        "movl $1, %%eax \n\t"
        "int $0x80      \n\t"
        "hlt            \n\t"::"m"(exitCode));
#endif
}

2.1 堆的实现

  1. 堆的实现:
    1. 使用空闲链表算法作为基础
    2. 堆大小固定为 32MB
    3. 需要特别注意,* 堆释放的时候, 如果有相连的空闲内存, 应该进行内存的合并处理操作 *
  2. 相关代码:
#include "minicrt.h"

typedef struct _heap_header{
    enum {
        HEAP_BLOCK_FREE = 0xABABABAB,
        HEAP_BLOCK_USED = 0xCDCDCDCD,
    } type;

    unsigned size;
    struct _heap_header * next;
    struct _heap_header * prev;
} heap_header;

#define ADDR_ADD(a, o) (((char *)(a)) + o)
#define HEADER_SIZE (sizeof(heap_header))

static heap_header * list_head = NULL;

void free(void * ptr){
    heap_header * header = (heap_header *)ADDR_ADD(ptr, -HEADER_SIZE);
    if (header->type != HEAP_BLOCK_USED)
        return;

    header->type = HEAP_BLOCK_FREE;
    if (header->prev != NULL && header->prev->type == HEAP_BLOCK_FREE){
        // merge
        header->prev->next = header->next;
        if (header->next != NULL)
            header->next->prev = header->prev;
        header->prev->size += header->size;

        header = header->prev;
    }

    if (header->next != NULL && header->next->type == HEAP_BLOCK_FREE){
        // merge
        header->size += header->next->size;
        header->next = header->next->next;
    }
}

void * malloc(unsigned size){
    heap_header * header;
    if (size == 0)
        return NULL;

    header = list_head;
    while (header != 0){
        if (header->type == HEAP_BLOCK_USED){
            header = header->next;
            continue;
        }

        if (header->size > size + HEADER_SIZE && 
            header->size <= size + HEADER_SIZE * 2){
            header->type = HEAP_BLOCK_USED;
        }
        if (header->size > size + HEADER_SIZE * 2){
            // split
            heap_header * next = (heap_header *)ADDR_ADD(header, size + HEADER_SIZE);
            next->prev = header;
            next->next = header->next;
            next->type = HEAP_BLOCK_FREE;
            next->size = header->size - (size - HEADER_SIZE);
            header->next = next;
            header->size = size + HEADER_SIZE;
            header->type = HEAP_BLOCK_USED;
            return ADDR_ADD(header, HEADER_SIZE);
        }
        header = header->next;
    }

    return NULL;
}

#ifndef WIN32
// Linux brk system call
static int brk(void * end_data_segment){
    int ret = 0;
    // brk system call number : 45
    // in /usr/include/asm-i386/unistd.h:
    // #define __NR_brk 45
    asm("movl $45, %%eax    \n\t"
        "movl %1, %%ebx     \n\t"
        "int $0x80          \n\t"
        "movl %%eax, %0     \n\t"
        : "=r"(ret): "m"(end_data_segment));
}
#endif

#ifdef WIN32
#include <windows.h>
#endif

int mini_crt_heap_init(){
    void * base = NULL;
    heap_header * header = NULL;
    // 32 MB heap size
    unsigned heap_size = 1024 * 1024 * 32;

#ifdef WIN32
    base = VirtualAlloc(0, heap_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (base == NULL)
        return 0;
#else
    base = (void *)brk(0);
    void * end = ADDR_ADD(base, heap_size);
    end = (void *)brk(end);
    if (!end)
        return 0;
#endif

    header = (heap_header *) base;

    header->size = heap_size;
    header->type = HEAP_BLOCK_FREE;
    header->next = NULL;
    header->prev = NULL;

    list_head = header;
    return 1;
}

2.3 格式化字符串操作

  1. 实现一个printf 操作, 本质上就是变长参数的函数的处理
  2. code
#include "minicrt.h"

int fputc(int c, FILE * stream){
    if (fwrite(&c, 1, 1, stream) != 1)
        return EOF;
    else
        return c;
}

int fputs(const char * str, FILE * stream){
    int len = strlen(str);
    if (fwrite(str, 1, len, stream) != len)
        return EOF;
    else
        return len;
}

#ifndef WIN32
#define va_list char *
#define va_start(ap, arg) (ap = (va_list)&arg + sizeof(arg))
#define va_arg(ap, t) (*(t*)((ap += sizeof(t)) - sizeof(t)))
#define va_end(ap) (ap = (va_list)0)
#else
#include <windows.h>
#endif

int vfprintf(FILE * stream, const char * format, va_list arglist){
    int translating = 0;
    int ret = 0;
    const char * p = 0;
    for (p = format; *p != '\0'; ++p){
        switch (*p){
            case '%':
                if (!translating)
                    translating = 1;
                else{
                    if (fputc('%', stream) < 0)
                        return EOF;
                    ++ret;
                    translating = 0;
                }
                break;
            case 'd':
                if (translating){
                    char buf[16];
                    translating = 0;
                    itoa(va_arg(arglist, int), buf, 10);
                    if (fputs(buf, stream) < 0)
                        return EOF;
                    ret += strlen(buf);
                }
                else if (fputc('d', stream) < 0)
                    return EOF;
                else
                    ++ret;
                break;
            case 's':
                if (translating){
                    const char * str = va_arg(arglist, const char *);
                    translating = 0;
                    if (fputs(str, stream) < 0)
                        return EOF;
                    ret += strlen(str);
                }
                else if (fputc('s', stream) < 0)
                    return EOF;
                else
                    ++ret;
                break;
            default:
                if (translating)
                    translating = 0;
                if (fputc(*p, stream) < 0)
                    return EOF;
                else
                    ++ret;
                break;
        }
    }
    return ret;
}

int printf(const char * format, ...){
    va_list(arglist);
    va_start(arglist, format);
    return vfprintf(stdout, format, arglist);
}

int fprintf(FILE * stream, const char * format, ...){
    va_list(arglist);
    va_start(arglist, format);
    return vfprintf(stream, format, arglist);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值