dongxinpa3101
2018-01-25 08:18
浏览 175

在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

图片转代码服务由CSDN问答提供 功能建议

我尝试运行以下Go代码:

  package  main 
 
import(
“ fmt” 
“ io / ioutil” 
)
 
func main(){
项目,err:= ioutil.ReadDir(“ / dev / fd”)
  err!= nil {
 panic(err)
} 
 fmt.Println(items)
} 
   
 
 

我只是收到此错误:

  panic:lstat / dev / fd / 4:错误的文件描述符
 
goroutine 1 [正在运行]:
main.main()
 / Users / andy / Desktop / demo  .go:11 + 0xe8 
exit状态2 
   
 
 

肯定存在 / dev / fd 文件夹,并且有一个 / dev / fd / 4 在其中,当我 ls 时。

  $ ls -Al / dev / fd 
总计9 \  ncrw--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根轮4419 Jan 23 20:42 3 / 
dr--r--r-- 1根轮0 Jan 23 20  :42 4 / 
   
 
 

这是怎么回事? 为什么我看不到这个目录? 我正在尝试将 ls 命令移植到此处, 因此我希望能够读取此目录,以便产生与 ls 类似的输出。

编辑: 一切都以非root用户身份进行。

  $ ls -al / dev |  / dev / fd   被设置。  grep fd 
dr-xr-xr-x 1根轮0 Jan 23 20:42 fd / 
 
 $ stat / dev / fd / 4#与-L标志相同的结果
stat:/ dev / fd / 4: 统计信息:错误的文件描述符
   
 
  • 写回答
  • 好问题 提建议
  • 追加酬金
  • 关注问题
  • 邀请回答

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

相关推荐 更多相似问题