我试着运行以下Go代码:
package main
import (
"fmt"
"io/ioutil"
)
func main() {
items, err := ioutil.ReadDir("/dev/fd")
if err != nil {
panic(err)
}
fmt.Println(items)
}
Run Code Online (Sandbox Code Playgroud)
我刚收到这个错误:
panic: lstat /dev/fd/4: bad file descriptor
goroutine 1 [running]:
main.main()
/Users/andy/Desktop/demo.go:11 +0xe8
exit status 2
Run Code Online (Sandbox Code Playgroud)
该/dev/fd文件夹的绝对存在,并且有一个/dev/fd/4里面就有我ls了.
$ ls -Al /dev/fd
total 9
crw--w---- 1 andy tty 16, 4 Jan 25 00:16 0
crw--w---- 1 andy tty 16, 4 Jan 25 00:16 1
crw--w---- 1 andy tty 16, 4 Jan 25 00:16 2
dr--r--r-- 3 root wheel 4419 Jan 23 20:42 3/
dr--r--r-- 1 root wheel 0 Jan 23 20:42 4/
Run Code Online (Sandbox Code Playgroud)
这是怎么回事?为什么我不能读这个目录?我试图端口的ls命令去这里,所以我想能够在此目录中,以产生类似的输出读取ls.
编辑:我作为非root用户运行一切.可执行位/dev/fd 已设置.
$ ls -al /dev | grep fd
dr-xr-xr-x 1 root wheel 0 Jan 23 20:42 fd/
$ stat /dev/fd/4 # same result with -L flag
stat: /dev/fd/4: stat: Bad file descriptor
Run Code Online (Sandbox Code Playgroud)
首先,让我们记住/ dev/fd很特别.它的内容对于每个进程都是不同的(因为它们反映了该进程的文件描述符),因此它并不意味着ls可以列出它的任何内容,因为它的内容对于ls您的程序而言是不同的.
无论如何,这是一个稍微更新的程序版本,而不是让ioutil背后的事情,我们自己做,看看发生了什么:
package main
import (
"fmt"
"time"
"os"
"log"
)
func main() {
d, err := os.Open("/dev/fd")
if err != nil {
log.Fatal(err)
}
names, err := d.Readdirnames(0)
if err != nil {
log.Fatal(err)
}
for _, n := range names {
n = "/dev/fd/" + n
fmt.Printf("file: %s\n", n)
_, err := os.Lstat(n)
if err != nil {
fmt.Printf("lstat error: %s - %v\n", n, err)
}
}
time.Sleep(time.Second * 200)
}
Run Code Online (Sandbox Code Playgroud)
然后在运行时它给了我:
file: /dev/fd/0
file: /dev/fd/1
file: /dev/fd/2
file: /dev/fd/3
file: /dev/fd/4
lstat error: /dev/fd/4 - lstat /dev/fd/4: bad file descriptor
Run Code Online (Sandbox Code Playgroud)
所以这确实是同样的问题.我在最后添加了睡眠,以便进程不会死亡,以便我们可以调试它具有哪些文件描述符.在另一个终端:
$ lsof -p 7861
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
foo 7861 art cwd DIR 1,4 2272 731702 /Users/art/src/go/src
foo 7861 art txt REG 1,4 1450576 8591078117 /private/var/folders/m7/d614cd9x61s0l3thb7cf3rkh0000gn/T/go-build268777304/command-line-arguments/_obj/exe/foo
foo 7861 art txt REG 1,4 837248 8590944844 /usr/lib/dyld
foo 7861 art 0u CHR 16,4 0t8129 645 /dev/ttys004
foo 7861 art 1u CHR 16,4 0t8129 645 /dev/ttys004
foo 7861 art 2u CHR 16,4 0t8129 645 /dev/ttys004
foo 7861 art 3r DIR 37,7153808 0 316 /dev/fd
foo 7861 art 4u KQUEUE count=0, state=0x8
Run Code Online (Sandbox Code Playgroud)
我们可以看到fd 4是KQUEUE.Kqueue文件描述符用于管理OSX epoll上的事件,类似于Linux,如果您知道该机制.
似乎OSX不允许stat使用/dev/fd我们可以验证的kqueue文件描述符:
$ cat > foo.c
#include <sys/types.h>
#include <sys/event.h>
#include <sys/stat.h>
#include <stdio.h>
#include <err.h>
int
main(int argc, char **argv)
{
int fd = kqueue();
char path[16];
struct stat st;
snprintf(path, sizeof(path), "/dev/fd/%d", fd);
if (lstat(path, &st) == -1)
err(1, "lstat");
return 0;
}
$ cc -o foo foo.c && ./foo
foo: lstat: Bad file descriptor
$
Run Code Online (Sandbox Code Playgroud)
OSX上的Go程序需要一个kqueue来处理各种事件(不完全确定哪些事件,但可能是信号,定时器和各种文件事件).
你有四个选择:不要统计,忽略错误,不要触摸/ dev/fd因为它很奇怪,说服Apple这是一个bug.
| 归档时间: |
|
| 查看次数: |
396 次 |
| 最近记录: |