elua解析器开源文档第二章:2.1、C库代码

目录

点击这里查看所有博文

2.1、C库代码

  由于某些原因我们的elua开源项目中不能采用标准的c库函数,而是内置了一套专用的c库代码,这套c库代码与标准c库的命名保持一致。不过其内部的实现却发生了很大的变化。

  有的同学在学习STM32的时候,可能会遇到一个问题那就是串口通信printf重定向。st官方并没有提供printf函数给我们用,这时候如果想要使用printf向串口打印日志的话那就需要对printf进行重定向。

  在标准的c库函数中printf是向控制台打印输出信息。而在嵌入式平台上,是没有控制台的。如果想要用printf函数的话那就只有两个办法。

  第一就是大家在使用stm32时用到的方法,对printf函数进行输出重定向。那么这个办法在裸机编程上倒是看不出来什么弊端。大家在使用操作系统编程比如freertos这些就会发现一个问题。如果任务的堆栈空间分配比较小,同时又在任务中使用到了printf打印数据那么就会出现堆栈溢出导致程序崩溃。这是因为printf函数使用时会在栈空间定义一个大缓冲区存储数据,这个缓冲区只是临时定义的,用完就会被析构掉。在使用printf时临时缓冲区会被算到任务堆栈大小中,只要给任务分配到空间小了,那么就会导致程序堆栈空间溢出程序崩溃。按照我以前踩坑的经验,即使你只是点个灯,只要用到了printf函数,如果此时任务的堆栈空间小于2048程序就会崩溃。这样就不太好了是吧,起步2048哪有这么多内存去搞。

在这里插入图片描述

  为了解决这个问题,那么我们就需要用到第二个方法,那就是我们即将要讲到的重写c标准库,把它里面的空间缩小,改变实现的方法。

  C库代码这一小节对应的是我们的elua开源项目中的newlib文件夹里面的内容,里面工作是将标准的C库函数重新实现了一下。主要就是一些我们经常使用到的这些,比如#include"string.h"#include"stdio.h"#include"stdlib.h"#include"malloc.h"#include"math.h"。还有一些用的比较少,但是elua解析器里面会用到的,这里就不讲了。

  newlib文件夹的结构如下图所示,涉及到的文件数量也是一大把。有兴趣的自己去看。

在这里插入图片描述

  有的函数需要和硬件平台打交道,不能直接用标准库。比如stdio库中的一些函数,就拿fopen_ext"函数来看一下,它里面的实现用到了一些和平台相关的代码。

char *fgets_ext(char *buf, int n, FILE *fp);
FILE *fopen_ext(const char *file, const char *mode);
int fclose_ext(FILE *fp);
int getc_ext(FILE *fp);
int ungetc_ext(int c, FILE *fp);
size_t fread_ext(void *buf, size_t size, size_t count, FILE *fp);
int fseek_ext(FILE *fp, long offset, int whence);
long ftell_ext(FILE *fp);
int feof_ext(FILE *fp);
FILE *fopen_ext(const char *file, const char *mode)
{
    FILE *fp = NULL;
    E_LUA_SCRIPT_TABLE_SECTION section = LUA_SCRIPT_TABLE_MAX_SECTION;
    T_UNCOMPRESS_FILE_TABLE_ITEM *pItem = NULL;
    int fileNameLen = strlen(file);
    
    if((!file) || (strlen(file) == 0))
    {
        OPENAT_print("[fopen_ext]: para error!\n");
        return fp;
    }

    if(FindUncompressFileItem(&section, &pItem, file))
    {        
        fp = L_CALLOC(1,sizeof(FILE));
        if(fp)
        {
            fp->_flags = section;
            fp->_cookie = pItem;
        }
        fp->_type = LUA_UNCOMPRESS_FILE;

    #ifdef AM_LUA_CRYPTO_SUPPORT
        if(strncmp(&file[fileNameLen - 5],".luac", 5) == 0)
        {
            fp->_type |= ENC_FILE;
        }
    #endif

        //printf("[fopen_ext]: %s %d!\n", file, fp->_type);

    }

    return fp;
}

  当然这其中个例不能说明一切,那我们就再来看一个函数_malloc_r

void* _malloc_r( size_t size )
{
#ifdef MUXUSE_DLMALLOC_MEMORY_AS_LUA_SCRIPT_LOAD
    if(bScriptLoaded)
    {
        return dlmalloc(size);
    }
    else
#endif
    {
        return CNAME( malloc )( size );
    }

  这个_malloc_r经过if判断调用的是CNAME( malloc )( size ),而CNAME( malloc )( size )最终又指向platform_malloc这是我们的elua抽象层的代码,最终它调用的是函数是需要和硬件打交道的,直接采用标准版的c库肯定是不适用的。

void* platform_malloc( size_t size )
{
    return OPENAT_malloc( size );
}

  这个其实也算是一个抽象层,只不过它抽象的是标准库函数。使elua解析器能够在嵌入式平台上面正常运行。

  有人可能会讲,那我用其他的开发平台怎么能直接用malloc函数、printf函数,这些标准函数直接用也没有什么问题啊,哪有这么多事情。这里我提醒一下,那是因为开发包底层已经把这部分代码处理好了,有可能是重写,也有可能是重定向。对于用户什么来讲都不用管,直接用。但是这个底层绝对不是直接使用的c库的标准代码的。

  各位同学不信的话可以打开我们的iot_sdk_4g_8910开发包看下components文件夹下是不是也有一个newlib文件夹。
在这里插入图片描述

  那我们可能又有一个疑问,既然elua开源项目是运行在iot_sdk_4g_8910上面的,为什么不直接用iot_sdk_4g_8910的newlib呢。这里那就说一下,既然我们做开源,那就说明这个elua软件包不仅仅是只能用在iot_sdk_4g_8910上面的,只要各位有能力那就可以把它运行在任何平台上。那就拿stm32来讲,他就没有给用户提供这些c标准库(现在我也不清楚有没有提供,至少几年前是没有的)。它都没有,那用户移植起来岂不是很复杂,甚至有可能移植不成功。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

遇雪长安

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值