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条)

报告相同问题?

悬赏问题

  • ¥15 matlab有关常微分方程的问题求解决
  • ¥15 perl MISA分析p3_in脚本出错
  • ¥15 k8s部署jupyterlab,jupyterlab保存不了文件
  • ¥15 ubuntu虚拟机打包apk错误
  • ¥199 rust编程架构设计的方案 有偿
  • ¥15 回答4f系统的像差计算
  • ¥15 java如何提取出pdf里的文字?
  • ¥100 求三轴之间相互配合画圆以及直线的算法
  • ¥100 c语言,请帮蒟蒻写一个题的范例作参考
  • ¥15 名为“Product”的列已属于此 DataTable