Reentrancy is a characteristic of library functions which allows multiple processes to use the same address space with assurance that the values stored in those spaces will remain constant between calls. Cygnus's implementation of the library functions ensures that whenever possible, these library functions are reentrant. However, there are some functions that can not be trivially made reentrant. Hooks have been provided to allow you to use these functions in a fully reentrant fashion. These hooks use the structure _reent defined in `reent.h'. A variable defined as `struct _reent' is called a reentrancy structure. All functions which must manipulate global information are available in two versions. The first version has the usual name, and uses a single global instance of the reentrancy structure. The second has a different name, normally formed by prepending `_' and appending `_r', and takes a pointer to the particular reentrancy structure to use. For example, the function fopen takes two arguments, file and mode, and uses the global reentrancy structure. The function _fopen_r takes the arguments, struct_reent, which is a pointer to an instance of the reentrancy structure, file and mode. There are two versions of `struct _reent', a normal one and one for small memory systems, controlled by the _REENT_SMALL definition from the (automatically included) `<sys/config.h>'. Each function which uses the global reentrancy structure uses the global variable _impure_ptr, which points to a reentrancy structure. This means that you have two ways to achieve reentrancy. Both require that each thread of execution control initialize a unique global variable of type `struct _reent':
1.Use the reentrant versions of the library functions, after initializing a global reentrancy structure for each process. Use the pointer to this structure as the extra argument for all library functions.
2.Ensure that each thread of execution control has a pointer to its own unique reentrancy structure in the global variable _impure_ptr, and call the standard library subroutines.
根据上述说明,Red Hat newlib C Library中的函数提供了可重入的特性,_r函数。而且,可重入性的实现有两种方法,一种是使用_r函数(方法1),一种是使用原函数(方法2)。现在特别想知道第二种方法。希望高手能够说一说方法1和2具体的代码如何实现,告诉代码的下载地址也行。谢谢!
7 个解决方案
#1
屏幕拉不过来了
#2
不好意思,没注意这个问题。
Reentrancy
Reentrancy is a characteristic of library functions which allows multiple processes to use the same address space with assurance that the values stored in those spaces will remain constant between calls. Cygnus's implementation of the library functions ensures that whenever possible, these library functions are reentrant. However, there are some functions that can not be trivially made reentrant. Hooks have been provided to allow you to use these functions in a fully reentrant fashion. These hooks use the structure _reent defined in `reent.h'. A variable defined as `struct _reent' is called a reentrancy structure. All functions which must manipulate global information are available in two versions. The first version has the usual name, and uses a single global instance of the reentrancy structure. The second has a different name, normally formed by prepending `_' and appending `_r', and takes a pointer to the particular reentrancy structure to use. For example, the function fopen takes two arguments, file and mode, and uses the global reentrancy structure. The function _fopen_r takes the arguments, struct_reent, which is a pointer to an instance of the reentrancy structure, file and mode. There are two versions of `struct _reent', a normal one and one for small memory systems, controlled by the _REENT_SMALL definition from the (automatically included) ` <sys/config.h>'. Each function which uses the global reentrancy structure uses the global variable _impure_ptr, which points to a reentrancy structure. This means that you have two ways to achieve reentrancy. Both require that each thread of execution control initialize a unique global variable of type `struct _reent':
1.Use the reentrant versions of the library functions, after initializing a global reentrancy structure for each process. Use the pointer to this structure as the extra argument for all library functions.
2.Ensure that each thread of execution control has a pointer to its own unique reentrancy structure in the global variable _impure_ptr, and call the standard library subroutines.
根据上述说明,Red Hat newlib C Library中的函数提供了可重入的特性,_r函数。而且,可重入性的实现有两种方法,一种是使用_r函数(方法1),一种是使用原函数(方法2)。现在特别想知道第二种方法。希望高手能够说一说方法1和2具体的代码如何实现,告诉代码的下载地址也行。谢谢!
Reentrancy
Reentrancy is a characteristic of library functions which allows multiple processes to use the same address space with assurance that the values stored in those spaces will remain constant between calls. Cygnus's implementation of the library functions ensures that whenever possible, these library functions are reentrant. However, there are some functions that can not be trivially made reentrant. Hooks have been provided to allow you to use these functions in a fully reentrant fashion. These hooks use the structure _reent defined in `reent.h'. A variable defined as `struct _reent' is called a reentrancy structure. All functions which must manipulate global information are available in two versions. The first version has the usual name, and uses a single global instance of the reentrancy structure. The second has a different name, normally formed by prepending `_' and appending `_r', and takes a pointer to the particular reentrancy structure to use. For example, the function fopen takes two arguments, file and mode, and uses the global reentrancy structure. The function _fopen_r takes the arguments, struct_reent, which is a pointer to an instance of the reentrancy structure, file and mode. There are two versions of `struct _reent', a normal one and one for small memory systems, controlled by the _REENT_SMALL definition from the (automatically included) ` <sys/config.h>'. Each function which uses the global reentrancy structure uses the global variable _impure_ptr, which points to a reentrancy structure. This means that you have two ways to achieve reentrancy. Both require that each thread of execution control initialize a unique global variable of type `struct _reent':
1.Use the reentrant versions of the library functions, after initializing a global reentrancy structure for each process. Use the pointer to this structure as the extra argument for all library functions.
2.Ensure that each thread of execution control has a pointer to its own unique reentrancy structure in the global variable _impure_ptr, and call the standard library subroutines.
根据上述说明,Red Hat newlib C Library中的函数提供了可重入的特性,_r函数。而且,可重入性的实现有两种方法,一种是使用_r函数(方法1),一种是使用原函数(方法2)。现在特别想知道第二种方法。希望高手能够说一说方法1和2具体的代码如何实现,告诉代码的下载地址也行。谢谢!
#3
Reentrancy
Reentrancy is a characteristic of library functions which allows multiple processes to use the same address space with assurance that the values stored in those spaces will remain constant between calls. Cygnus's implementation of the library functions ensures that whenever possible, these library functions are reentrant. However, there are some functions that can not be trivially made reentrant. Hooks have been provided to allow you to use these functions in a fully reentrant fashion. These hooks use the structure _reent defined in `reent.h'. A variable defined as `struct _reent' is called a reentrancy structure. All functions which must manipulate global information are available in two versions. The first version has the usual name, and uses a single global instance of the reentrancy structure. The second has a different name, normally formed by prepending `_' and appending `_r', and takes a pointer to the particular reentrancy structure to use. For example, the function fopen takes two arguments, file and mode, and uses the global reentrancy structure. The function _fopen_r takes the arguments, struct_reent, which is a pointer to an instance of the reentrancy structure, file and mode. There are two versions of `struct _reent', a normal one and one for small memory systems, controlled by the _REENT_SMALL definition from the (automatically included) ` <sys/config.h>'. Each function which uses the global reentrancy structure uses the global variable _impure_ptr, which points to a reentrancy structure. This means that you have two ways to achieve reentrancy. Both require that each thread of execution control initialize a unique global variable of type `struct _reent':
1.Use the reentrant versions of the library functions, after initializing a global reentrancy structure for each process. Use the pointer to this structure as the extra argument for all library functions.
2.Ensure that each thread of execution control has a pointer to its own unique reentrancy structure in the global variable _impure_ptr, and call the standard library subroutines.
根据上述说明,Red Hat newlib C Library中的函数提供了可重入的特性,_r函数。而且,可重入性的实现有两种方法,一种是使用_r函数(方法1),一种是使用原函数(方法2)。现在特别想知道第二种方法。希望高手能够说一说方法1和2具体的代码如何实现,告诉代码的下载地址也行。谢谢!
Reentrancy is a characteristic of library functions which allows multiple processes to use the same address space with assurance that the values stored in those spaces will remain constant between calls. Cygnus's implementation of the library functions ensures that whenever possible, these library functions are reentrant. However, there are some functions that can not be trivially made reentrant. Hooks have been provided to allow you to use these functions in a fully reentrant fashion. These hooks use the structure _reent defined in `reent.h'. A variable defined as `struct _reent' is called a reentrancy structure. All functions which must manipulate global information are available in two versions. The first version has the usual name, and uses a single global instance of the reentrancy structure. The second has a different name, normally formed by prepending `_' and appending `_r', and takes a pointer to the particular reentrancy structure to use. For example, the function fopen takes two arguments, file and mode, and uses the global reentrancy structure. The function _fopen_r takes the arguments, struct_reent, which is a pointer to an instance of the reentrancy structure, file and mode. There are two versions of `struct _reent', a normal one and one for small memory systems, controlled by the _REENT_SMALL definition from the (automatically included) ` <sys/config.h>'. Each function which uses the global reentrancy structure uses the global variable _impure_ptr, which points to a reentrancy structure. This means that you have two ways to achieve reentrancy. Both require that each thread of execution control initialize a unique global variable of type `struct _reent':
1.Use the reentrant versions of the library functions, after initializing a global reentrancy structure for each process. Use the pointer to this structure as the extra argument for all library functions.
2.Ensure that each thread of execution control has a pointer to its own unique reentrancy structure in the global variable _impure_ptr, and call the standard library subroutines.
根据上述说明,Red Hat newlib C Library中的函数提供了可重入的特性,_r函数。而且,可重入性的实现有两种方法,一种是使用_r函数(方法1),一种是使用原函数(方法2)。现在特别想知道第二种方法。希望高手能够说一说方法1和2具体的代码如何实现,告诉代码的下载地址也行。谢谢!
#4
Reentrancy
Reentrancy is a characteristic of library functions which allows multiple processes to use the
same address space with assurance that the values stored in those spaces will remain const
ant between calls. Cygnus's implementation of the library functions ensures that whenever
possible, these library functions are reentrant. However, there are some functions that can
not be trivially made reentrant. Hooks have been provided to allow you to use these function
s in a fully reentrant fashion. These hooks use the structure _reent defined in `reent.h'. A
variable defined as `struct _reent' is called a reentrancy structure. All functions which must
manipulate global information are available in two versions. The first version has the usual
name, and uses a single global instance of the reentrancy structure. The second has a
different name, normally formed by prepending `_' and appending `_r', and takes a pointer
to the particular reentrancy structure to use. For example, the function fopen takes two
arguments, file and mode, and uses the global reentrancy structure. The function _fopen_r
takes the arguments, struct_reent, which is a pointer to an instance of the reentrancy
structure, file and mode. There are two versions of `struct _reent', a normal one and one
for small memory systems, controlled by the _REENT_SMALL definition from the
(automatically included) ` <sys/config.h>'. Each function which uses the global reentrancy
structure uses the global variable _impure_ptr, which points to a reentrancy structure. This
means that you have two ways to achieve reentrancy. Both require that each thread of
execution control initialize a unique global variable of type `struct _reent':
1.Use the reentrant versions of the library functions, after initializing a global reentrancy
structure for each process. Use the pointer to this structure as the extra argument for all
library functions.
2.Ensure that each thread of execution control has a pointer to its own unique reentrancy
structure in the global variable _impure_ptr, and call the standard library subroutines.
根据上述说明,Red Hat newlib C Library中的函数提供了可重入的特性,_r函数。而且,可重入性的实现
有两种方法,一种是使用_r函数(方法1),一种是使用原函数(方法2)。现在特别想知道第二种方法。希望高手
能够说一说方法1和2具体的代码如何实现,告诉代码的下载地址也行。谢谢!
Reentrancy is a characteristic of library functions which allows multiple processes to use the
same address space with assurance that the values stored in those spaces will remain const
ant between calls. Cygnus's implementation of the library functions ensures that whenever
possible, these library functions are reentrant. However, there are some functions that can
not be trivially made reentrant. Hooks have been provided to allow you to use these function
s in a fully reentrant fashion. These hooks use the structure _reent defined in `reent.h'. A
variable defined as `struct _reent' is called a reentrancy structure. All functions which must
manipulate global information are available in two versions. The first version has the usual
name, and uses a single global instance of the reentrancy structure. The second has a
different name, normally formed by prepending `_' and appending `_r', and takes a pointer
to the particular reentrancy structure to use. For example, the function fopen takes two
arguments, file and mode, and uses the global reentrancy structure. The function _fopen_r
takes the arguments, struct_reent, which is a pointer to an instance of the reentrancy
structure, file and mode. There are two versions of `struct _reent', a normal one and one
for small memory systems, controlled by the _REENT_SMALL definition from the
(automatically included) ` <sys/config.h>'. Each function which uses the global reentrancy
structure uses the global variable _impure_ptr, which points to a reentrancy structure. This
means that you have two ways to achieve reentrancy. Both require that each thread of
execution control initialize a unique global variable of type `struct _reent':
1.Use the reentrant versions of the library functions, after initializing a global reentrancy
structure for each process. Use the pointer to this structure as the extra argument for all
library functions.
2.Ensure that each thread of execution control has a pointer to its own unique reentrancy
structure in the global variable _impure_ptr, and call the standard library subroutines.
根据上述说明,Red Hat newlib C Library中的函数提供了可重入的特性,_r函数。而且,可重入性的实现
有两种方法,一种是使用_r函数(方法1),一种是使用原函数(方法2)。现在特别想知道第二种方法。希望高手
能够说一说方法1和2具体的代码如何实现,告诉代码的下载地址也行。谢谢!
#5
在unix高级环境编程中有讲述
如果要下载函数的源代码的话
有个netbsd的libc
http://cvsweb.netbsd.org/bsdweb.cgi/src/lib/libc/
glibc也能在其他网站上找到
如果要下载函数的源代码的话
有个netbsd的libc
http://cvsweb.netbsd.org/bsdweb.cgi/src/lib/libc/
glibc也能在其他网站上找到
#6
http://61.185.242.103/~kjqk/dzkjdxxb/dzkj2005/0504pdf/050425.pdf
应该是你要的东西
应该是你要的东西
#7
【摘要】对嵌入式C运行岸- newlib进行了深入研究,阐述了该运行库在多任务坏境下可重入性的实现方
法;介绍了移植newlib到嵌入式系统上需要的桩函数及其实现方法,并重点介绍了与珊相关的四个桩函数.pen,
close, read和write的实现方法,以及动态内存分配器malloc的两种实现方法.
关键词嵌入式系统;C运行库;可重入性;桩函数
中图分类号TP393文献标识码A
Research and Minimum Accomplishment of Newlib
ZHANG Yu-min, LUO Lei
(School of Computer Science andVEST of China Chengdu 610054)
This article has a research on a embedded CLibrary: newlib.
way to accomplish the reentry of newlib
implementation which is, needed by porting
write implementation which relate with I/O,
under multi-task environment. It introduces
It presents the
stub functions
newlib on embedded system, especially open, close, read and
and malloc two implementation
Key words embedded system; newlib; c runtime library;
1公叹10nways.
reentry; stub
1 Newlib简介
Newlib是一个面向嵌入式系统的C运行库.最初是由Solutions收集组装的一个源代码集合,取
名为newlib,现在由Red Hat维护,目前的最新的版本是1.11.01'1.
对于与GNU兼容的嵌入式C运行库,Newlib并不是唯一的选择,但是从成熟度来讲,newlib是最优秀
的.newlib具有独特的体系结构,使得它能够非常好地满足深度嵌入式系统的要求.newlib可移植性强,
具有可重入特性,功能完备等特点,已广泛应用于各种嵌入式系统中.
2可重入性的实现
C运行库的可重入性问题主要是库中的全局变量在多任务环境下的可重入性问题,Newlib解决这个问
题的方法是,定义一个struct一ent类型的结构,将运行库所有会引起可重入性问题的全局变量都放到该结
构中.而这些全局变量则被重新定义为若干个宏,以errn.为例,名为"ermo"的宏引用指向struct reent结
构类型的一个全局指针,这个指针叫做-impure_ptra
收稿日期:2003一12一05
基金项目:四川省重点科技攻关项目
作者简介:张宇昊(1978一),男,硕士,主要从事嵌入式系统软件方面的研究;罗曹(1967一),女,硕士,教授,主要从事嵌入式系统软件方
面的研究.
电子科技大学学报第34卷
对于用户,这一切都被ermo宏隐藏了,需要检查错误时,用户只需要像其他ANSI C环境下所做的一样,
检查ermo"变量"就可以了.实际上,用户对ermo宏的访问是返回_impure_ptr->erm.的值,而不是一个全
局变量的值.
Newlib定义了一ent结构类型的一个静态实例,并在系统初始化时用全局指针-unpure_ptr指向它.如果
系统中只有一个任务,那么系统将正常运行,不需要做额外的工作;如果希望newlib运行在多任务环境下,
必须完成下面的两个步骤:
1)每个任务提供一个一ent结构的实例并初始化;
2)任务上下文切换的时刻重新设置_impure_ptr指针,使它指向即将投入运行任务的一ent结构实例.
这样就可以保障大多数库函数(尤其是stdio库函数)的可重入性.如果需要可重入的malloc,还必须设法
实现一malloc lockOjV一 malloc unlockO f数,它们在内存分配过程中保障堆((heap)在多任务环境下的安全.
3 Newlib的移植
Newlib的所有库函数都建立在20个桩函数的基础上[2],这20个桩函数完成一些newlib无法实现的功能:
1)级I/O和文件系统访问(open, close, read, write, lseek, stat, fstat, fcntl, link, unlink, rename);
2)扩大内存堆的需求(sbrk);
3)获得当前系统的日期和时间((gettimeofday, times);
4)各种类型的任务管理函数(execve, fork, getpid, kill, wait,exit);
这20个桩函数在语义,语法上与POSIx标准下对应的20个同名系统调用是完全兼容的[3].成功移植newlib
的关键是在目标系统环境下,找到能够与这些桩函数衔接的功能函数并实现这些桩函数.
Newlib为每个桩函数提供了可重入的和不可重入的两种版本.两种版本的区别在于,如果不可重入版
桩函数的名字是xxx,则对应的可重入版桩函数的名字是'xx r,如close和一loser, open和一pen -r,等等.
此外,可重入的桩函数在参数表中含有一个一ent结构指针,这个指针使得系统的实现者能在库和目标操
作环境之间传送上下文相关的信息,尤其是发生错误时,能够便捷的传送ermo的值到适当的任务中.
所谓最小实现是指,假定将要移植的目标系统中没有文件系统,也没有符合POSIX标准的任务管理机
制和应用编程接口((Application Programming Interface, API),仅仅实现newlib的一个最小移植.在newlib的
移植过程中全功能实现的桩函数只有open, close, read, write和sbrk五个,其他桩函数仅仅实现一个返回
错误的空函数.
任务管理的execve, fork, getpid, kill, wait和exit六个桩函数,仅仅实现一个返回一1的空函数,返回
之前将ermo设置为ENOTSUP,表示系统不支持该函数.
与文件相关的link和unlink桩函数也仅仅实现一个返回一1的空函数,将errn.设置为EMLINK表示连接过
多;lseek函数则不需要返回任何错误,直接返回0,表示操作成功.
fstat和stat桩函数在newlib中主要用于判断流的类型(常规文件,字符设备,目录),将其实现为不论输
入参数如何,都返回字符设备类型的空函数.
times桩函数返回当前进程中的各种时间信息,如果目标系统中的任务不能提供类似的时间信息,仅仅
实现一个返回一I的空函数,将ermo设置为ENOTSUP.
由于newlib认为在目标系统中fcntl, rename和gettimeofday三个桩函数缺省是不提供的,所以也不提供
这三个桩函数的实现.
4 1/O桩函数的实现
Newlib在使用open, close, read和write桩函数时严格遵守POSIX标准,为了使实现的桩函数完全符合
POSIX,就必须在内部机制上实现设备名表,文件描述符表和驱动地址表3个表的相关操作.
4.1三个表的结构,作用及相关操作
1)设备名表记录系统中所有设备的名字及其设备号.系统初始化时必须将所有的设备名及其设备号填
入表中备查.
第4期张宇曼等:Newlib的研究与最小实现
对于设备名表应该实现以下两个操作:
(1)设备名/设备号注册函数NameRegister;
(2)从设备名到设备号的转换函数NameLookup;
2)文件描述符表记录系统中当前打开的设备的设备号.每个表项代表一个处于打开状态的设备.每个
表项的索引值就是需要返回给用户的文件描述符.
对文件描述符表需要实现以下3个操作:
(1)文件描述符分配函数FdAllocate;
(2)文件描述符释放函数FdFree;
(3)从文件描述符到设备号的转换函数Fd2DevCode;
3)驱动地址表记录系统中每个驱动程序的入口地址.每个表项代表一个驱动程序,对每个驱动程序都
应该实现五个具有统一接口的操组函数:init, open, close, read, write.每个表项在表中的索引值就是该
设备的设备号.需要注意是每个驱动程序都必须提供init操作.
对驱动地址表需要实现以下操作:
初始化驱动表中的所有驱动函数InitAllDrivers;
该操作对表中的每一个驱动程序调用init操作,完成表中所有驱动程序的初始化操作.
在系统初始化的时间,应该调用InitAllDriverso操作,完成系统中所有驱动程序的初始化操作.在每个
驱动程序的init操作中,应该调用NameRegistero操作,完成驱动程序对应的设备注册,以COMI驱动程序
的com l_ inito操作为例,它的实现如下:
void coml_ init(int devCode)
{
/*首先注册设备名和设备号到设备名表中*/
NameRegister("COMl", devCode);
/*然后完成其他的设备初始化操作*/
}
只要所有的设备驱动程序都遵守这个约定,在系统初始化完成之后,系统中所有的驱动程序就得到了
初始化,并且系统中所有的设备都注册到了设备名表中.后续的v0桩函数的实现就非常容易了.
设备名表,文件描述符表和驱动地址表3个表的结构及相关操作如图I所示.
文件描述符表设备名表驱动地址表
fd设备号设备名设备号设备号init open close read write
图1文件描述符表,设备名表和驱动地址表
4.2 open桩函数的实现
open桩函数的实现流程如下:
1)用NameLookupo操作在设备名表中搜索匹配的设备名,并获得对应的设备号;
2)用FdAllocateo操作从文件描述符表中分配一个空的表项,填入设备号,并获得对应的索引号即fd;
3)通过设备号直接调用驱动地址表中对应驱动程序的open操作;
4)返回fdo
4.3 read, write和close桩函数的实现
read和write桩函数的实现方法完全相同,流程如下:
电子科技大学学报第34卷
1)调用Fd2DevCode()操作获得与输入参数fd对应的设备号devCode;
2)通过设备号直接调用驱动地址表中对应驱动的read或write操作;
3)返回实际交换的数据量.
close桩函数的实现与read, write几乎完全相同,唯一不同之处在于最后调用FdFree()操作,释放fd而不
是返回实际交换的数据量,流程如下:
1)调用Fd2DevCode()操作获得与输入参数fd对应的设备号devCode;
2)通过设备号直接调用驱动地址表中对应驱动的close操作;
3)调用FdFree()操作释放fdo
至此,与设备I/O相关的四个桩函数open, close, read和write的实现就全部完成了.
本文没有介绍驱动程序的实现方法,并不是驱动程序不重要,恰恰相反,驱动程序中必须完成可靠高
效的设备操作,保证驱动程序的各项操作在语义上与上面4个桩函数完全一致,并且实质性的操作都在驱
动程序中完成.因此,在驱动程序的实现上必须仔细斟酌.由于篇幅的原因,不再赘述.
5关于malloc
大多数嵌入式操作系统都实现了自己的动态内存分配机制,并且提供了多任务环境下对内存分配机制
的保护措施,如果移植newlib到这样的系统时,可以放弃newlib自带的malloc函数.尽管newlib自带的malloc
非常高效,但是几乎所有的用户都习惯使用malloc来作为动态内存分配器.在这种情况下,最好对系统自
带的动态内存分配API进行封装,使它不论在风格,外观上,还是在语义上都与malloc完全相同,这对于提
高应用程序的可移植性大有好处.
对于那些没有实现动态内存分配机制的嵌入式系统环境来说,newlib的malloc是一个非常好的选择,
只需实现sbrk桩函数,malloc就可以非常好地工作起来.与之同名的POSIX系统调用的作用是从系统中获得
一块内存,每当malloc需要更多的内存时,都会调用sbrk函数.
在单任务环境下,只需实现sbrk桩函数,malloc就可以正常运行;但在多任务环境下,还需实现
一一malloc locko和一malloc unlock()函数,newlib用这两个函数来保护内存堆免受冲击.用户可利用目标环
境中的互斥信号量机制来实现这两个函数,在一malloc lock()函数中申请互斥信号量,而在一malloc unlock()
函数中释放同一个互斥信号量.
6结束语
本文中详细介绍了newlib作为一个面向嵌入式系统的C运行库所具备的各种特点,重点介绍了ewlib在
多任务环境下的可重入性和实现方法,以及ewlib的移植方法两方面的内容.尤其是针对newlib的移植,详
细介绍了每一个桩函数的实现方法,并给出了许多的代码实例.但是并不是说对newlib只能像本文中所描
述的那样进行移植,本文给出的仅仅是移植newlib到特定系统之上的一个最小实现.Newlib的体系结及其
清晰简洁的实现方法决定了newlib具有非常的灵活性,能够适应各式各样的系统和目标环境.随着研究工
作的深入,newlib在目标系统中将会发挥更大的作用.
法;介绍了移植newlib到嵌入式系统上需要的桩函数及其实现方法,并重点介绍了与珊相关的四个桩函数.pen,
close, read和write的实现方法,以及动态内存分配器malloc的两种实现方法.
关键词嵌入式系统;C运行库;可重入性;桩函数
中图分类号TP393文献标识码A
Research and Minimum Accomplishment of Newlib
ZHANG Yu-min, LUO Lei
(School of Computer Science andVEST of China Chengdu 610054)
This article has a research on a embedded CLibrary: newlib.
way to accomplish the reentry of newlib
implementation which is, needed by porting
write implementation which relate with I/O,
under multi-task environment. It introduces
It presents the
stub functions
newlib on embedded system, especially open, close, read and
and malloc two implementation
Key words embedded system; newlib; c runtime library;
1公叹10nways.
reentry; stub
1 Newlib简介
Newlib是一个面向嵌入式系统的C运行库.最初是由Solutions收集组装的一个源代码集合,取
名为newlib,现在由Red Hat维护,目前的最新的版本是1.11.01'1.
对于与GNU兼容的嵌入式C运行库,Newlib并不是唯一的选择,但是从成熟度来讲,newlib是最优秀
的.newlib具有独特的体系结构,使得它能够非常好地满足深度嵌入式系统的要求.newlib可移植性强,
具有可重入特性,功能完备等特点,已广泛应用于各种嵌入式系统中.
2可重入性的实现
C运行库的可重入性问题主要是库中的全局变量在多任务环境下的可重入性问题,Newlib解决这个问
题的方法是,定义一个struct一ent类型的结构,将运行库所有会引起可重入性问题的全局变量都放到该结
构中.而这些全局变量则被重新定义为若干个宏,以errn.为例,名为"ermo"的宏引用指向struct reent结
构类型的一个全局指针,这个指针叫做-impure_ptra
收稿日期:2003一12一05
基金项目:四川省重点科技攻关项目
作者简介:张宇昊(1978一),男,硕士,主要从事嵌入式系统软件方面的研究;罗曹(1967一),女,硕士,教授,主要从事嵌入式系统软件方
面的研究.
电子科技大学学报第34卷
对于用户,这一切都被ermo宏隐藏了,需要检查错误时,用户只需要像其他ANSI C环境下所做的一样,
检查ermo"变量"就可以了.实际上,用户对ermo宏的访问是返回_impure_ptr->erm.的值,而不是一个全
局变量的值.
Newlib定义了一ent结构类型的一个静态实例,并在系统初始化时用全局指针-unpure_ptr指向它.如果
系统中只有一个任务,那么系统将正常运行,不需要做额外的工作;如果希望newlib运行在多任务环境下,
必须完成下面的两个步骤:
1)每个任务提供一个一ent结构的实例并初始化;
2)任务上下文切换的时刻重新设置_impure_ptr指针,使它指向即将投入运行任务的一ent结构实例.
这样就可以保障大多数库函数(尤其是stdio库函数)的可重入性.如果需要可重入的malloc,还必须设法
实现一malloc lockOjV一 malloc unlockO f数,它们在内存分配过程中保障堆((heap)在多任务环境下的安全.
3 Newlib的移植
Newlib的所有库函数都建立在20个桩函数的基础上[2],这20个桩函数完成一些newlib无法实现的功能:
1)级I/O和文件系统访问(open, close, read, write, lseek, stat, fstat, fcntl, link, unlink, rename);
2)扩大内存堆的需求(sbrk);
3)获得当前系统的日期和时间((gettimeofday, times);
4)各种类型的任务管理函数(execve, fork, getpid, kill, wait,exit);
这20个桩函数在语义,语法上与POSIx标准下对应的20个同名系统调用是完全兼容的[3].成功移植newlib
的关键是在目标系统环境下,找到能够与这些桩函数衔接的功能函数并实现这些桩函数.
Newlib为每个桩函数提供了可重入的和不可重入的两种版本.两种版本的区别在于,如果不可重入版
桩函数的名字是xxx,则对应的可重入版桩函数的名字是'xx r,如close和一loser, open和一pen -r,等等.
此外,可重入的桩函数在参数表中含有一个一ent结构指针,这个指针使得系统的实现者能在库和目标操
作环境之间传送上下文相关的信息,尤其是发生错误时,能够便捷的传送ermo的值到适当的任务中.
所谓最小实现是指,假定将要移植的目标系统中没有文件系统,也没有符合POSIX标准的任务管理机
制和应用编程接口((Application Programming Interface, API),仅仅实现newlib的一个最小移植.在newlib的
移植过程中全功能实现的桩函数只有open, close, read, write和sbrk五个,其他桩函数仅仅实现一个返回
错误的空函数.
任务管理的execve, fork, getpid, kill, wait和exit六个桩函数,仅仅实现一个返回一1的空函数,返回
之前将ermo设置为ENOTSUP,表示系统不支持该函数.
与文件相关的link和unlink桩函数也仅仅实现一个返回一1的空函数,将errn.设置为EMLINK表示连接过
多;lseek函数则不需要返回任何错误,直接返回0,表示操作成功.
fstat和stat桩函数在newlib中主要用于判断流的类型(常规文件,字符设备,目录),将其实现为不论输
入参数如何,都返回字符设备类型的空函数.
times桩函数返回当前进程中的各种时间信息,如果目标系统中的任务不能提供类似的时间信息,仅仅
实现一个返回一I的空函数,将ermo设置为ENOTSUP.
由于newlib认为在目标系统中fcntl, rename和gettimeofday三个桩函数缺省是不提供的,所以也不提供
这三个桩函数的实现.
4 1/O桩函数的实现
Newlib在使用open, close, read和write桩函数时严格遵守POSIX标准,为了使实现的桩函数完全符合
POSIX,就必须在内部机制上实现设备名表,文件描述符表和驱动地址表3个表的相关操作.
4.1三个表的结构,作用及相关操作
1)设备名表记录系统中所有设备的名字及其设备号.系统初始化时必须将所有的设备名及其设备号填
入表中备查.
第4期张宇曼等:Newlib的研究与最小实现
对于设备名表应该实现以下两个操作:
(1)设备名/设备号注册函数NameRegister;
(2)从设备名到设备号的转换函数NameLookup;
2)文件描述符表记录系统中当前打开的设备的设备号.每个表项代表一个处于打开状态的设备.每个
表项的索引值就是需要返回给用户的文件描述符.
对文件描述符表需要实现以下3个操作:
(1)文件描述符分配函数FdAllocate;
(2)文件描述符释放函数FdFree;
(3)从文件描述符到设备号的转换函数Fd2DevCode;
3)驱动地址表记录系统中每个驱动程序的入口地址.每个表项代表一个驱动程序,对每个驱动程序都
应该实现五个具有统一接口的操组函数:init, open, close, read, write.每个表项在表中的索引值就是该
设备的设备号.需要注意是每个驱动程序都必须提供init操作.
对驱动地址表需要实现以下操作:
初始化驱动表中的所有驱动函数InitAllDrivers;
该操作对表中的每一个驱动程序调用init操作,完成表中所有驱动程序的初始化操作.
在系统初始化的时间,应该调用InitAllDriverso操作,完成系统中所有驱动程序的初始化操作.在每个
驱动程序的init操作中,应该调用NameRegistero操作,完成驱动程序对应的设备注册,以COMI驱动程序
的com l_ inito操作为例,它的实现如下:
void coml_ init(int devCode)
{
/*首先注册设备名和设备号到设备名表中*/
NameRegister("COMl", devCode);
/*然后完成其他的设备初始化操作*/
}
只要所有的设备驱动程序都遵守这个约定,在系统初始化完成之后,系统中所有的驱动程序就得到了
初始化,并且系统中所有的设备都注册到了设备名表中.后续的v0桩函数的实现就非常容易了.
设备名表,文件描述符表和驱动地址表3个表的结构及相关操作如图I所示.
文件描述符表设备名表驱动地址表
fd设备号设备名设备号设备号init open close read write
图1文件描述符表,设备名表和驱动地址表
4.2 open桩函数的实现
open桩函数的实现流程如下:
1)用NameLookupo操作在设备名表中搜索匹配的设备名,并获得对应的设备号;
2)用FdAllocateo操作从文件描述符表中分配一个空的表项,填入设备号,并获得对应的索引号即fd;
3)通过设备号直接调用驱动地址表中对应驱动程序的open操作;
4)返回fdo
4.3 read, write和close桩函数的实现
read和write桩函数的实现方法完全相同,流程如下:
电子科技大学学报第34卷
1)调用Fd2DevCode()操作获得与输入参数fd对应的设备号devCode;
2)通过设备号直接调用驱动地址表中对应驱动的read或write操作;
3)返回实际交换的数据量.
close桩函数的实现与read, write几乎完全相同,唯一不同之处在于最后调用FdFree()操作,释放fd而不
是返回实际交换的数据量,流程如下:
1)调用Fd2DevCode()操作获得与输入参数fd对应的设备号devCode;
2)通过设备号直接调用驱动地址表中对应驱动的close操作;
3)调用FdFree()操作释放fdo
至此,与设备I/O相关的四个桩函数open, close, read和write的实现就全部完成了.
本文没有介绍驱动程序的实现方法,并不是驱动程序不重要,恰恰相反,驱动程序中必须完成可靠高
效的设备操作,保证驱动程序的各项操作在语义上与上面4个桩函数完全一致,并且实质性的操作都在驱
动程序中完成.因此,在驱动程序的实现上必须仔细斟酌.由于篇幅的原因,不再赘述.
5关于malloc
大多数嵌入式操作系统都实现了自己的动态内存分配机制,并且提供了多任务环境下对内存分配机制
的保护措施,如果移植newlib到这样的系统时,可以放弃newlib自带的malloc函数.尽管newlib自带的malloc
非常高效,但是几乎所有的用户都习惯使用malloc来作为动态内存分配器.在这种情况下,最好对系统自
带的动态内存分配API进行封装,使它不论在风格,外观上,还是在语义上都与malloc完全相同,这对于提
高应用程序的可移植性大有好处.
对于那些没有实现动态内存分配机制的嵌入式系统环境来说,newlib的malloc是一个非常好的选择,
只需实现sbrk桩函数,malloc就可以非常好地工作起来.与之同名的POSIX系统调用的作用是从系统中获得
一块内存,每当malloc需要更多的内存时,都会调用sbrk函数.
在单任务环境下,只需实现sbrk桩函数,malloc就可以正常运行;但在多任务环境下,还需实现
一一malloc locko和一malloc unlock()函数,newlib用这两个函数来保护内存堆免受冲击.用户可利用目标环
境中的互斥信号量机制来实现这两个函数,在一malloc lock()函数中申请互斥信号量,而在一malloc unlock()
函数中释放同一个互斥信号量.
6结束语
本文中详细介绍了newlib作为一个面向嵌入式系统的C运行库所具备的各种特点,重点介绍了ewlib在
多任务环境下的可重入性和实现方法,以及ewlib的移植方法两方面的内容.尤其是针对newlib的移植,详
细介绍了每一个桩函数的实现方法,并给出了许多的代码实例.但是并不是说对newlib只能像本文中所描
述的那样进行移植,本文给出的仅仅是移植newlib到特定系统之上的一个最小实现.Newlib的体系结及其
清晰简洁的实现方法决定了newlib具有非常的灵活性,能够适应各式各样的系统和目标环境.随着研究工
作的深入,newlib在目标系统中将会发挥更大的作用.
#1
屏幕拉不过来了
#2
不好意思,没注意这个问题。
Reentrancy
Reentrancy is a characteristic of library functions which allows multiple processes to use the same address space with assurance that the values stored in those spaces will remain constant between calls. Cygnus's implementation of the library functions ensures that whenever possible, these library functions are reentrant. However, there are some functions that can not be trivially made reentrant. Hooks have been provided to allow you to use these functions in a fully reentrant fashion. These hooks use the structure _reent defined in `reent.h'. A variable defined as `struct _reent' is called a reentrancy structure. All functions which must manipulate global information are available in two versions. The first version has the usual name, and uses a single global instance of the reentrancy structure. The second has a different name, normally formed by prepending `_' and appending `_r', and takes a pointer to the particular reentrancy structure to use. For example, the function fopen takes two arguments, file and mode, and uses the global reentrancy structure. The function _fopen_r takes the arguments, struct_reent, which is a pointer to an instance of the reentrancy structure, file and mode. There are two versions of `struct _reent', a normal one and one for small memory systems, controlled by the _REENT_SMALL definition from the (automatically included) ` <sys/config.h>'. Each function which uses the global reentrancy structure uses the global variable _impure_ptr, which points to a reentrancy structure. This means that you have two ways to achieve reentrancy. Both require that each thread of execution control initialize a unique global variable of type `struct _reent':
1.Use the reentrant versions of the library functions, after initializing a global reentrancy structure for each process. Use the pointer to this structure as the extra argument for all library functions.
2.Ensure that each thread of execution control has a pointer to its own unique reentrancy structure in the global variable _impure_ptr, and call the standard library subroutines.
根据上述说明,Red Hat newlib C Library中的函数提供了可重入的特性,_r函数。而且,可重入性的实现有两种方法,一种是使用_r函数(方法1),一种是使用原函数(方法2)。现在特别想知道第二种方法。希望高手能够说一说方法1和2具体的代码如何实现,告诉代码的下载地址也行。谢谢!
Reentrancy
Reentrancy is a characteristic of library functions which allows multiple processes to use the same address space with assurance that the values stored in those spaces will remain constant between calls. Cygnus's implementation of the library functions ensures that whenever possible, these library functions are reentrant. However, there are some functions that can not be trivially made reentrant. Hooks have been provided to allow you to use these functions in a fully reentrant fashion. These hooks use the structure _reent defined in `reent.h'. A variable defined as `struct _reent' is called a reentrancy structure. All functions which must manipulate global information are available in two versions. The first version has the usual name, and uses a single global instance of the reentrancy structure. The second has a different name, normally formed by prepending `_' and appending `_r', and takes a pointer to the particular reentrancy structure to use. For example, the function fopen takes two arguments, file and mode, and uses the global reentrancy structure. The function _fopen_r takes the arguments, struct_reent, which is a pointer to an instance of the reentrancy structure, file and mode. There are two versions of `struct _reent', a normal one and one for small memory systems, controlled by the _REENT_SMALL definition from the (automatically included) ` <sys/config.h>'. Each function which uses the global reentrancy structure uses the global variable _impure_ptr, which points to a reentrancy structure. This means that you have two ways to achieve reentrancy. Both require that each thread of execution control initialize a unique global variable of type `struct _reent':
1.Use the reentrant versions of the library functions, after initializing a global reentrancy structure for each process. Use the pointer to this structure as the extra argument for all library functions.
2.Ensure that each thread of execution control has a pointer to its own unique reentrancy structure in the global variable _impure_ptr, and call the standard library subroutines.
根据上述说明,Red Hat newlib C Library中的函数提供了可重入的特性,_r函数。而且,可重入性的实现有两种方法,一种是使用_r函数(方法1),一种是使用原函数(方法2)。现在特别想知道第二种方法。希望高手能够说一说方法1和2具体的代码如何实现,告诉代码的下载地址也行。谢谢!
#3
Reentrancy
Reentrancy is a characteristic of library functions which allows multiple processes to use the same address space with assurance that the values stored in those spaces will remain constant between calls. Cygnus's implementation of the library functions ensures that whenever possible, these library functions are reentrant. However, there are some functions that can not be trivially made reentrant. Hooks have been provided to allow you to use these functions in a fully reentrant fashion. These hooks use the structure _reent defined in `reent.h'. A variable defined as `struct _reent' is called a reentrancy structure. All functions which must manipulate global information are available in two versions. The first version has the usual name, and uses a single global instance of the reentrancy structure. The second has a different name, normally formed by prepending `_' and appending `_r', and takes a pointer to the particular reentrancy structure to use. For example, the function fopen takes two arguments, file and mode, and uses the global reentrancy structure. The function _fopen_r takes the arguments, struct_reent, which is a pointer to an instance of the reentrancy structure, file and mode. There are two versions of `struct _reent', a normal one and one for small memory systems, controlled by the _REENT_SMALL definition from the (automatically included) ` <sys/config.h>'. Each function which uses the global reentrancy structure uses the global variable _impure_ptr, which points to a reentrancy structure. This means that you have two ways to achieve reentrancy. Both require that each thread of execution control initialize a unique global variable of type `struct _reent':
1.Use the reentrant versions of the library functions, after initializing a global reentrancy structure for each process. Use the pointer to this structure as the extra argument for all library functions.
2.Ensure that each thread of execution control has a pointer to its own unique reentrancy structure in the global variable _impure_ptr, and call the standard library subroutines.
根据上述说明,Red Hat newlib C Library中的函数提供了可重入的特性,_r函数。而且,可重入性的实现有两种方法,一种是使用_r函数(方法1),一种是使用原函数(方法2)。现在特别想知道第二种方法。希望高手能够说一说方法1和2具体的代码如何实现,告诉代码的下载地址也行。谢谢!
Reentrancy is a characteristic of library functions which allows multiple processes to use the same address space with assurance that the values stored in those spaces will remain constant between calls. Cygnus's implementation of the library functions ensures that whenever possible, these library functions are reentrant. However, there are some functions that can not be trivially made reentrant. Hooks have been provided to allow you to use these functions in a fully reentrant fashion. These hooks use the structure _reent defined in `reent.h'. A variable defined as `struct _reent' is called a reentrancy structure. All functions which must manipulate global information are available in two versions. The first version has the usual name, and uses a single global instance of the reentrancy structure. The second has a different name, normally formed by prepending `_' and appending `_r', and takes a pointer to the particular reentrancy structure to use. For example, the function fopen takes two arguments, file and mode, and uses the global reentrancy structure. The function _fopen_r takes the arguments, struct_reent, which is a pointer to an instance of the reentrancy structure, file and mode. There are two versions of `struct _reent', a normal one and one for small memory systems, controlled by the _REENT_SMALL definition from the (automatically included) ` <sys/config.h>'. Each function which uses the global reentrancy structure uses the global variable _impure_ptr, which points to a reentrancy structure. This means that you have two ways to achieve reentrancy. Both require that each thread of execution control initialize a unique global variable of type `struct _reent':
1.Use the reentrant versions of the library functions, after initializing a global reentrancy structure for each process. Use the pointer to this structure as the extra argument for all library functions.
2.Ensure that each thread of execution control has a pointer to its own unique reentrancy structure in the global variable _impure_ptr, and call the standard library subroutines.
根据上述说明,Red Hat newlib C Library中的函数提供了可重入的特性,_r函数。而且,可重入性的实现有两种方法,一种是使用_r函数(方法1),一种是使用原函数(方法2)。现在特别想知道第二种方法。希望高手能够说一说方法1和2具体的代码如何实现,告诉代码的下载地址也行。谢谢!
#4
Reentrancy
Reentrancy is a characteristic of library functions which allows multiple processes to use the
same address space with assurance that the values stored in those spaces will remain const
ant between calls. Cygnus's implementation of the library functions ensures that whenever
possible, these library functions are reentrant. However, there are some functions that can
not be trivially made reentrant. Hooks have been provided to allow you to use these function
s in a fully reentrant fashion. These hooks use the structure _reent defined in `reent.h'. A
variable defined as `struct _reent' is called a reentrancy structure. All functions which must
manipulate global information are available in two versions. The first version has the usual
name, and uses a single global instance of the reentrancy structure. The second has a
different name, normally formed by prepending `_' and appending `_r', and takes a pointer
to the particular reentrancy structure to use. For example, the function fopen takes two
arguments, file and mode, and uses the global reentrancy structure. The function _fopen_r
takes the arguments, struct_reent, which is a pointer to an instance of the reentrancy
structure, file and mode. There are two versions of `struct _reent', a normal one and one
for small memory systems, controlled by the _REENT_SMALL definition from the
(automatically included) ` <sys/config.h>'. Each function which uses the global reentrancy
structure uses the global variable _impure_ptr, which points to a reentrancy structure. This
means that you have two ways to achieve reentrancy. Both require that each thread of
execution control initialize a unique global variable of type `struct _reent':
1.Use the reentrant versions of the library functions, after initializing a global reentrancy
structure for each process. Use the pointer to this structure as the extra argument for all
library functions.
2.Ensure that each thread of execution control has a pointer to its own unique reentrancy
structure in the global variable _impure_ptr, and call the standard library subroutines.
根据上述说明,Red Hat newlib C Library中的函数提供了可重入的特性,_r函数。而且,可重入性的实现
有两种方法,一种是使用_r函数(方法1),一种是使用原函数(方法2)。现在特别想知道第二种方法。希望高手
能够说一说方法1和2具体的代码如何实现,告诉代码的下载地址也行。谢谢!
Reentrancy is a characteristic of library functions which allows multiple processes to use the
same address space with assurance that the values stored in those spaces will remain const
ant between calls. Cygnus's implementation of the library functions ensures that whenever
possible, these library functions are reentrant. However, there are some functions that can
not be trivially made reentrant. Hooks have been provided to allow you to use these function
s in a fully reentrant fashion. These hooks use the structure _reent defined in `reent.h'. A
variable defined as `struct _reent' is called a reentrancy structure. All functions which must
manipulate global information are available in two versions. The first version has the usual
name, and uses a single global instance of the reentrancy structure. The second has a
different name, normally formed by prepending `_' and appending `_r', and takes a pointer
to the particular reentrancy structure to use. For example, the function fopen takes two
arguments, file and mode, and uses the global reentrancy structure. The function _fopen_r
takes the arguments, struct_reent, which is a pointer to an instance of the reentrancy
structure, file and mode. There are two versions of `struct _reent', a normal one and one
for small memory systems, controlled by the _REENT_SMALL definition from the
(automatically included) ` <sys/config.h>'. Each function which uses the global reentrancy
structure uses the global variable _impure_ptr, which points to a reentrancy structure. This
means that you have two ways to achieve reentrancy. Both require that each thread of
execution control initialize a unique global variable of type `struct _reent':
1.Use the reentrant versions of the library functions, after initializing a global reentrancy
structure for each process. Use the pointer to this structure as the extra argument for all
library functions.
2.Ensure that each thread of execution control has a pointer to its own unique reentrancy
structure in the global variable _impure_ptr, and call the standard library subroutines.
根据上述说明,Red Hat newlib C Library中的函数提供了可重入的特性,_r函数。而且,可重入性的实现
有两种方法,一种是使用_r函数(方法1),一种是使用原函数(方法2)。现在特别想知道第二种方法。希望高手
能够说一说方法1和2具体的代码如何实现,告诉代码的下载地址也行。谢谢!
#5
在unix高级环境编程中有讲述
如果要下载函数的源代码的话
有个netbsd的libc
http://cvsweb.netbsd.org/bsdweb.cgi/src/lib/libc/
glibc也能在其他网站上找到
如果要下载函数的源代码的话
有个netbsd的libc
http://cvsweb.netbsd.org/bsdweb.cgi/src/lib/libc/
glibc也能在其他网站上找到
#6
http://61.185.242.103/~kjqk/dzkjdxxb/dzkj2005/0504pdf/050425.pdf
应该是你要的东西
应该是你要的东西
#7
【摘要】对嵌入式C运行岸- newlib进行了深入研究,阐述了该运行库在多任务坏境下可重入性的实现方
法;介绍了移植newlib到嵌入式系统上需要的桩函数及其实现方法,并重点介绍了与珊相关的四个桩函数.pen,
close, read和write的实现方法,以及动态内存分配器malloc的两种实现方法.
关键词嵌入式系统;C运行库;可重入性;桩函数
中图分类号TP393文献标识码A
Research and Minimum Accomplishment of Newlib
ZHANG Yu-min, LUO Lei
(School of Computer Science andVEST of China Chengdu 610054)
This article has a research on a embedded CLibrary: newlib.
way to accomplish the reentry of newlib
implementation which is, needed by porting
write implementation which relate with I/O,
under multi-task environment. It introduces
It presents the
stub functions
newlib on embedded system, especially open, close, read and
and malloc two implementation
Key words embedded system; newlib; c runtime library;
1公叹10nways.
reentry; stub
1 Newlib简介
Newlib是一个面向嵌入式系统的C运行库.最初是由Solutions收集组装的一个源代码集合,取
名为newlib,现在由Red Hat维护,目前的最新的版本是1.11.01'1.
对于与GNU兼容的嵌入式C运行库,Newlib并不是唯一的选择,但是从成熟度来讲,newlib是最优秀
的.newlib具有独特的体系结构,使得它能够非常好地满足深度嵌入式系统的要求.newlib可移植性强,
具有可重入特性,功能完备等特点,已广泛应用于各种嵌入式系统中.
2可重入性的实现
C运行库的可重入性问题主要是库中的全局变量在多任务环境下的可重入性问题,Newlib解决这个问
题的方法是,定义一个struct一ent类型的结构,将运行库所有会引起可重入性问题的全局变量都放到该结
构中.而这些全局变量则被重新定义为若干个宏,以errn.为例,名为"ermo"的宏引用指向struct reent结
构类型的一个全局指针,这个指针叫做-impure_ptra
收稿日期:2003一12一05
基金项目:四川省重点科技攻关项目
作者简介:张宇昊(1978一),男,硕士,主要从事嵌入式系统软件方面的研究;罗曹(1967一),女,硕士,教授,主要从事嵌入式系统软件方
面的研究.
电子科技大学学报第34卷
对于用户,这一切都被ermo宏隐藏了,需要检查错误时,用户只需要像其他ANSI C环境下所做的一样,
检查ermo"变量"就可以了.实际上,用户对ermo宏的访问是返回_impure_ptr->erm.的值,而不是一个全
局变量的值.
Newlib定义了一ent结构类型的一个静态实例,并在系统初始化时用全局指针-unpure_ptr指向它.如果
系统中只有一个任务,那么系统将正常运行,不需要做额外的工作;如果希望newlib运行在多任务环境下,
必须完成下面的两个步骤:
1)每个任务提供一个一ent结构的实例并初始化;
2)任务上下文切换的时刻重新设置_impure_ptr指针,使它指向即将投入运行任务的一ent结构实例.
这样就可以保障大多数库函数(尤其是stdio库函数)的可重入性.如果需要可重入的malloc,还必须设法
实现一malloc lockOjV一 malloc unlockO f数,它们在内存分配过程中保障堆((heap)在多任务环境下的安全.
3 Newlib的移植
Newlib的所有库函数都建立在20个桩函数的基础上[2],这20个桩函数完成一些newlib无法实现的功能:
1)级I/O和文件系统访问(open, close, read, write, lseek, stat, fstat, fcntl, link, unlink, rename);
2)扩大内存堆的需求(sbrk);
3)获得当前系统的日期和时间((gettimeofday, times);
4)各种类型的任务管理函数(execve, fork, getpid, kill, wait,exit);
这20个桩函数在语义,语法上与POSIx标准下对应的20个同名系统调用是完全兼容的[3].成功移植newlib
的关键是在目标系统环境下,找到能够与这些桩函数衔接的功能函数并实现这些桩函数.
Newlib为每个桩函数提供了可重入的和不可重入的两种版本.两种版本的区别在于,如果不可重入版
桩函数的名字是xxx,则对应的可重入版桩函数的名字是'xx r,如close和一loser, open和一pen -r,等等.
此外,可重入的桩函数在参数表中含有一个一ent结构指针,这个指针使得系统的实现者能在库和目标操
作环境之间传送上下文相关的信息,尤其是发生错误时,能够便捷的传送ermo的值到适当的任务中.
所谓最小实现是指,假定将要移植的目标系统中没有文件系统,也没有符合POSIX标准的任务管理机
制和应用编程接口((Application Programming Interface, API),仅仅实现newlib的一个最小移植.在newlib的
移植过程中全功能实现的桩函数只有open, close, read, write和sbrk五个,其他桩函数仅仅实现一个返回
错误的空函数.
任务管理的execve, fork, getpid, kill, wait和exit六个桩函数,仅仅实现一个返回一1的空函数,返回
之前将ermo设置为ENOTSUP,表示系统不支持该函数.
与文件相关的link和unlink桩函数也仅仅实现一个返回一1的空函数,将errn.设置为EMLINK表示连接过
多;lseek函数则不需要返回任何错误,直接返回0,表示操作成功.
fstat和stat桩函数在newlib中主要用于判断流的类型(常规文件,字符设备,目录),将其实现为不论输
入参数如何,都返回字符设备类型的空函数.
times桩函数返回当前进程中的各种时间信息,如果目标系统中的任务不能提供类似的时间信息,仅仅
实现一个返回一I的空函数,将ermo设置为ENOTSUP.
由于newlib认为在目标系统中fcntl, rename和gettimeofday三个桩函数缺省是不提供的,所以也不提供
这三个桩函数的实现.
4 1/O桩函数的实现
Newlib在使用open, close, read和write桩函数时严格遵守POSIX标准,为了使实现的桩函数完全符合
POSIX,就必须在内部机制上实现设备名表,文件描述符表和驱动地址表3个表的相关操作.
4.1三个表的结构,作用及相关操作
1)设备名表记录系统中所有设备的名字及其设备号.系统初始化时必须将所有的设备名及其设备号填
入表中备查.
第4期张宇曼等:Newlib的研究与最小实现
对于设备名表应该实现以下两个操作:
(1)设备名/设备号注册函数NameRegister;
(2)从设备名到设备号的转换函数NameLookup;
2)文件描述符表记录系统中当前打开的设备的设备号.每个表项代表一个处于打开状态的设备.每个
表项的索引值就是需要返回给用户的文件描述符.
对文件描述符表需要实现以下3个操作:
(1)文件描述符分配函数FdAllocate;
(2)文件描述符释放函数FdFree;
(3)从文件描述符到设备号的转换函数Fd2DevCode;
3)驱动地址表记录系统中每个驱动程序的入口地址.每个表项代表一个驱动程序,对每个驱动程序都
应该实现五个具有统一接口的操组函数:init, open, close, read, write.每个表项在表中的索引值就是该
设备的设备号.需要注意是每个驱动程序都必须提供init操作.
对驱动地址表需要实现以下操作:
初始化驱动表中的所有驱动函数InitAllDrivers;
该操作对表中的每一个驱动程序调用init操作,完成表中所有驱动程序的初始化操作.
在系统初始化的时间,应该调用InitAllDriverso操作,完成系统中所有驱动程序的初始化操作.在每个
驱动程序的init操作中,应该调用NameRegistero操作,完成驱动程序对应的设备注册,以COMI驱动程序
的com l_ inito操作为例,它的实现如下:
void coml_ init(int devCode)
{
/*首先注册设备名和设备号到设备名表中*/
NameRegister("COMl", devCode);
/*然后完成其他的设备初始化操作*/
}
只要所有的设备驱动程序都遵守这个约定,在系统初始化完成之后,系统中所有的驱动程序就得到了
初始化,并且系统中所有的设备都注册到了设备名表中.后续的v0桩函数的实现就非常容易了.
设备名表,文件描述符表和驱动地址表3个表的结构及相关操作如图I所示.
文件描述符表设备名表驱动地址表
fd设备号设备名设备号设备号init open close read write
图1文件描述符表,设备名表和驱动地址表
4.2 open桩函数的实现
open桩函数的实现流程如下:
1)用NameLookupo操作在设备名表中搜索匹配的设备名,并获得对应的设备号;
2)用FdAllocateo操作从文件描述符表中分配一个空的表项,填入设备号,并获得对应的索引号即fd;
3)通过设备号直接调用驱动地址表中对应驱动程序的open操作;
4)返回fdo
4.3 read, write和close桩函数的实现
read和write桩函数的实现方法完全相同,流程如下:
电子科技大学学报第34卷
1)调用Fd2DevCode()操作获得与输入参数fd对应的设备号devCode;
2)通过设备号直接调用驱动地址表中对应驱动的read或write操作;
3)返回实际交换的数据量.
close桩函数的实现与read, write几乎完全相同,唯一不同之处在于最后调用FdFree()操作,释放fd而不
是返回实际交换的数据量,流程如下:
1)调用Fd2DevCode()操作获得与输入参数fd对应的设备号devCode;
2)通过设备号直接调用驱动地址表中对应驱动的close操作;
3)调用FdFree()操作释放fdo
至此,与设备I/O相关的四个桩函数open, close, read和write的实现就全部完成了.
本文没有介绍驱动程序的实现方法,并不是驱动程序不重要,恰恰相反,驱动程序中必须完成可靠高
效的设备操作,保证驱动程序的各项操作在语义上与上面4个桩函数完全一致,并且实质性的操作都在驱
动程序中完成.因此,在驱动程序的实现上必须仔细斟酌.由于篇幅的原因,不再赘述.
5关于malloc
大多数嵌入式操作系统都实现了自己的动态内存分配机制,并且提供了多任务环境下对内存分配机制
的保护措施,如果移植newlib到这样的系统时,可以放弃newlib自带的malloc函数.尽管newlib自带的malloc
非常高效,但是几乎所有的用户都习惯使用malloc来作为动态内存分配器.在这种情况下,最好对系统自
带的动态内存分配API进行封装,使它不论在风格,外观上,还是在语义上都与malloc完全相同,这对于提
高应用程序的可移植性大有好处.
对于那些没有实现动态内存分配机制的嵌入式系统环境来说,newlib的malloc是一个非常好的选择,
只需实现sbrk桩函数,malloc就可以非常好地工作起来.与之同名的POSIX系统调用的作用是从系统中获得
一块内存,每当malloc需要更多的内存时,都会调用sbrk函数.
在单任务环境下,只需实现sbrk桩函数,malloc就可以正常运行;但在多任务环境下,还需实现
一一malloc locko和一malloc unlock()函数,newlib用这两个函数来保护内存堆免受冲击.用户可利用目标环
境中的互斥信号量机制来实现这两个函数,在一malloc lock()函数中申请互斥信号量,而在一malloc unlock()
函数中释放同一个互斥信号量.
6结束语
本文中详细介绍了newlib作为一个面向嵌入式系统的C运行库所具备的各种特点,重点介绍了ewlib在
多任务环境下的可重入性和实现方法,以及ewlib的移植方法两方面的内容.尤其是针对newlib的移植,详
细介绍了每一个桩函数的实现方法,并给出了许多的代码实例.但是并不是说对newlib只能像本文中所描
述的那样进行移植,本文给出的仅仅是移植newlib到特定系统之上的一个最小实现.Newlib的体系结及其
清晰简洁的实现方法决定了newlib具有非常的灵活性,能够适应各式各样的系统和目标环境.随着研究工
作的深入,newlib在目标系统中将会发挥更大的作用.
法;介绍了移植newlib到嵌入式系统上需要的桩函数及其实现方法,并重点介绍了与珊相关的四个桩函数.pen,
close, read和write的实现方法,以及动态内存分配器malloc的两种实现方法.
关键词嵌入式系统;C运行库;可重入性;桩函数
中图分类号TP393文献标识码A
Research and Minimum Accomplishment of Newlib
ZHANG Yu-min, LUO Lei
(School of Computer Science andVEST of China Chengdu 610054)
This article has a research on a embedded CLibrary: newlib.
way to accomplish the reentry of newlib
implementation which is, needed by porting
write implementation which relate with I/O,
under multi-task environment. It introduces
It presents the
stub functions
newlib on embedded system, especially open, close, read and
and malloc two implementation
Key words embedded system; newlib; c runtime library;
1公叹10nways.
reentry; stub
1 Newlib简介
Newlib是一个面向嵌入式系统的C运行库.最初是由Solutions收集组装的一个源代码集合,取
名为newlib,现在由Red Hat维护,目前的最新的版本是1.11.01'1.
对于与GNU兼容的嵌入式C运行库,Newlib并不是唯一的选择,但是从成熟度来讲,newlib是最优秀
的.newlib具有独特的体系结构,使得它能够非常好地满足深度嵌入式系统的要求.newlib可移植性强,
具有可重入特性,功能完备等特点,已广泛应用于各种嵌入式系统中.
2可重入性的实现
C运行库的可重入性问题主要是库中的全局变量在多任务环境下的可重入性问题,Newlib解决这个问
题的方法是,定义一个struct一ent类型的结构,将运行库所有会引起可重入性问题的全局变量都放到该结
构中.而这些全局变量则被重新定义为若干个宏,以errn.为例,名为"ermo"的宏引用指向struct reent结
构类型的一个全局指针,这个指针叫做-impure_ptra
收稿日期:2003一12一05
基金项目:四川省重点科技攻关项目
作者简介:张宇昊(1978一),男,硕士,主要从事嵌入式系统软件方面的研究;罗曹(1967一),女,硕士,教授,主要从事嵌入式系统软件方
面的研究.
电子科技大学学报第34卷
对于用户,这一切都被ermo宏隐藏了,需要检查错误时,用户只需要像其他ANSI C环境下所做的一样,
检查ermo"变量"就可以了.实际上,用户对ermo宏的访问是返回_impure_ptr->erm.的值,而不是一个全
局变量的值.
Newlib定义了一ent结构类型的一个静态实例,并在系统初始化时用全局指针-unpure_ptr指向它.如果
系统中只有一个任务,那么系统将正常运行,不需要做额外的工作;如果希望newlib运行在多任务环境下,
必须完成下面的两个步骤:
1)每个任务提供一个一ent结构的实例并初始化;
2)任务上下文切换的时刻重新设置_impure_ptr指针,使它指向即将投入运行任务的一ent结构实例.
这样就可以保障大多数库函数(尤其是stdio库函数)的可重入性.如果需要可重入的malloc,还必须设法
实现一malloc lockOjV一 malloc unlockO f数,它们在内存分配过程中保障堆((heap)在多任务环境下的安全.
3 Newlib的移植
Newlib的所有库函数都建立在20个桩函数的基础上[2],这20个桩函数完成一些newlib无法实现的功能:
1)级I/O和文件系统访问(open, close, read, write, lseek, stat, fstat, fcntl, link, unlink, rename);
2)扩大内存堆的需求(sbrk);
3)获得当前系统的日期和时间((gettimeofday, times);
4)各种类型的任务管理函数(execve, fork, getpid, kill, wait,exit);
这20个桩函数在语义,语法上与POSIx标准下对应的20个同名系统调用是完全兼容的[3].成功移植newlib
的关键是在目标系统环境下,找到能够与这些桩函数衔接的功能函数并实现这些桩函数.
Newlib为每个桩函数提供了可重入的和不可重入的两种版本.两种版本的区别在于,如果不可重入版
桩函数的名字是xxx,则对应的可重入版桩函数的名字是'xx r,如close和一loser, open和一pen -r,等等.
此外,可重入的桩函数在参数表中含有一个一ent结构指针,这个指针使得系统的实现者能在库和目标操
作环境之间传送上下文相关的信息,尤其是发生错误时,能够便捷的传送ermo的值到适当的任务中.
所谓最小实现是指,假定将要移植的目标系统中没有文件系统,也没有符合POSIX标准的任务管理机
制和应用编程接口((Application Programming Interface, API),仅仅实现newlib的一个最小移植.在newlib的
移植过程中全功能实现的桩函数只有open, close, read, write和sbrk五个,其他桩函数仅仅实现一个返回
错误的空函数.
任务管理的execve, fork, getpid, kill, wait和exit六个桩函数,仅仅实现一个返回一1的空函数,返回
之前将ermo设置为ENOTSUP,表示系统不支持该函数.
与文件相关的link和unlink桩函数也仅仅实现一个返回一1的空函数,将errn.设置为EMLINK表示连接过
多;lseek函数则不需要返回任何错误,直接返回0,表示操作成功.
fstat和stat桩函数在newlib中主要用于判断流的类型(常规文件,字符设备,目录),将其实现为不论输
入参数如何,都返回字符设备类型的空函数.
times桩函数返回当前进程中的各种时间信息,如果目标系统中的任务不能提供类似的时间信息,仅仅
实现一个返回一I的空函数,将ermo设置为ENOTSUP.
由于newlib认为在目标系统中fcntl, rename和gettimeofday三个桩函数缺省是不提供的,所以也不提供
这三个桩函数的实现.
4 1/O桩函数的实现
Newlib在使用open, close, read和write桩函数时严格遵守POSIX标准,为了使实现的桩函数完全符合
POSIX,就必须在内部机制上实现设备名表,文件描述符表和驱动地址表3个表的相关操作.
4.1三个表的结构,作用及相关操作
1)设备名表记录系统中所有设备的名字及其设备号.系统初始化时必须将所有的设备名及其设备号填
入表中备查.
第4期张宇曼等:Newlib的研究与最小实现
对于设备名表应该实现以下两个操作:
(1)设备名/设备号注册函数NameRegister;
(2)从设备名到设备号的转换函数NameLookup;
2)文件描述符表记录系统中当前打开的设备的设备号.每个表项代表一个处于打开状态的设备.每个
表项的索引值就是需要返回给用户的文件描述符.
对文件描述符表需要实现以下3个操作:
(1)文件描述符分配函数FdAllocate;
(2)文件描述符释放函数FdFree;
(3)从文件描述符到设备号的转换函数Fd2DevCode;
3)驱动地址表记录系统中每个驱动程序的入口地址.每个表项代表一个驱动程序,对每个驱动程序都
应该实现五个具有统一接口的操组函数:init, open, close, read, write.每个表项在表中的索引值就是该
设备的设备号.需要注意是每个驱动程序都必须提供init操作.
对驱动地址表需要实现以下操作:
初始化驱动表中的所有驱动函数InitAllDrivers;
该操作对表中的每一个驱动程序调用init操作,完成表中所有驱动程序的初始化操作.
在系统初始化的时间,应该调用InitAllDriverso操作,完成系统中所有驱动程序的初始化操作.在每个
驱动程序的init操作中,应该调用NameRegistero操作,完成驱动程序对应的设备注册,以COMI驱动程序
的com l_ inito操作为例,它的实现如下:
void coml_ init(int devCode)
{
/*首先注册设备名和设备号到设备名表中*/
NameRegister("COMl", devCode);
/*然后完成其他的设备初始化操作*/
}
只要所有的设备驱动程序都遵守这个约定,在系统初始化完成之后,系统中所有的驱动程序就得到了
初始化,并且系统中所有的设备都注册到了设备名表中.后续的v0桩函数的实现就非常容易了.
设备名表,文件描述符表和驱动地址表3个表的结构及相关操作如图I所示.
文件描述符表设备名表驱动地址表
fd设备号设备名设备号设备号init open close read write
图1文件描述符表,设备名表和驱动地址表
4.2 open桩函数的实现
open桩函数的实现流程如下:
1)用NameLookupo操作在设备名表中搜索匹配的设备名,并获得对应的设备号;
2)用FdAllocateo操作从文件描述符表中分配一个空的表项,填入设备号,并获得对应的索引号即fd;
3)通过设备号直接调用驱动地址表中对应驱动程序的open操作;
4)返回fdo
4.3 read, write和close桩函数的实现
read和write桩函数的实现方法完全相同,流程如下:
电子科技大学学报第34卷
1)调用Fd2DevCode()操作获得与输入参数fd对应的设备号devCode;
2)通过设备号直接调用驱动地址表中对应驱动的read或write操作;
3)返回实际交换的数据量.
close桩函数的实现与read, write几乎完全相同,唯一不同之处在于最后调用FdFree()操作,释放fd而不
是返回实际交换的数据量,流程如下:
1)调用Fd2DevCode()操作获得与输入参数fd对应的设备号devCode;
2)通过设备号直接调用驱动地址表中对应驱动的close操作;
3)调用FdFree()操作释放fdo
至此,与设备I/O相关的四个桩函数open, close, read和write的实现就全部完成了.
本文没有介绍驱动程序的实现方法,并不是驱动程序不重要,恰恰相反,驱动程序中必须完成可靠高
效的设备操作,保证驱动程序的各项操作在语义上与上面4个桩函数完全一致,并且实质性的操作都在驱
动程序中完成.因此,在驱动程序的实现上必须仔细斟酌.由于篇幅的原因,不再赘述.
5关于malloc
大多数嵌入式操作系统都实现了自己的动态内存分配机制,并且提供了多任务环境下对内存分配机制
的保护措施,如果移植newlib到这样的系统时,可以放弃newlib自带的malloc函数.尽管newlib自带的malloc
非常高效,但是几乎所有的用户都习惯使用malloc来作为动态内存分配器.在这种情况下,最好对系统自
带的动态内存分配API进行封装,使它不论在风格,外观上,还是在语义上都与malloc完全相同,这对于提
高应用程序的可移植性大有好处.
对于那些没有实现动态内存分配机制的嵌入式系统环境来说,newlib的malloc是一个非常好的选择,
只需实现sbrk桩函数,malloc就可以非常好地工作起来.与之同名的POSIX系统调用的作用是从系统中获得
一块内存,每当malloc需要更多的内存时,都会调用sbrk函数.
在单任务环境下,只需实现sbrk桩函数,malloc就可以正常运行;但在多任务环境下,还需实现
一一malloc locko和一malloc unlock()函数,newlib用这两个函数来保护内存堆免受冲击.用户可利用目标环
境中的互斥信号量机制来实现这两个函数,在一malloc lock()函数中申请互斥信号量,而在一malloc unlock()
函数中释放同一个互斥信号量.
6结束语
本文中详细介绍了newlib作为一个面向嵌入式系统的C运行库所具备的各种特点,重点介绍了ewlib在
多任务环境下的可重入性和实现方法,以及ewlib的移植方法两方面的内容.尤其是针对newlib的移植,详
细介绍了每一个桩函数的实现方法,并给出了许多的代码实例.但是并不是说对newlib只能像本文中所描
述的那样进行移植,本文给出的仅仅是移植newlib到特定系统之上的一个最小实现.Newlib的体系结及其
清晰简洁的实现方法决定了newlib具有非常的灵活性,能够适应各式各样的系统和目标环境.随着研究工
作的深入,newlib在目标系统中将会发挥更大的作用.