Newlib:修订间差异
| (未显示同一用户的10个中间版本) | |||
| 第6行: | 第6行: | ||
== 说明 ==  | == 说明 ==  | ||
newlib是一个用于嵌入式系统的C运行库,通常与GCC搭配使用。  | newlib是一个用于嵌入式系统的C运行库,通常与GCC搭配使用。  | ||
常用于单片机。  | |||
== 系统调用 ==  | == 系统调用 ==  | ||
| 第259行: | 第261行: | ||
* 每一个进程(线程)都必须有一个可重入结构体变量,并且在创建进程(线程)时初始化,删除进程(线程)时清理。个人推荐将可重入结构体附加到PCB(进程控制块)上。  | * 每一个进程(线程)都必须有一个可重入结构体变量,并且在创建进程(线程)时初始化,删除进程(线程)时清理。个人推荐将可重入结构体附加到PCB(进程控制块)上。  | ||
* 在调用相关库函数时将全局变量 <code>_impure_ptr</code> 改成当前进程(线程)的独有的可重入结构体变量指针或者启用<code>__DYNAMIC_REENT__</code>手动实现<code>struct _reent * __getreent (void);</code>函数。个人推荐在线程上下文切换时进行修改变量操作或者手动实现<code>__getreent</code>函数(对于多核CPU而言,只能手动实现<code>__getreent</code>,直接返回当前CPU的运行线程的可重入结构体变量指针)。  | * 在调用相关库函数时将全局变量 <code>_impure_ptr</code> 改成当前进程(线程)的独有的可重入结构体变量指针或者启用<code>__DYNAMIC_REENT__</code>手动实现<code>struct _reent * __getreent (void);</code>函数。个人推荐在线程上下文切换时进行修改变量操作或者手动实现<code>__getreent</code>函数(对于多核CPU而言,只能手动实现<code>__getreent</code>,直接返回当前CPU的运行线程的可重入结构体变量指针)。  | ||
当启用<code>_REENT_THREAD_LOCAL</code>   | 当启用<code>_REENT_THREAD_LOCAL</code> 宏定义时,可重入的实现由线程局部(TLS)变量实现,无需可重入结构体,调用可重入函数时<samp><code>struct _reent</code> 的指针为NULL</samp>。  | ||
{| class="wikitable"  | {| class="wikitable"  | ||
!函数  | !函数  | ||
| 第418行: | 第420行: | ||
== 官方资料 ==  | == 官方资料 ==  | ||
网站: http://sourceware.org/newlib/  | 网站: http://sourceware.org/newlib/  | ||
=== 相关数据手册 ===  | |||
==== Newlib 4.4.0 ====  | |||
[[文件:Newlib Libc.4.4.0.pdf|居中|缩略图]]  | |||
[[文件:Newlib Libm.4.4.0.pdf|居中|缩略图]]  | |||
==== Newlib 4.3.0 ====  | |||
* [[文件:Newlib Libc.pdf|居中|缩略图|libc]]  | |||
* [[文件:Newlib Libm.pdf|居中|缩略图|libm]]  | |||
== 相关资料 ==  | |||
* picolibc:https://github.com/picolibc/picolibc  | |||
2025年1月13日 (一) 06:56的最新版本
源代码
主要
https://sourceware.org/git/newlib-cygwin.git
说明
newlib是一个用于嵌入式系统的C运行库,通常与GCC搭配使用。
常用于单片机。
系统调用
对于嵌入式系统而言,环境复杂多样,有的支持多线程有的不支持,因此部分关于OS的接口C运行库不能预先实现。
对于newlib而言,用户需要手动实现一些操作系统接口。
基本OS接口
基本OS接口用于单进程(线程)环境或者不使用OS相关特性(只要求编译通过而不使用系统调用)的场景。
实现OS接口时应当包含errno.h头文件(用于使用错误代码)。
| 名称 | 类型 | 说明 | 备注 | 
|---|---|---|---|
_exit
 | 
函数 | 退出程序并清理文件 | |
close
 | 
函数 | 关闭文件 | 默认示例:int close(int file) 
{
  return -1;
}
 | 
environ
 | 
指针 | 环境变量 | 默认示例:char *__env[1] = { 0 };
char **environ = __env;
 | 
execve
 | 
函数 | 将控制权交给新进程 | 默认示例:#include <errno.h>
#undef errno
extern int errno;
int execve(char *name, char **argv, char **env) 
{
  errno = ENOMEM;
  return -1;
}
 | 
fork
 | 
函数 | 创建新进程 | 默认示例:#include <errno.h>
#undef errno
extern int errno;
int fork(void) 
{
  errno = EAGAIN;
  return -1;
}
 | 
fstat
 | 
函数 | 获取打开的文件的状态 | 默认示例:#include <sys/stat.h>
int fstat(int file, struct stat *st) 
{
  st->st_mode = S_IFCHR;
  return 0;
}
 | 
getpid
 | 
函数 | 获取进程ID | 默认示例:int getpid(void) 
{
  return 1;
}
 | 
isatty
 | 
函数 | 查询输出流是否为终端 | 默认示例:int isatty(int file) 
{
  return 1;
}
 | 
kill
 | 
函数 | 发送信号 | 默认示例:#include <errno.h>
#undef errno
extern int errno;
int kill(int pid, int sig) 
{
  errno = EINVAL;
  return -1;
}
 | 
link
 | 
函数 | 给已有文件创建一个新名字 | 默认示例:#include <errno.h>
#undef errno
extern int errno;
int link(char *old, char *new) 
{
  errno = EMLINK;
  return -1;
}
 | 
lseek
 | 
函数 | 设置文件位置 | 默认示例:int lseek(int file, int ptr, int dir) 
{
  return 0;
}
 | 
open
 | 
函数 | 打开文件 | 默认示例:int open(const char *name, int flags, int mode) 
{
  return -1;
}
 | 
read
 | 
函数 | 读取文件 | 默认示例:int read(int file, char *ptr, int len) 
{
  return 0;
}
 | 
sbrk
 | 
函数 | 增大程序数据空间,默认的内存分配函数依赖此函数 | 默认示例:caddr_t sbrk(int incr) 
{
  extern char _end;		/* Defined by the linker */
  static char *heap_end;
  char *prev_heap_end;
 
  if (heap_end == 0) {
    heap_end = &_end;
  }
  prev_heap_end = heap_end;
  if (heap_end + incr > stack_ptr) {
    write (1, "Heap and stack collision\n", 25);
    abort ();
  }
  heap_end += incr;
  return (caddr_t) prev_heap_end;
}
 | 
stat
 | 
函数 | 获取文件状态 | 默认示例:int stat(char *file, struct stat *st) 
{
  st->st_mode = S_IFCHR;
  return 0;
}
 | 
times
 | 
函数 | 当前进程时间信息 | 默认示例:int times(struct tms *buf) 
{
  return -1;
}
 | 
unlink
 | 
函数 | 移除文件的目录入口,相当于删除 | 默认示例:#include <errno.h>
#undef errno
extern int errno;
int unlink(char *name) 
{
  errno = ENOENT;
  return -1; 
}
 | 
wait
 | 
函数 | 等待子进程 | 默认示例:#include <errno.h>
#undef errno
extern int errno;
int wait(int *status) 
{
  errno = ECHILD;
  return -1;
}
 | 
write
 | 
函数 | 写入数据到文件 | 默认示例:int write(int file, char *ptr, int len) 
{
  int todo;
  for (todo = 0; todo < len; todo++) {
    outbyte (*ptr++);
  }
  return len;
}
 | 
可重入OS接口
可重入OS接口用于多进程(线程)环境。
newlib的可重入实现在reent.h头文件中,可重入结构体定义为struct _reent,未启用_REENT_THREAD_LOCAL宏定义时实现可重入的要求如下:
- 每一个进程(线程)都必须有一个可重入结构体变量,并且在创建进程(线程)时初始化,删除进程(线程)时清理。个人推荐将可重入结构体附加到PCB(进程控制块)上。
 - 在调用相关库函数时将全局变量 
_impure_ptr改成当前进程(线程)的独有的可重入结构体变量指针或者启用__DYNAMIC_REENT__手动实现struct _reent * __getreent (void);函数。个人推荐在线程上下文切换时进行修改变量操作或者手动实现__getreent函数(对于多核CPU而言,只能手动实现__getreent,直接返回当前CPU的运行线程的可重入结构体变量指针)。 
当启用_REENT_THREAD_LOCAL 宏定义时,可重入的实现由线程局部(TLS)变量实现,无需可重入结构体,调用可重入函数时struct _reent 的指针为NULL。
| 函数 | 声明 | 
|---|---|
| _close_r | #include <reent.h>
int _close_r(struct _reent *ptr, int fd);
 | 
_execve_r
 | 
#include <reent.h>
int _execve_r(struct _reent *ptr, const char *name,char *const argv[], char *const env[]);
 | 
_fork_r
 | 
#include <reent.h>
int _fork_r(struct _reent *ptr);
 | 
_wait_r
 | 
#include <reent.h>
int _wait_r(struct _reent *ptr, int *status);
 | 
_fstat_r
 | 
#include <reent.h>
int _fstat_r(struct _reent *ptr,int fd, struct stat *pstat);
 | 
_link_r
 | 
#include <reent.h>
int _link_r(struct _reent *ptr,const char *old, const char *new);
 | 
_lseek_r
 | 
#include <reent.h>
off_t _lseek_r(struct _reent *ptr,int fd, off_t pos, int whence);
 | 
_open_r
 | 
#include <reent.h>
int _open_r(struct _reent *ptr,const char *file, int flags, int mode);
 | 
_read_r
 | 
#include <reent.h>
_ssize_t _read_r(struct _reent *ptr,int fd, void *buf, size_t cnt);
 | 
_sbrk_r
 | 
#include <reent.h>
void *_sbrk_r(struct _reent *ptr, ptrdiff_t incr);
 | 
_kill_r
 | 
#include <reent.h>
int _kill_r(struct _reent *ptr, int pid, int sig);
 | 
_getpid_r
 | 
#include <reent.h>
int _getpid_r(struct _reent *ptr);
 | 
_stat_r
 | 
#include <reent.h>
int _stat_r(struct _reent *ptr,const char *file, struct stat *pstat);
 | 
_times_r
 | 
#include <reent.h>
#include <sys/times.h>
clock_t _times_r(struct _reent *ptr, struct tms *ptms);
 | 
_unlink_r
 | 
#include <reent.h>
int _unlink_r(struct _reent *ptr, const char *file);
 | 
_write_r
 | 
#include <reent.h>
_ssize_t _write_r(struct _reent *ptr,int fd, const void *buf, size_t cnt);
 | 
内存管理
对于C语言而言,内存管理主要指动态内存分配与释放。
对于newlib而言,主要的内存管理函数如下:
| 函数 | 可重入版本 | 备注 | 
|---|---|---|
void *malloc(size_t nbytes);
 | 
void *_malloc_r(void *reent, size_t nbytes);
 | 
常用函数 | 
void *realloc(void *aptr, size_t nbytes);
 | 
void *_realloc_r(void *reent,   void *aptr, size_t nbytes);
 | 
|
void *reallocf(void *aptr, size_t nbytes);
 | 
void *_reallocf_r(void *reent,     void *aptr, size_t nbytes);
 | 
|
void free(void *aptr);
 | 
void _free_r(void *reent, void *aptr);
 | 
常用函数 | 
void *memalign(size_t align, size_t nbytes);
 | 
void *_memalign_r(void *reent,
  | 
|
size_t malloc_usable_size(void *aptr);
 | 
size_t _malloc_usable_size_r(void *reent, void *aptr);
 | 
对于有些RTOS而言,它们有自己的内存分配机制,通常直接重写malloc与free函数而不使用newlib自身的内存分配机制。
newlib的内存分配依靠sbrk(或_sbrk_r)实现,对于单线程环境而言,无需其它操作,对于多线程环境而言,需要给内存分配与释放加锁,需要手工实现以下内存分配锁:
| 函数 | 说明 | 
|---|---|
void __malloc_lock (struct _reent *reent);
 | 
malloc锁 | 
void __malloc_unlock (struct _reent *reent);
 | 
malloc解锁 | 
注意:内存分配锁中不能调用内存分配相关函数,尽量使用静态变量。内存分配锁应当具有递归锁的特点,即同一线程可多次加锁后解锁相同次数才解锁。
官方资料
网站: http://sourceware.org/newlib/
相关数据手册
Newlib 4.4.0
Newlib 4.3.0
相关资料
- picolibc:https://github.com/picolibc/picolibc