fclose在android和linux上的工作方式不同

时间:2012-05-30 13:01:08

标签: android linux fclose android-ndk freopen

遵循计划:

#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>

int main() {
  fclose( stderr );
  printf( "%d\n", fileno( stderr ) );
  return 0;
}

在ubuntu 11.04上显示-1,在ICS 4.0.3模拟器上显示2。找不到有关此问题的任何信息 - 我可以在两个平台上使此代码的工作类似吗? freopen上的stderr也有同样的问题。

更新

之前的小程序演示了我遇到的实际问题的原因:如果我尝试freopen stderr在不存在的目录中提交,则在Linux stderr上关闭但在android上停留开了!甚至更多 - 如果我在这个打开的stderr文件中写入smth然后在其他文件上执行fopen,则打印到stderr的文本将被写入此打开的文件。

所以,这个程序:

#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>

# define LOGD( ... ) printf( __VA_ARGS__ ); printf( "\n" )

# ifdef ANDROID
#   define HOMEDIR "/data/data/com.myapp/" // for android
# else
#   define HOMEDIR "/home/darkmist/" // for linux
# endif

# define _T( x ) x

void TestFreopen_mkdir() {
  int mkdirres = mkdir( HOMEDIR "1.d", 0777 );
  LOGD(_T("TestFreopen mkdirres=0x%08x"),mkdirres);
}

void TestFreopen() {
  LOGD(_T("TestFreopen begin"));

  LOGD(_T("TestFreopen stderr=0x%08x"),fileno(stderr));
  fprintf(stderr,"fprintf_1 to stderr\n");

  // TestFreopen_mkdir(); // case 1

  if ( NULL == freopen( HOMEDIR "1.d/1", "w", stderr ) ) {
    LOGD( "freopen failed" );
    if ( -1 != fileno( stderr ) ) {
      fclose( stderr );
      LOGD( "freopen closed" );
    }
  }

  LOGD(_T("TestFreopen stderr=0x%08x"),fileno(stderr));
  fprintf(stderr,"fprintf_2 to stderr\n");

  TestFreopen_mkdir(); // case 2

  FILE* fopen_file = fopen( HOMEDIR "1.d/2", _T( "wb" ) );

  LOGD(_T("TestFreopen fopen_file=0x%08x"),fileno(fopen_file)); // same as for reopened stderr!!

  fprintf(stderr,"fprintf_3 to stderr\n");
  fprintf(fopen_file,"fprintf_1 to fopen_file\n");
  fflush(fopen_file);

  LOGD(_T("TestFreopen end"));
}

int main() {
  TestFreopen();
  return 0;
}

在linux上显示:

$ ./a.out
TestFreopen begin
TestFreopen stderr=0x00000002
fprintf_1 to stderr
freopen failed
TestFreopen stderr=0xffffffff
TestFreopen mkdirres=0x00000000
TestFreopen fopen_file=0x00000002
TestFreopen end

$ cat ~/1.d/2 
fprintf_1 to fopen_file

这个在android:

$ adb push ./a.out /data/data/com.myapp
573 KB/s (34635 bytes in 0.058s)

$ adb shell run-as com.myapp /data/data/com.myapp/a.out
TestFreopen begin
TestFreopen stderr=0x00000002
fprintf_1 to stderr
freopen failed
freopen closed
TestFreopen stderr=0x00000002
TestFreopen mkdirres=0x00000000
TestFreopen fopen_file=0x00000002
TestFreopen end

$ adb shell run-as com.myapp cat /data/data/com.myapp/1.d/2
fprintf_3 to stderr
fprintf_1 to fopen_file

3 个答案:

答案 0 :(得分:10)

在我看来,您希望正式声明的文件指针在其关闭之后的未定义行为在具有非常不同的C库的设备上是相同的。

一旦以一种旨在失败的方式调用freopen(),未来尝试使用该文件指针就不能依赖于获得一致的结果。

你发现了一些有趣的结果,剩下的碎片仍然可以用于意外的结果,但问题并不是它们不会导致某种异常,问题是你试图使用无效的文件指针用一种不宣传自己有保护措施的语言。

问题不在于系统,而在于您的程序滥用系统。

答案 1 :(得分:4)

在任何平台上关闭它后尝试使用stderr没有任何意义。

答案 2 :(得分:3)

fclose()是由libc提供的函数,因此在未确定 fclose()之后,libc的不同实现可能具有与文件描述符的状态不同的行为

Ubuntu使用eglibc,而android使用bionic作为标准libc。


eglibc

查看 fclose()的eglibc 2.15源代码,我们有:

来自iofclose.c的

...
_IO_acquire_lock (fp);
if (fp->_IO_file_flags & _IO_IS_FILEBUF)
    status = INTUSE(_IO_file_close_it) (fp);
else
    status = fp->_flags & _IO_ERR_SEEN ? -1 : 0;
...

您正在关闭strerr,第一个语句将被执行。查看以下函数:

来自fileops.c的

fp->_flags = _IO_MAGIC|CLOSED_FILEBUF_FLAGS;
fp->_fileno = -1;
fp->_offset = _IO_pos_BAD;

return close_status ? close_status : write_status;

如您所见,eglibc将显式将fileno设置为-1。


bionic

Bionic以自己的方式处理文件,从 fileno()开始,返回 fp-&gt; _file

来自stdio.h

#define __sfileno(p)    ((p)->_file)
来自fileno.c的

int
fileno(FILE *fp)
{
        int ret;

    FLOCKFILE(fp);
        ret = __sfileno(fp);
        FUNLOCKFILE(fp);
        return (ret);
}

查看 fclose()的仿生源代码,我们有:

来自fclose.c

int
fclose(FILE *fp)
{
        int r;

        if (fp->_flags == 0) {  /* not open! */
                errno = EBADF;
                return (EOF);
        }
        FLOCKFILE(fp);
        WCIO_FREE(fp);
        r = fp->_flags & __SWR ? __sflush(fp) : 0;
        if (fp->_close != NULL && (*fp->_close)(fp->_cookie) < 0)
                r = EOF;
        if (fp->_flags & __SMBF)
                free((char *)fp->_bf._base);
        if (HASUB(fp))
                FREEUB(fp);
        if (HASLB(fp))
                FREELB(fp);
        fp->_r = fp->_w = 0;    /* Mess up if reaccessed. */
        fp->_flags = 0;         /* Release this FILE for reuse. */
        FUNLOCKFILE(fp);
        return (r);
}

来自wcio.h

#define WCIO_FREE(fp) ((void)(0))
来自local.h的

#define HASUB(fp) (_UB(fp)._base != NULL)
#define FREEUB(fp) { \
        if (_UB(fp)._base != (fp)->_ubuf) \
                free(_UB(fp)._base); \
        _UB(fp)._base = NULL; \
}

如您所见,fp-&gt; _file在fclose()过程中保持不变。这实际上不是一个错误,因为 fclose()之后文件描述符的状态未确定,但这应该澄清在Ubuntu和Android中执行程序之间的区别。

为了使你的代码可移植,你应该避免在关闭它之后关闭stderr或重新打开它/ / dev / null。