5

次のプログラム:

#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;
}

-1ubuntu11.04および2ICS4.0.3エミュレーターで表示されます。この問題に関する情報が見つかりません-このコードを両方のプラットフォームで同様に機能させることはできますか?freopenonにstderrも同じ問題があります。

アップデート:

以前の小さなプログラムは、私が直面した実際の問題の原因を示していfreopen stderrます。存在しないディレクトリにファイルしようとすると、Linuxstderrでは閉じられますが、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

そしてこれはアンドロイドで:

$ 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
4

3 に答える 3

10

非常に異なるCライブラリを持つデバイスでは、閉じた後のファイルポインタの未定義動作として正式に宣言されているものが同じであると期待しているように思われます。

失敗するように設計された方法でfreopen()を呼び出すと、そのファイルポインターを使用する将来の試みは、一貫した結果を得ることに依存できるものではありません。

残った部分を予期しない結果で使用できる興味深い結果がいくつか見つかりましたが、問題はそれらが何らかの例外を引き起こさないということではなく、問題は言語で無効なファイルポインタを使用しようとしていることですそれは、セーフガードを持っていると宣伝していません。

問題はシステムにあるのではなく、プログラムによるシステムの誤用にあります。

于 2012-06-13T16:38:42.720 に答える
4

どのプラットフォームでも、stderrを閉じた後に使用しようとしても意味がありません。

于 2012-05-31T18:40:41.337 に答える
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は、ファイルを独自の方法で処理します。fileno()から開始して、 fp->_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()のbionicソースコードを見ると、次のようになっています。

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; \
}

ご覧のとおり、fclose()プロセス中はfp->_fileは変更されません。fclose()後のファイル記述子の状態が決定されていないため、これは実際にはバグではありませんが、UbuntuとAndroidでのプログラムの実行の違いを明確にする必要があります。

コードを移植可能にするには、stderrを閉じるのを避けるか、閉じた後に/ dev/nullのようなものに再度開く必要があります。

于 2012-06-18T20:26:29.787 に答える