由open()的O_DIRECT参数引发的公共标准的问题

来源:互联网 发布:淘宝卖家花呗支付 编辑:程序博客网 时间:2024/05/18 11:46

最近比较忙,RHEL-7.1要发布所有同仁们都在紧锣密鼓的为7.1做最后的工作。关于文件系统的大话题只好等到这之后再找时间继续写了,不过今天发现了一个小问题可以简单的和大家在这里分享一下,问题比较浅显请大牛自动略过。本来我想给这篇文章起名叫“公共标准对C语言程序的影响”,不过转念一想这个题目太大了,我也说不过来,所以我就把名字改成了对open()的O_DIRECT参数引发的问题的讨论,至于更多的公共标准的问题就不是本文的范围了;)


问题的起因是这样的,我有一个bug的reproducer,原作者是谁我早已经忘了,我只是想尝试一下它所描述的bug还能否复现,但是测试后发现结果是失败。嗯??难道出现了传说中的regression fail? 为了确认只好阅读这个别人写的code,以及它所对应的bug的描述(读别人的代码真是遭罪,特别是写的不怎样的。。。),发现错误的现象并不复合bug的描述。由于code有很多很多行,还有多线程,我就不把代码都列出来了,我只把和问题相关的代码重新组织如下:

#define _XOPEN_SOURCE  600#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#define READ_SIZE 512#define BUF_SIZE 1024 * 1024#ifndef O_DIRECT#define O_DIRECT 00040000#endifint main(){    .....    .....    fd = open(filename, O_RDONLY | O_DIRECT);    ....    ....    return 0;}

这段代码在x86_64上运行良好,但是到ppc64上运行时open就会报错,说filename不是一个目录。


莫名其妙的错误输出,我创建的filename确实是一个文件,而且我也没告诉open这个filename需要是个目录啊? 而且在x86_64上运行的好好的,怎么跑到ppc64上就出莫名其妙的错误了?

根据经验以及查看man 2 open,知道这种要求目标文件必须是个目录的情况跟open的O_DIRECTORY参数有关,通过调试发现在ppc64上O_DIRECTORY的值就是0x00040000,而上面的代码使用了

#define O_DIRECT 00040000

的定义。这样就导致程序误认为我在使用O_DIRECTORY参数。但是在x86_64上O_DIRECTORY的数值对应0x0100000,而O_DIRECT对应上面定义的0x00040000。

于是问题来了,为什么程序没有找到O_DIRECT的定义而使用了手动的定义数值呢? 我明明都include了和open相关的所有头文件了呀!仔细翻看man 2 open的文档发现下面几句话:

       The O_DIRECT, O_NOATIME, O_PATH, and O_TMPFILE flags are Linux-specific.  One must define _GNU_SOURCE to obtain their definitions.       The O_CLOEXEC, O_DIRECTORY, and O_NOFOLLOW flags are not specified in POSIX.1-2001, but are specified in POSIX.1-2008.  Since glibc 2.12, one can obtain their definitions by defining either _POSIX_C_SOURCE with a value greater       than or equal to 200809L or _XOPEN_SOURCE with a value greater than or equal to 700.  In glibc 2.11 and earlier, one obtains the definitions by defining _GNU_SOURCE.       As noted in feature_test_macros(7), feature test macros such as _POSIX_C_SOURCE, _XOPEN_SOURCE, and _GNU_SOURCE must be defined before including any header files.

第一句就说明了问题的原因,O_DIRECT是linux特有的标记,必须通过#define _GNU_SOURCE来指明使用它的定义。意思就是有很多标准规定这不同的open参数,而O_DIRECT是linux特有的,要想使用的必须启用_GNU_SOURCE这个宏来让程序只要你要使用带有O_DIRECT的标准定义。


于是将

#define _XOPEN_SOURCE  600
改成

#define _GNU_SOURCE
并去掉ifndef那几行,重新编译执行就通过了。而通过查看_XOPEN_SOURCE 600的标准发现里面确实没有O_DIRECT的定义。


通过man 2 open的文档我们还可以看出还有很多参数都是复合不同标准的,甚至是同一个标准的不同版本的。所以在写程序或者移植程序的时候最好要确认你当前的标准环境和程序原来的试用标准。


那么最开始的那个_XOPEN_SOURCE 600是什么标准呢? 最后给出几个和_XOPEN_SOURCE相关的标准说明,更多的标准请在使用中具体查阅。我觉得标准不是用来背诵的,但是在实际生产使用中至少要清楚的认识自己要准从什么标准做事,查明白哪些东西试用于你所用的标准,哪些不适用。

XOPEN_SOURCE 500 - X/Open 5, incorporating POSIX 1995

XOPEN_SOURCE 600 - X/Open 6, incorporating POSIX 2004

XOPEN_SOURCE 700 - X/Open 7, incorporating POSIX 2008


0 0
原创粉丝点击