dongxinpa3101 2018-01-25 08:18
浏览 176
已采纳

在MacOS上无法在`/ dev / fd`上调用`ioutil.ReadDir`

I tried to run the following Go code:

package main

import (
    "fmt"
    "io/ioutil"
)

func main() {
    items, err := ioutil.ReadDir("/dev/fd")
    if err != nil {
        panic(err)
    }
    fmt.Println(items)
}

I just get this error:

panic: lstat /dev/fd/4: bad file descriptor

goroutine 1 [running]:
main.main()
    /Users/andy/Desktop/demo.go:11 +0xe8
exit status 2

The /dev/fd folder definitely exists, and there is a /dev/fd/4 inside there when I ls it.

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

What's going on? Why can't I read this directory? I'm trying to port the ls command to Go here, so I would like to be able to read this directory in order to produce similar output to ls.

EDIT: I am running everything as non-root user. The executable bit on /dev/fd is set.

$ 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
  • 写回答

2条回答 默认 最新

  • doulu8537 2018-01-30 13:34
    关注

    First, let's remember that /dev/fd is special. Its contents are different for every process (since they reflect the file descriptors of that process), so it doesn't really mean anything that ls can list it because its contents will be different for ls and your program.

    Anyway, here's a slightly updated version of your program where instead of letting ioutil do things behind our back, we do it ourselves to see what's going on:

    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)
                    _, err := os.Lstat(n)
                    if err != nil {
                            fmt.Printf("lstat error: %s - %v
    ", n, err)
                    }
            }
    
            time.Sleep(time.Second * 200)
    }
    

    And then when run it gives me:

    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
    

    So this is indeed the same problem. I added the sleep at the end so that the process doesn't die so that we can debug which file descriptors it has. In another terminal:

    $ 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
    

    We can see that fd 4 is a KQUEUE. Kqueue file descriptors are used for managing events on OSX, similar to epoll on Linux if you know that mechanism.

    It appears that OSX does not allow stat on kqueue file descriptors in /dev/fd which we can verify with:

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

    Go programs on OSX need a kqueue to handle various events (not exactly sure which, but probably signals, timers and various file events).

    You have four options here: don't stat, ignore the error, don't touch /dev/fd because it's weird, convince Apple that this is a bug.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥20 有偿 写代码 要用特定的软件anaconda 里的jvpyter 用python3写
  • ¥20 cad图纸,chx-3六轴码垛机器人
  • ¥15 移动摄像头专网需要解vlan
  • ¥20 access多表提取相同字段数据并合并
  • ¥20 基于MSP430f5529的MPU6050驱动,求出欧拉角
  • ¥20 Java-Oj-桌布的计算
  • ¥15 powerbuilder中的datawindow数据整合到新的DataWindow
  • ¥20 有人知道这种图怎么画吗?
  • ¥15 pyqt6如何引用qrc文件加载里面的的资源
  • ¥15 安卓JNI项目使用lua上的问题