为什么 S_ISDIR() 表示一个目录,而没有一个目录?
Why is S_ISDIR() indicating a directory when there isn't one?
POSIX函数S_ISDIR偶尔会骗我
它告诉我一个目录存在,而它显然不存在。
这是一个说明问题的小程序:
#include <sys/stat.h>
#include <dirent.h>
#include <string>
#include <iostream>
#include <iomanip>
bool Is_Directory(const char* path_to_file){
struct stat fileInfo;
std::cout << lstat(path_to_file, &fileInfo) << " ";
return S_ISDIR(fileInfo.st_mode);
}
int main(){
std::cout << std::boolalpha;
std::cout << Is_Directory("folder") << '\n';
std::cout << Is_Directory("folder") << '\n';
std::cout << Is_Directory("folder") << '\n';
std::cout << Is_Directory("folder") << '\n';
std::cout << Is_Directory("folder") << '\n';
std::cout << Is_Directory("folder") << '\n';
}
如果我运行这个程序(很多次),很快,我会看到下面的输出:
$./main
-1 false
-1 false
-1 false
-1 false
-1 false
-1 false
$./main
-1 false
-1 false
-1 true
-1 true
-1 true
-1 true
$./main
-1 false
-1 false
-1 false
-1 false
-1 false
-1 false
看看函数怎么突然returns true
,虽然目录不存在。
奇怪的是,如果我让程序无限循环检查,它会继续说目录不存在。只有通过 运行 一次又一次快速连续地运行程序,我才能发现问题。
这是我到目前为止尝试过的方法:
查看代码:
代码似乎没有错。
Macro: int S_ISDIR (mode_t m)
This macro returns non-zero if the file is a directory.
lstat
的错误代码始终为 -1,因此我认为填充统计信息不会偶尔出现错误。
阅读文档:
我在 lstat
上看到了以下文档:
lstat() is identical to stat(), except that if pathname is a symbolic
link, then it returns information about the link itself, not the file
that it refers to.
我不太明白这意味着什么,但也许它与我的问题有关?
所以我决定改用常规 stat()
,但我仍然看到同样的问题。
不同的编译器:
我已经尝试了两种带有警告和消毒剂的不同编译器。
g++
和 clang++
。两者都表现出同样的问题。
需要用C编译器编译吗?
我 re-wrote 香草 C 中的程序(但仍使用 g++/clang++ 编译)。
#include <sys/stat.h>
#include <dirent.h>
#include <stdio.h>
bool Is_Directory(const char* path_to_file){
struct stat fileInfo;
printf("%d ",lstat(path_to_file, &fileInfo));
return S_ISDIR(fileInfo.st_mode);
}
int main(){
printf("%d\n",Is_Directory("folder"));
printf("%d\n",Is_Directory("folder"));
printf("%d\n",Is_Directory("folder"));
printf("%d\n",Is_Directory("folder"));
printf("%d\n",Is_Directory("folder"));
printf("%d\n",Is_Directory("folder"));
}
一下子,问题就解决了。我快速地一次又一次地启动程序,但它总是正确地报告目录不存在。
我切换回 C++ 代码,再次 运行 我的测试。果然,偶有误报。
是系统吗header?
我把 C++ headers 放到了 C 版本中。程序仍然可以正常运行。
是std::cout吗?
也许 std::cout
比较慢,这就是我看到问题的原因......或者它可能完全不相关。也许使用 std::cout
间接将导致问题的二进制文件保留下来。或者 std::cout
正在对我的程序环境进行全局操作?
我在黑暗中拍摄。
我尝试了以下方法:
#include <sys/stat.h>
#include <dirent.h>
#include <stdio.h>
#include <iostream>
bool Is_Directory(const char* path_to_file){
struct stat fileInfo;
printf("%d ",lstat(path_to_file, &fileInfo));
return S_ISDIR(fileInfo.st_mode);
}
int main(){
std::cout << "test" << std::endl;
printf("%d\n",Is_Directory("folder"));
printf("%d\n",Is_Directory("folder"));
printf("%d\n",Is_Directory("folder"));
printf("%d\n",Is_Directory("folder"));
printf("%d\n",Is_Directory("folder"));
printf("%d\n",Is_Directory("folder"));
}
啊哈!
$./main
test
-1 0
-1 0
-1 0
-1 0
-1 0
-1 0
$./main
test
-1 0
-1 0
-1 0
-1 0
-1 0
-1 0
$./main
test
-1 1
-1 0
-1 0
-1 0
-1 0
-1 0
$./main
test
-1 0
-1 0
-1 0
-1 0
-1 0
-1 0
$./main
test
-1 1
-1 0
-1 0
-1 0
-1 0
-1 0
$./main
test
-1 0
-1 0
-1 0
-1 0
-1 0
-1 0
现在它只是第一次检查有时 return 是真的。
就像 std::cout
以某种方式弄乱了 S_ISDIR
,但是在调用 S_ISDIR
之后,它不会弄乱对 S_ISDIR
的下一次调用。
调查来源:
我在 /usr/include/sys
中找到了 S_ISDIR
的源代码:
/* Test macros for file types. */
#define __S_ISTYPE(mode, mask) (((mode) & __S_IFMT) == (mask))
#define S_ISDIR(mode) __S_ISTYPE((mode), __S_IFDIR)
S_ISDIR
好像只是个帮手,目录有没有存在,已经由stat()
决定了。 (同样,我已经尝试了 stat
和 lstat
。 我应该使用 fstat
吗? 我不这么认为。我在网上找到了人们正在使用的其他示例
S_ISDIR
与我的示例代码相同)。
同样,当我将代码放入使用 std::cout
进行检查和打印的无限循环时,它没有显示出症状。这让我相信问题只发生在程序开始时,但我想这似乎也不是真的,因为如果你看一下我的原始输出,它是:
$./main
-1 false
-1 false
-1 true
-1 true
-1 true
-1 true
操作系统/硬盘/系统库/编译器:
我的机器有问题吗?
不,我在 Ubuntu 16.04.1 LTS
。我去另一台机器 CentOS 6.5
上用旧版本 g++
尝试了这个。相同的结果。
所以我的代码很糟糕。
隔离问题:
我简化了问题。
这个程序会有时return一个错误。
#include <sys/stat.h>
#include <iostream>
bool Is_Directory(const char* path_to_file){
struct stat fileInfo;
stat(path_to_file, &fileInfo);
return S_ISDIR(fileInfo.st_mode);
}
int main(){
std::cout << std::endl;
return Is_Directory("folder");
}
此程序将永远不会return出错。
#include <sys/stat.h>
#include <iostream>
bool Is_Directory(const char* path_to_file){
struct stat fileInfo;
stat(path_to_file, &fileInfo);
return S_ISDIR(fileInfo.st_mode);
}
int main(){
return Is_Directory("folder");
}
为什么刷新缓冲区会导致目录有时存在?
实际上,如果我只刷新缓冲区,问题就会消失。
此程序将永远不会return出错。
#include <sys/stat.h>
#include <iostream>
bool Is_Directory(const char* path_to_file){
struct stat fileInfo;
stat(path_to_file, &fileInfo);
return S_ISDIR(fileInfo.st_mode);
}
int main(){
std::cout.flush();
return Is_Directory("folder");
}
嗯,那可能是因为它没有什么可冲洗的。
只要我至少刷新一个字符,我们的问题又来了。
这是真正的 MCVE:
#include <sys/stat.h>
#include <iostream>
int main(){
std::cout << std::endl;
struct stat fileInfo;
stat("f", &fileInfo);
return S_ISDIR(fileInfo.st_mode);
}
同样,无限循环不起作用。
该程序将 never return(假设它在第一次尝试时很幸运):
#include <sys/stat.h>
#include <iostream>
int main(){
while (true){
std::cout << std::endl;
struct stat fileInfo;
stat("f", &fileInfo);
if(S_ISDIR(fileInfo.st_mode)) return 0;
}
}
所以问题出现在重新启动进程以及刷新时?
我放弃了大会,但这对我来说意义不大。
g++ -std=c++1z -g -c a.cpp
objdump -d -M intel -S a.o > a.s
a.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <main>:
#include <sys/stat.h>
#include <iostream>
int main(){
0: 55 push rbp
1: 48 89 e5 mov rbp,rsp
4: 48 81 ec a0 00 00 00 sub rsp,0xa0
b: 64 48 8b 04 25 28 00 mov rax,QWORD PTR fs:0x28
12: 00 00
14: 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax
18: 31 c0 xor eax,eax
std::cout << std::endl;
1a: be 00 00 00 00 mov esi,0x0
1f: bf 00 00 00 00 mov edi,0x0
24: e8 00 00 00 00 call 29 <main+0x29>
struct stat fileInfo;
stat("f", &fileInfo);
29: 48 8d 85 60 ff ff ff lea rax,[rbp-0xa0]
30: 48 89 c6 mov rsi,rax
33: bf 00 00 00 00 mov edi,0x0
38: e8 00 00 00 00 call 3d <main+0x3d>
return S_ISDIR(fileInfo.st_mode);
3d: 8b 85 78 ff ff ff mov eax,DWORD PTR [rbp-0x88]
43: 25 00 f0 00 00 and eax,0xf000
48: 3d 00 40 00 00 cmp eax,0x4000
4d: 0f 94 c0 sete al
50: 0f b6 c0 movzx eax,al
53: 48 8b 55 f8 mov rdx,QWORD PTR [rbp-0x8]
57: 64 48 33 14 25 28 00 xor rdx,QWORD PTR fs:0x28
5e: 00 00
60: 74 05 je 67 <main+0x67>
62: e8 00 00 00 00 call 67 <main+0x67>
67: c9 leave
68: c3 ret
0000000000000069 <_Z41__static_initialization_and_destruction_0ii>:
69: 55 push rbp
6a: 48 89 e5 mov rbp,rsp
6d: 48 83 ec 10 sub rsp,0x10
71: 89 7d fc mov DWORD PTR [rbp-0x4],edi
74: 89 75 f8 mov DWORD PTR [rbp-0x8],esi
77: 83 7d fc 01 cmp DWORD PTR [rbp-0x4],0x1
7b: 75 27 jne a4 <_Z41__static_initialization_and_destruction_0ii+0x3b>
7d: 81 7d f8 ff ff 00 00 cmp DWORD PTR [rbp-0x8],0xffff
84: 75 1e jne a4 <_Z41__static_initialization_and_destruction_0ii+0x3b>
extern wostream wclog; /// Linked to standard error (buffered)
#endif
//@}
// For construction of filebuffers for cout, cin, cerr, clog et. al.
static ios_base::Init __ioinit;
86: bf 00 00 00 00 mov edi,0x0
8b: e8 00 00 00 00 call 90 <_Z41__static_initialization_and_destruction_0ii+0x27>
90: ba 00 00 00 00 mov edx,0x0
95: be 00 00 00 00 mov esi,0x0
9a: bf 00 00 00 00 mov edi,0x0
9f: e8 00 00 00 00 call a4 <_Z41__static_initialization_and_destruction_0ii+0x3b>
a4: 90 nop
a5: c9 leave
a6: c3 ret
00000000000000a7 <_GLOBAL__sub_I_main>:
a7: 55 push rbp
a8: 48 89 e5 mov rbp,rsp
ab: be ff ff 00 00 mov esi,0xffff
b0: bf 01 00 00 00 mov edi,0x1
b5: e8 af ff ff ff call 69 <_Z41__static_initialization_and_destruction_0ii>
ba: 5d pop rbp
bb: c3 ret
我尝试遵循 stat 源代码,但迷路了。
C++ 源代码更容易理解。这是来自 /bits/ostream.tcc
的刷新函数:
template<typename _CharT, typename _Traits>
basic_ostream<_CharT, _Traits>&
basic_ostream<_CharT, _Traits>::
flush()
{
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// DR 60. What is a formatted input function?
// basic_ostream::flush() is *not* an unformatted output function.
ios_base::iostate __err = ios_base::goodbit;
__try
{
if (this->rdbuf() && this->rdbuf()->pubsync() == -1)
__err |= ios_base::badbit;
}
__catch(__cxxabiv1::__forced_unwind&)
{
this->_M_setstate(ios_base::badbit);
__throw_exception_again;
}
__catch(...)
{ this->_M_setstate(ios_base::badbit); }
if (__err)
this->setstate(__err);
return *this;
}
它似乎调用了 pubsync()
这导致我进入一个 sync()
方法/ext/stdio_sync_filebuf.h
:
sync()
{ return std::fflush(_M_file); }
virtual std::streampos
seekoff(std::streamoff __off, std::ios_base::seekdir __dir,
std::ios_base::openmode = std::ios_base::in | std::ios_base::out)
{
std::streampos __ret(std::streamoff(-1));
int __whence;
if (__dir == std::ios_base::beg)
__whence = SEEK_SET;
else if (__dir == std::ios_base::cur)
__whence = SEEK_CUR;
else
__whence = SEEK_END;
#ifdef _GLIBCXX_USE_LFS
if (!fseeko64(_M_file, __off, __whence))
__ret = std::streampos(ftello64(_M_file));
#else
if (!fseek(_M_file, __off, __whence))
__ret = std::streampos(std::ftell(_M_file));
#endif
return __ret;
}
virtual std::streampos
seekpos(std::streampos __pos,
std::ios_base::openmode __mode =
std::ios_base::in | std::ios_base::out)
{ return seekoff(std::streamoff(__pos), std::ios_base::beg, __mode); }
}; sync()
{ return std::fflush(_M_file); }
virtual std::streampos
seekoff(std::streamoff __off, std::ios_base::seekdir __dir,
std::ios_base::openmode = std::ios_base::in | std::ios_base::out)
{
std::streampos __ret(std::streamoff(-1));
int __whence;
if (__dir == std::ios_base::beg)
__whence = SEEK_SET;
else if (__dir == std::ios_base::cur)
__whence = SEEK_CUR;
else
__whence = SEEK_END;
#ifdef _GLIBCXX_USE_LFS
if (!fseeko64(_M_file, __off, __whence))
__ret = std::streampos(ftello64(_M_file));
#else
if (!fseek(_M_file, __off, __whence))
__ret = std::streampos(std::ftell(_M_file));
#endif
return __ret;
}
virtual std::streampos
seekpos(std::streampos __pos,
std::ios_base::openmode __mode =
std::ios_base::in | std::ios_base::out)
{ return seekoff(std::streamoff(__pos), std::ios_base::beg, __mode); }
};
据我所知,C++ 正在将工作分摊给 std::fflush
。
经过更多的测试,我发现
来自 <iostream>
的 fflush()
出现问题,但来自 <stdio.h>
的 fflush()
没有。
我试图从 fflush()
向后追溯,但我认为我触及了源代码边界。
This function is a possible cancellation point and therefore not
marked with __THROW. */
extern int fflush (FILE *__stream);
__END_NAMESPACE_STD
#ifdef __USE_MISC
/* Faster versions when locking is not required.
This function is not part of POSIX and therefore no official
cancellation point. But due to similarity with an POSIX interface
or due to the implementation it is a cancellation point and
therefore not marked with __THROW. */
extern int fflush_unlocked (FILE *__stream);
#endif
所以我 link 肯定是这样的?
//exhibits the problem
#include <sys/stat.h>
#include <iostream>
int main(){
printf("\n");fflush(stdout);
struct stat fileInfo;
stat("f", &fileInfo);
return S_ISDIR(fileInfo.st_mode);
}
g++ -std=c++11 -o main a.cpp
ldd main
linux-vdso.so.1 => (0x00007ffdc878e000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f1300c00000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f1300837000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f130052d000)
/lib64/ld-linux-x86-64.so.2 (0x000055bace4bc000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f1300316000)
//works correctly
#include <sys/stat.h>
#include <stdio.h>
int main(){
printf("\n");fflush(stdout);
struct stat fileInfo;
stat("f", &fileInfo);
return S_ISDIR(fileInfo.st_mode);
}
g++ -std=c++11 -o main a.cpp
ldd main
linux-vdso.so.1 => (0x00007ffd57f7c000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f482dc6c000)
/lib64/ld-linux-x86-64.so.2 (0x000055828633a000)
我假设 libstdc++.so.6
不适合使用 S_ISDIR
,但 libc.so.6
是?我应该单独构建使用 S_ISDIR
的代码,然后使用 C++ 代码构建 link 代码吗?我怎样才能更快地发现这样的问题?我仍然不明白发生了什么。我 trampling/observing 是不是因为我 link 编辑了错误的库而记忆错误?你会如何解决这个问题?
只有系统调用成功才能分析lstat()
设置的struct stat
数据。如果失败,它 returns -1
(它可能根本没有修改 fileInfo
中的数据——尽管这些值是不确定的)。你在 fileInfo.st_mode
中得到的是垃圾,因为 lstat()
失败了——它可以 return 对 S_ISDIR()
为真或为假。
因此,您的第一个示例表明 lstat()
每次都失败,因此对 struct stat
的任何分析都是徒劳的;尚未设置任何确定值,任何结果都可以。
我相信同样的论点适用于所有示例代码。
stat()
和lstat()
的区别在于,如果提供的名称是符号链接,stat()
系统调用引用符号链接远端的文件系统对象(假设有一个;如果符号链接指向 non-existent 对象,它会失败),而 lstat()
系统调用引用符号链接本身。当名称不是符号链接时,两者调用return相同的信息。
POSIX函数S_ISDIR偶尔会骗我
它告诉我一个目录存在,而它显然不存在。
这是一个说明问题的小程序:
#include <sys/stat.h>
#include <dirent.h>
#include <string>
#include <iostream>
#include <iomanip>
bool Is_Directory(const char* path_to_file){
struct stat fileInfo;
std::cout << lstat(path_to_file, &fileInfo) << " ";
return S_ISDIR(fileInfo.st_mode);
}
int main(){
std::cout << std::boolalpha;
std::cout << Is_Directory("folder") << '\n';
std::cout << Is_Directory("folder") << '\n';
std::cout << Is_Directory("folder") << '\n';
std::cout << Is_Directory("folder") << '\n';
std::cout << Is_Directory("folder") << '\n';
std::cout << Is_Directory("folder") << '\n';
}
如果我运行这个程序(很多次),很快,我会看到下面的输出:
$./main
-1 false
-1 false
-1 false
-1 false
-1 false
-1 false
$./main
-1 false
-1 false
-1 true
-1 true
-1 true
-1 true
$./main
-1 false
-1 false
-1 false
-1 false
-1 false
-1 false
看看函数怎么突然returns true
,虽然目录不存在。
奇怪的是,如果我让程序无限循环检查,它会继续说目录不存在。只有通过 运行 一次又一次快速连续地运行程序,我才能发现问题。
这是我到目前为止尝试过的方法:
查看代码: 代码似乎没有错。
Macro: int S_ISDIR (mode_t m)
This macro returns non-zero if the file is a directory.
lstat
的错误代码始终为 -1,因此我认为填充统计信息不会偶尔出现错误。
阅读文档:
我在 lstat
上看到了以下文档:
lstat() is identical to stat(), except that if pathname is a symbolic
link, then it returns information about the link itself, not the file
that it refers to.
我不太明白这意味着什么,但也许它与我的问题有关?
所以我决定改用常规 stat()
,但我仍然看到同样的问题。
不同的编译器:
我已经尝试了两种带有警告和消毒剂的不同编译器。
g++
和 clang++
。两者都表现出同样的问题。
需要用C编译器编译吗?
我 re-wrote 香草 C 中的程序(但仍使用 g++/clang++ 编译)。
#include <sys/stat.h>
#include <dirent.h>
#include <stdio.h>
bool Is_Directory(const char* path_to_file){
struct stat fileInfo;
printf("%d ",lstat(path_to_file, &fileInfo));
return S_ISDIR(fileInfo.st_mode);
}
int main(){
printf("%d\n",Is_Directory("folder"));
printf("%d\n",Is_Directory("folder"));
printf("%d\n",Is_Directory("folder"));
printf("%d\n",Is_Directory("folder"));
printf("%d\n",Is_Directory("folder"));
printf("%d\n",Is_Directory("folder"));
}
一下子,问题就解决了。我快速地一次又一次地启动程序,但它总是正确地报告目录不存在。
我切换回 C++ 代码,再次 运行 我的测试。果然,偶有误报。
是系统吗header?
我把 C++ headers 放到了 C 版本中。程序仍然可以正常运行。
是std::cout吗?
也许 std::cout
比较慢,这就是我看到问题的原因......或者它可能完全不相关。也许使用 std::cout
间接将导致问题的二进制文件保留下来。或者 std::cout
正在对我的程序环境进行全局操作?
我在黑暗中拍摄。
我尝试了以下方法:
#include <sys/stat.h>
#include <dirent.h>
#include <stdio.h>
#include <iostream>
bool Is_Directory(const char* path_to_file){
struct stat fileInfo;
printf("%d ",lstat(path_to_file, &fileInfo));
return S_ISDIR(fileInfo.st_mode);
}
int main(){
std::cout << "test" << std::endl;
printf("%d\n",Is_Directory("folder"));
printf("%d\n",Is_Directory("folder"));
printf("%d\n",Is_Directory("folder"));
printf("%d\n",Is_Directory("folder"));
printf("%d\n",Is_Directory("folder"));
printf("%d\n",Is_Directory("folder"));
}
啊哈!
$./main
test
-1 0
-1 0
-1 0
-1 0
-1 0
-1 0
$./main
test
-1 0
-1 0
-1 0
-1 0
-1 0
-1 0
$./main
test
-1 1
-1 0
-1 0
-1 0
-1 0
-1 0
$./main
test
-1 0
-1 0
-1 0
-1 0
-1 0
-1 0
$./main
test
-1 1
-1 0
-1 0
-1 0
-1 0
-1 0
$./main
test
-1 0
-1 0
-1 0
-1 0
-1 0
-1 0
现在它只是第一次检查有时 return 是真的。
就像 std::cout
以某种方式弄乱了 S_ISDIR
,但是在调用 S_ISDIR
之后,它不会弄乱对 S_ISDIR
的下一次调用。
调查来源:
我在 /usr/include/sys
中找到了 S_ISDIR
的源代码:
/* Test macros for file types. */
#define __S_ISTYPE(mode, mask) (((mode) & __S_IFMT) == (mask))
#define S_ISDIR(mode) __S_ISTYPE((mode), __S_IFDIR)
S_ISDIR
好像只是个帮手,目录有没有存在,已经由stat()
决定了。 (同样,我已经尝试了 stat
和 lstat
。 我应该使用 fstat
吗? 我不这么认为。我在网上找到了人们正在使用的其他示例
S_ISDIR
与我的示例代码相同)。
同样,当我将代码放入使用 std::cout
进行检查和打印的无限循环时,它没有显示出症状。这让我相信问题只发生在程序开始时,但我想这似乎也不是真的,因为如果你看一下我的原始输出,它是:
$./main
-1 false
-1 false
-1 true
-1 true
-1 true
-1 true
操作系统/硬盘/系统库/编译器:
我的机器有问题吗?
不,我在 Ubuntu 16.04.1 LTS
。我去另一台机器 CentOS 6.5
上用旧版本 g++
尝试了这个。相同的结果。
所以我的代码很糟糕。
隔离问题:
我简化了问题。
这个程序会有时return一个错误。
#include <sys/stat.h>
#include <iostream>
bool Is_Directory(const char* path_to_file){
struct stat fileInfo;
stat(path_to_file, &fileInfo);
return S_ISDIR(fileInfo.st_mode);
}
int main(){
std::cout << std::endl;
return Is_Directory("folder");
}
此程序将永远不会return出错。
#include <sys/stat.h>
#include <iostream>
bool Is_Directory(const char* path_to_file){
struct stat fileInfo;
stat(path_to_file, &fileInfo);
return S_ISDIR(fileInfo.st_mode);
}
int main(){
return Is_Directory("folder");
}
为什么刷新缓冲区会导致目录有时存在?
实际上,如果我只刷新缓冲区,问题就会消失。
此程序将永远不会return出错。
#include <sys/stat.h>
#include <iostream>
bool Is_Directory(const char* path_to_file){
struct stat fileInfo;
stat(path_to_file, &fileInfo);
return S_ISDIR(fileInfo.st_mode);
}
int main(){
std::cout.flush();
return Is_Directory("folder");
}
嗯,那可能是因为它没有什么可冲洗的。
只要我至少刷新一个字符,我们的问题又来了。
这是真正的 MCVE:
#include <sys/stat.h>
#include <iostream>
int main(){
std::cout << std::endl;
struct stat fileInfo;
stat("f", &fileInfo);
return S_ISDIR(fileInfo.st_mode);
}
同样,无限循环不起作用。
该程序将 never return(假设它在第一次尝试时很幸运):
#include <sys/stat.h>
#include <iostream>
int main(){
while (true){
std::cout << std::endl;
struct stat fileInfo;
stat("f", &fileInfo);
if(S_ISDIR(fileInfo.st_mode)) return 0;
}
}
所以问题出现在重新启动进程以及刷新时?
我放弃了大会,但这对我来说意义不大。
g++ -std=c++1z -g -c a.cpp
objdump -d -M intel -S a.o > a.s
a.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <main>:
#include <sys/stat.h>
#include <iostream>
int main(){
0: 55 push rbp
1: 48 89 e5 mov rbp,rsp
4: 48 81 ec a0 00 00 00 sub rsp,0xa0
b: 64 48 8b 04 25 28 00 mov rax,QWORD PTR fs:0x28
12: 00 00
14: 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax
18: 31 c0 xor eax,eax
std::cout << std::endl;
1a: be 00 00 00 00 mov esi,0x0
1f: bf 00 00 00 00 mov edi,0x0
24: e8 00 00 00 00 call 29 <main+0x29>
struct stat fileInfo;
stat("f", &fileInfo);
29: 48 8d 85 60 ff ff ff lea rax,[rbp-0xa0]
30: 48 89 c6 mov rsi,rax
33: bf 00 00 00 00 mov edi,0x0
38: e8 00 00 00 00 call 3d <main+0x3d>
return S_ISDIR(fileInfo.st_mode);
3d: 8b 85 78 ff ff ff mov eax,DWORD PTR [rbp-0x88]
43: 25 00 f0 00 00 and eax,0xf000
48: 3d 00 40 00 00 cmp eax,0x4000
4d: 0f 94 c0 sete al
50: 0f b6 c0 movzx eax,al
53: 48 8b 55 f8 mov rdx,QWORD PTR [rbp-0x8]
57: 64 48 33 14 25 28 00 xor rdx,QWORD PTR fs:0x28
5e: 00 00
60: 74 05 je 67 <main+0x67>
62: e8 00 00 00 00 call 67 <main+0x67>
67: c9 leave
68: c3 ret
0000000000000069 <_Z41__static_initialization_and_destruction_0ii>:
69: 55 push rbp
6a: 48 89 e5 mov rbp,rsp
6d: 48 83 ec 10 sub rsp,0x10
71: 89 7d fc mov DWORD PTR [rbp-0x4],edi
74: 89 75 f8 mov DWORD PTR [rbp-0x8],esi
77: 83 7d fc 01 cmp DWORD PTR [rbp-0x4],0x1
7b: 75 27 jne a4 <_Z41__static_initialization_and_destruction_0ii+0x3b>
7d: 81 7d f8 ff ff 00 00 cmp DWORD PTR [rbp-0x8],0xffff
84: 75 1e jne a4 <_Z41__static_initialization_and_destruction_0ii+0x3b>
extern wostream wclog; /// Linked to standard error (buffered)
#endif
//@}
// For construction of filebuffers for cout, cin, cerr, clog et. al.
static ios_base::Init __ioinit;
86: bf 00 00 00 00 mov edi,0x0
8b: e8 00 00 00 00 call 90 <_Z41__static_initialization_and_destruction_0ii+0x27>
90: ba 00 00 00 00 mov edx,0x0
95: be 00 00 00 00 mov esi,0x0
9a: bf 00 00 00 00 mov edi,0x0
9f: e8 00 00 00 00 call a4 <_Z41__static_initialization_and_destruction_0ii+0x3b>
a4: 90 nop
a5: c9 leave
a6: c3 ret
00000000000000a7 <_GLOBAL__sub_I_main>:
a7: 55 push rbp
a8: 48 89 e5 mov rbp,rsp
ab: be ff ff 00 00 mov esi,0xffff
b0: bf 01 00 00 00 mov edi,0x1
b5: e8 af ff ff ff call 69 <_Z41__static_initialization_and_destruction_0ii>
ba: 5d pop rbp
bb: c3 ret
我尝试遵循 stat 源代码,但迷路了。
C++ 源代码更容易理解。这是来自 /bits/ostream.tcc
的刷新函数:
template<typename _CharT, typename _Traits>
basic_ostream<_CharT, _Traits>&
basic_ostream<_CharT, _Traits>::
flush()
{
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// DR 60. What is a formatted input function?
// basic_ostream::flush() is *not* an unformatted output function.
ios_base::iostate __err = ios_base::goodbit;
__try
{
if (this->rdbuf() && this->rdbuf()->pubsync() == -1)
__err |= ios_base::badbit;
}
__catch(__cxxabiv1::__forced_unwind&)
{
this->_M_setstate(ios_base::badbit);
__throw_exception_again;
}
__catch(...)
{ this->_M_setstate(ios_base::badbit); }
if (__err)
this->setstate(__err);
return *this;
}
它似乎调用了 pubsync()
这导致我进入一个 sync()
方法/ext/stdio_sync_filebuf.h
:
sync()
{ return std::fflush(_M_file); }
virtual std::streampos
seekoff(std::streamoff __off, std::ios_base::seekdir __dir,
std::ios_base::openmode = std::ios_base::in | std::ios_base::out)
{
std::streampos __ret(std::streamoff(-1));
int __whence;
if (__dir == std::ios_base::beg)
__whence = SEEK_SET;
else if (__dir == std::ios_base::cur)
__whence = SEEK_CUR;
else
__whence = SEEK_END;
#ifdef _GLIBCXX_USE_LFS
if (!fseeko64(_M_file, __off, __whence))
__ret = std::streampos(ftello64(_M_file));
#else
if (!fseek(_M_file, __off, __whence))
__ret = std::streampos(std::ftell(_M_file));
#endif
return __ret;
}
virtual std::streampos
seekpos(std::streampos __pos,
std::ios_base::openmode __mode =
std::ios_base::in | std::ios_base::out)
{ return seekoff(std::streamoff(__pos), std::ios_base::beg, __mode); }
}; sync()
{ return std::fflush(_M_file); }
virtual std::streampos
seekoff(std::streamoff __off, std::ios_base::seekdir __dir,
std::ios_base::openmode = std::ios_base::in | std::ios_base::out)
{
std::streampos __ret(std::streamoff(-1));
int __whence;
if (__dir == std::ios_base::beg)
__whence = SEEK_SET;
else if (__dir == std::ios_base::cur)
__whence = SEEK_CUR;
else
__whence = SEEK_END;
#ifdef _GLIBCXX_USE_LFS
if (!fseeko64(_M_file, __off, __whence))
__ret = std::streampos(ftello64(_M_file));
#else
if (!fseek(_M_file, __off, __whence))
__ret = std::streampos(std::ftell(_M_file));
#endif
return __ret;
}
virtual std::streampos
seekpos(std::streampos __pos,
std::ios_base::openmode __mode =
std::ios_base::in | std::ios_base::out)
{ return seekoff(std::streamoff(__pos), std::ios_base::beg, __mode); }
};
据我所知,C++ 正在将工作分摊给 std::fflush
。
经过更多的测试,我发现
来自 <iostream>
的 fflush()
出现问题,但来自 <stdio.h>
的 fflush()
没有。
我试图从 fflush()
向后追溯,但我认为我触及了源代码边界。
This function is a possible cancellation point and therefore not
marked with __THROW. */
extern int fflush (FILE *__stream);
__END_NAMESPACE_STD
#ifdef __USE_MISC
/* Faster versions when locking is not required.
This function is not part of POSIX and therefore no official
cancellation point. But due to similarity with an POSIX interface
or due to the implementation it is a cancellation point and
therefore not marked with __THROW. */
extern int fflush_unlocked (FILE *__stream);
#endif
所以我 link 肯定是这样的?
//exhibits the problem
#include <sys/stat.h>
#include <iostream>
int main(){
printf("\n");fflush(stdout);
struct stat fileInfo;
stat("f", &fileInfo);
return S_ISDIR(fileInfo.st_mode);
}
g++ -std=c++11 -o main a.cpp
ldd main
linux-vdso.so.1 => (0x00007ffdc878e000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f1300c00000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f1300837000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f130052d000)
/lib64/ld-linux-x86-64.so.2 (0x000055bace4bc000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f1300316000)
//works correctly
#include <sys/stat.h>
#include <stdio.h>
int main(){
printf("\n");fflush(stdout);
struct stat fileInfo;
stat("f", &fileInfo);
return S_ISDIR(fileInfo.st_mode);
}
g++ -std=c++11 -o main a.cpp
ldd main
linux-vdso.so.1 => (0x00007ffd57f7c000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f482dc6c000)
/lib64/ld-linux-x86-64.so.2 (0x000055828633a000)
我假设 libstdc++.so.6
不适合使用 S_ISDIR
,但 libc.so.6
是?我应该单独构建使用 S_ISDIR
的代码,然后使用 C++ 代码构建 link 代码吗?我怎样才能更快地发现这样的问题?我仍然不明白发生了什么。我 trampling/observing 是不是因为我 link 编辑了错误的库而记忆错误?你会如何解决这个问题?
只有系统调用成功才能分析lstat()
设置的struct stat
数据。如果失败,它 returns -1
(它可能根本没有修改 fileInfo
中的数据——尽管这些值是不确定的)。你在 fileInfo.st_mode
中得到的是垃圾,因为 lstat()
失败了——它可以 return 对 S_ISDIR()
为真或为假。
因此,您的第一个示例表明 lstat()
每次都失败,因此对 struct stat
的任何分析都是徒劳的;尚未设置任何确定值,任何结果都可以。
我相信同样的论点适用于所有示例代码。
stat()
和lstat()
的区别在于,如果提供的名称是符号链接,stat()
系统调用引用符号链接远端的文件系统对象(假设有一个;如果符号链接指向 non-existent 对象,它会失败),而 lstat()
系统调用引用符号链接本身。当名称不是符号链接时,两者调用return相同的信息。