如何使用Newlib实现write()、_write()或_write_r()函数?

10

我正在尝试在ARM GCC工具链环境下为STM32F411RET微控制器重新定位printf()函数,该环境使用Newlib作为标准C库。

当我搜索如何重新定位printf()时,许多人说我需要实现_write()_write_r()。似乎两者都能工作。

但是我仍然有一些问题:

  1. 当我查看Newlib文档时,它说我可以实现write()以输出文件,但这似乎不起作用。看起来我们可以实现_write(),但是文档中从未提到过这个函数。 write()发生了什么?下划线有什么不同吗?

  2. 在什么情况下,_write_r()优于_wirte()?我不理解C语言中的可重入性概念。有什么例子吗?

感谢阅读。


2
由于标准C没有定义“write”函数,符合规范的程序允许定义自己的函数(或变量)命名为“write”,而不会改变“printf”的行为。这意味着“printf”不能使用名为“write”的函数,它必须使用名称与符合规范的程序不允许使用的函数相同,比如说“_write”。 - Ross Ridge
3
write()函数和open()函数是标准的POSIX类型函数,可在此之间进行配合使用。特殊文件号码0、1、2对应stdin、stdout、stderr(通常如此)。对于newlib中的这些特殊文件,您需要编写实现,并且如果文件号码>2,则必须以某种方式进行索引。也就是说,open()函数将指定要写入的目标文件并返回一个文件号码>2。用户代码不应依赖这些值。但是,实现者需要定义一些内容。 - artless noise
3
这不是对你问题的直接回答,但是这个printf.c实现非常容易移植到微控制器。我已经使用它多次,并且从未遇到任何问题,除了一个注意事项:某些编译器会将printf优化为puts,并在此过程中破坏一切。在这种情况下,您可以实现puts或将printf重命名为其他名称。 - Brian McFarland
2
Pinetwig在下面给出了一个很好的回答。我想要强调的一个重要点是:"open()"、"write()"等函数通常只是对底层系统调用的简单封装。与"printf()"不同(它是完全实现在C库中的复杂代码),"write()"通常只是一个最小的C接口到操作系统。我不确定你是否想要去改变它... - paulsm4
1
PS:这里有一个关于“可重入”的好讨论。需要注意的是,“可重入”与“线程安全”密切相关,但它们并不相同。例如,一个子例程可能会调用它自己(或调用一个嵌套函数,该函数反过来再次调用它),如果它维护任何“静态”数据,则会发生糟糕的事情。一个很好的例子是"strtok()",它既不是线程安全的也不是可重入的。 - paulsm4
1个回答

3

对于第一个问题,这是因为Newlib希望通过在名称前添加下划线来避免名称冲突(“命名空间清洁版本”)。另请参见C编译器为什么在外部名称前加下划线?

对于第二个问题,请参见C中的可重入性和可重入函数?。如果您的板上有多个线程可以调用newlib函数,则需要使用可重入系统调用的版本。如果中断处理程序可以调用newlib函数,则还需要使用可重入系统调用。但是,在单线程应用程序中使用可重入系统调用也完全没有问题。

例如,如果您在多线程应用程序中使用非可重入系统调用,则只有一个全局errno标志。如果两个或更多线程中发生错误,则全局errno值可能会被覆盖。在可重入系统调用中,每个线程都有自己的errno标志(在_reent-struct中 - 请参见此处以获取实现)。因此,每个线程都可以检查和处理自己的错误。
注意:您必须重新编译newlib以选择要使用的系统调用模型。请参见http://www.embecosm.com/appnotes/ean9/ean9-howto-newlib-1.0.html#sec_configure_host

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接