关于文件seek有一系列函数,在stream上操作的fseek, fseeko,在file descriptor上操作的lseek, lseek64等。下面是几个函数原型:
int fseek(FILE *stream, long offset, int whence); int fseeko(FILE *stream, off_t offset, int whence); off_t lseek(int fd, off_t offset, int whence); off64_t lseek64(int fd, off64_t offset, int whence);
对于2G以上的大文件,如果想直接seek到2G之后的位置,offset就得大于2G,这就要求offset必须是64位的类型。
从上面offset的类型来看,有三种:long,off_t,off64_t
off_t和off64_t是typedef出来的新类型,明显off64_t肯定是64位的,就是说lseek64是肯定支持大文件的。
对于long和off_t,fseeko的man手册有下面一段话:
#define _FILE_OFFSET_BITS 64
will turn off_t into a 64-bit type.
对于off_t,只要加一个宏编译参数,就可以让它变成64位。
对于long,其长度取决于系统和编译器,32位平台下,long是32位,64位平台下,long可能是32位,也可能是64位,这个取决于编译器。
总结一下就是,fseek在32位平台下无法支持大文件,64位平台下可能支持大文件(取决于编译器);fseeko和lseek可以通过宏参数设置,使其支持大文件;lseek64从函数名就可以看出来,它使支持大文件的。
上面都是针对Linux,现在我们来说Android。
Android上,fseek是无法支持大文件的,fseeko和lseek呢,设置了宏 _FILE_OFFSET_BITS之后,还是不行,google之后发现原来Android不支持啊。https://code.google.com/p/android/issues/detail?id=64613
鉴于这个网页不太方便打开,这里把内容贴出来:
This is arguably a dupe of Issue #55866 (NDK: Missing large file support), but that bug is still in NotEnoughInformation, so lets provide more information...
The NDK currently declares e.g.
extern off_t lseek(int, off_t, int);
extern off64_t lseek64(int, off64_t, int);
While this provides "large file support", it does not go as far as glibc does. On "proper" Linux, it's more complicated; if _FILE_OFFSET_BITS is set to 64, then the "normal" file I/O functions are 64-bit -- lseek(2) would take a 64-bit off_t, not a 32-bit off_t.
http://users.suse.com/~aj/linux_lfs.html
> In a nutshell for using LFS you can choose either of the following:
> * Compile your programs with "gcc -D_FILE_OFFSET_BITS=64". This forces all
> file access calls to use the 64 bit variants.
(See also e.g. glibc <features.h> and <unistd.h> which has lots of fun/complicated #if-fu.)
Many open-source libraries will use autoconf's AC_SYS_LARGEFILE macro (or variants thereof) in order to check for and enable 64-bit off_t on 32-bit platforms, effectively resulting in:
#define lseek lseek64
http://www.gnu.org/software/autoconf/manual/autoconf-2.69/html_node/System-Services.html
The problem with the NDK is that it doesn't support any of these patterns. Consequently, software built to support _FILE_OFFSET_BITS=64 behavior won't be built with this support enabled (because the NDK doesn't support it), resulting in use of the 32-bit file APIs instead of the 64-bit APIs.
https://code.google.com/p/android/issues/detail?id=55866#c6
> I have the same problem with my ffmpeg build. Can't open video files larger than 2GB.
What would be useful is for the NDK to implement/conform to the current glibc macros/patterns so that software with large file support can easily make use of it on Android.
Jan 9, 2014 Project Member #1 e...@google.com
we don't actually have the full set of *64 functions yet either, but we're working on it.
Summary: implement _FILE_OFFSET_BITS (was: NDK: Missing "GNU compatible" large file support.)
Owner: e...@google.com
Cc: e...@google.com cfer...@google.com
Nov 6, 2014 #2 wolfgang...@gmail.com
This issue hit me recently in migrating some code that was safe using autoconf (AC_SYS_LARGEFILE) and I tried (paranoid) adding in `#define _FILE_OFFSET_BITS 64` all over the place.
Finally realized with a very small test program that Android does not respect `#define _FILE_OFFSET_BITS 64` or the autoconf macro as expected.
This led to a maddening bug that was hard to track down as core expectations were not correct.
Is this non-conformance documented anywhere?
好了,就只有lseek64了,好在Android支持这个。
但是怎么用呢,之前的代码全是用的fopen, fread, fseek, ftell系列的函数,好在有fileno这个函数。
int fileno(FILE *stream);
这个函数把stream转成file descriptor。
下面封装出自己的支持64位的fseek函数,注意fseek和lseek64的返回值。
int fseek_64(FILE *stream, int64_t offset, int origin) { int fd = fileno(stream); if (lseek64(fd, offset, origin) == -1) return errno; return 0; }
就这样,it works。
但是程序跑了一段时间后,发现有些不正常,一路追踪下来,bug锁定在了我们自己写的fseek_64函数。
具体表现是,用fread读了一些数据,然后fseek_64,接下来再fread,发现读到的数据不是我们期望的,在我们想要的数据前面,总是有一些脏数据,于是猜想是不是fread有缓存,脏数据就是缓存中未读完的数据呢?为什么fseek就没有问题,而我们的调用了lseek64的fseek_64却有问题?fseek和lseek之间有什么联系,又有什么区别?于是google之,证实了我的猜想。
首先,fread/fwrite系列函数在实现时确实是使用了缓存的。而lseek是系统调用,fseek是标准c库,它的底层实现也是调用了lseek,但是同时对缓存做了相应处理。比如,假设缓存中有10字节的数据,这时要往后跳4字节,这是fseek不需要调用lseek,只要把缓存的指针往后挪4个字节就ok了;如果要往后跳40字节呢,fseek就调用lseek,跳到指定位置,然后把缓存清空。
我们的fseek_64实现里面,只调用lseek64跳到了指定的地方,而没有去操作缓存,所以导致了上面的bug。
这里又要用到sefbuf函数来操作缓存。于是修改fseek_64函数如下:
int fseek_64(FILE *stream, int64_t offset, int origin) { setbuf(stream, NULL); //清空buffer int fd = fileno(stream); if (lseek64(fd, offset, origin) == -1) return errno; return 0; }
这样改过之后,上面那个bug就没了。
最后,还有个问题,在做项目的过程中发现,对于已经到达EOF的stream,使用lseek是不能让stream再次可读的。不知道fseek函数有没有处理这个,如果有处理的话(目前感觉这种可能性很大),我们的fseek_64函数应该继续改进,使用rewind甚至重新打开文件,来使其再次可读。如果是这样,代码应改成这样:
int fseek_64(FILE *stream, int64_t offset, int origin) { if (feof(stream)) { rewind(stream); } else { setbuf(stream, NULL); //清空fread的缓存 } int fd = fileno(stream); if (lseek64(fd, offset, origin) == -1) { return errno; } return 0; }
后面验证过了再来更新验证结果。
update:
验证过了,fseek可以使已经EOF的stream重新可读,rewind也可以。所以,上面的代码是可以工作的。
相关推荐
Android NDK,全称为Native Development Kit,是Google提供的一款用于Android平台的C和C++开发工具集。这个压缩包“android-ndk-r25b-windows.zip”包含了NDK的第25个版本,专为Windows操作系统设计。NDK的主要功能...
Android NDK环境配置是Android应用开发中的一个重要环节,它允许开发者使用C或C++编写高性能的原生代码,这些代码可以被编译成动态库并与Java应用一同打包成APK。NDK集成了交叉编译器,使得开发者能够针对不同的CPU...
Android NDK,全称为Native Development Kit,是Google提供的一款用于Android平台的C/C++开发工具集。这个"android-ndk-r23b-windows.zip"压缩包包含了NDK的第23个版本,专为Windows操作系统设计。NDK是Android应用...
Android NDK,全称为Native Development Kit,是Google提供的一款用于Android平台的C和C++库开发工具集。这个“android-ndk-r25b-linux.zip”文件是NDK的一个特定版本,即r25b,专为Linux操作系统设计。在Android...
3. **创建Android.mk文件**:`Android.mk`是Android NDK的构建脚本,它告诉NDK如何编译源代码。你需要创建一个名为`Android.mk`的文件,放在`libiconv`源代码目录下,内容如下: ``` LOCAL_PATH := $(call my-dir...
Android NDK,全称为Native Development Kit,是Google提供的一款用于Android平台的C和C++库开发工具集。这个“android-ndk-r23b-linux.zip”文件是NDK的一个特定版本,即r23b,专为Linux操作系统设计。在Android...
在Ubuntu系统上搭建Android NDK编译环境是Android原生代码开发的重要步骤,适用于那些需要进行C/C++底层开发或优化的应用程序。Android NDK(Native Development Kit)是一套工具,允许开发者使用C/C++编写部分应用...
### 非常强大的Eclipse中Android NDK开发环境的配置说明 #### 一、概述 本文档将详细介绍如何在Eclipse中配置Android NDK开发环境,并实现C/C++代码的自动编译以及通过Eclipse使用Ant生成JNI所需的头文件。配置流程...
Android NDK,全称为Native Development Kit,是Google提供的一款用于Android平台的C和C++原生代码开发工具。NDK允许开发者在Android应用中使用原生代码,以实现高性能、低级别的硬件交互以及利用已有的C/C++库。在...
下载 https://developer.android.google.cn/ndk/downloads/ https://developer.android.google.cn/ndk/downloads/older_releases 编译ffmpeg:最高版本16b,再高的版本...export NDK=/home/quantum6/android-ndk-16b
Android NDK,全称为Native Development Kit,是Google提供的一款用于Android平台的C和C++开发工具集。...通过“android-ndk-r26b-windows.zip”,Windows用户可以方便地获取并开始他们的原生代码开发之旅。
《Android NDK开发实战》是针对移动应用开发者的一份宝贵资源,主要涵盖了如何在Android平台上进行原生代码开发。NDK(Native Development Kit)是Google为Android提供的一个工具集,它允许开发者使用C和C++编写性能...
在Android系统中,由于原生的Java API无法直接处理这些底层的流媒体协议,因此需要借助于NDK(Native Development Kit)来实现C或C++代码的编译,将Live555移植到Android平台。 **关于Live555:** Live555是一个...
博客名称 : 【Android NDK 开发】在 C 代码中获取 Android 系统信息 ( NDK 项目创建 | NDK 配置 | 获取 Android 系统版本号 ) 博客地址 : https://hanshuliang.blog.csdn.net/article/details/102933704
Android NDK,全称为Native Development Kit,是Google提供的一款用于Android平台的C和C++库开发工具集。这个工具允许开发者在Android应用中使用原生代码,以实现高性能计算、图形处理、游戏引擎等复杂功能。`...
Android NDK,全称为Native Development Kit,是Google提供的一款用于Android平台的开发工具,它允许开发者使用C++和其他原生编程语言编写应用的部分或全部代码。这个“android-ndk-r26b-linux.zip”文件是NDK的一个...
本文将深入探讨如何使用Android NDK(Native Development Kit)来编译适用于Android平台的eXosip库,包括静态库和动态库的构建过程。 首先,eXosip是基于OSI(Open Systems Interconnection)模型的SIP协议栈,它是...
为了满足不同层次的开发需求,Google 提供了多种工具和技术栈,其中 Android NDK (Native Development Kit) 就是其中之一。它允许开发者使用 C 或 C++ 编写部分应用逻辑,从而利用更底层的语言来优化性能。本文将...
【Android NDK】是Android Native Development Kit的缩写,它允许开发者使用C和C++编写应用程序的底层代码,以提高性能或利用特定硬件功能。在Android应用开发中,NDK通常用于处理图形计算、音频处理、游戏引擎等对...