zoukankan      html  css  js  c++  java
  • linux下文件描述符的查看及分析

    起因

    近期在调试一个Android播放内核是遇到上层传递的是fd(file descriptor),但是在文件播放结束之后调用lseek却提示返回-1,errno=29(#define ESPIPE 29 /* Illegal seek */)。
    好吧。那就确定下原因。
    在网上搜到有说lseek存在问题,“对于已经到达EOF的stream,使用lseek是不能让stream再次可读的”。具体参考Android NDK之fseek, lseek。随即写了个命令行程序,在android shell下验证了下,经过验证是可以的。那就继续找吧。
    最终发现一个有趣的现象,Android的MediaServer传递的fd只能在调用时使用,之后就被复用了,指针都改变了。具体发现的方法就是本文描述的内容。

    文件操作

    文件操作比较通用的就是C库的FILE(带缓冲的文件流),也就是常用的fopen, fclose, fprintf, fscanf, fseek, fread, fwrite等函数。这里面比较核心的概念是FILE结构,这是C库提供的跨平台的文件操作函数,多数情况下是封装了系统内核提供的文件读写函数,比如在windows下是CreateFile, CloseFile, OpenFile, WriteFile, ReadFile等函数,在linux下是open, close, lseek, read, write等内核API。
    在linux下内核API主要提供了基于文件描述(FD,file descriptor)的文件操作机制,注意FD默认是非负的,通常0-stdin、1-stdout、2-stderr。

    先看看如何实现FILE到fd的转换,函数fileno可以实现这种转换,原型如下:

    int fileno(FILE *stream);
    

    那么fd如何转换为FILE呢? 函数fdopen可以基于FD打开文件,原型如下:

    FILE *fdopen(int fd, const char *mode);
    

    那么如何通过fd拿到文件原始路径呢? 函数readlink提供了这种机制,可以参考下面代码

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <climits>
    #include <cstring>
    #include <sys/types.h>
    #include <unistd.h>
    #include <errno.h>
    
    int main()
    {
       FILE * stream = fopen(__FILE__, "rb");
       if (NULL == stream)
       {
            printf("open %s failed
    ", __FILE__);
            return -1;
       }
       
       int fd = fileno(stream);
       char buf[4096] = {0};
       
       // read to file end
       while (read(fd, buf, sizeof(buf)) > 0);
          
       // test whether lseek is ok in EOF
       off_t offset = lseek(fd, 0, SEEK_CUR);  
       printf("lseek ret %d err_no %d
    ", offset, errno);
       
       // read file path from fd
       char path[PATH_MAX] = {0};
       snprintf(path, sizeof(path), "/proc/%d/fd/%d", getpid(), fd);
       
       memset(buf, 0, sizeof(buf));
       int buf_size = readlink(path, buf, sizeof(buf));
       if (buf_size < 0) 
       {
            printf("readlink() ret %d error %d
    ", buf_size, errno);
       } 
       else 
            printf("readlink() returned '%s' for '%s'
    ", buf, path);
       
       getchar();
       
       if (NULL != stream)
            fclose(stream);
       
       return 0;
    }
    

    原理很简单,linux下的fd就是一个链接,可以通过/proc/pid/fd读取到相关信息。
    比如上面那个程序的输出如下:

    /proc/11203/fd$ ll
    总用量 0
    dr-x------ 2 root root  0  4月  1 15:48 ./
    dr-xr-xr-x 9 root root  0  4月  1 15:48 ../
    lrwx------ 1 root root 64  4月  1 15:48 0 -> /dev/pts/22
    lrwx------ 1 root root 64  4月  1 15:48 1 -> /dev/pts/22
    lrwx------ 1 root root 64  4月  1 15:48 2 -> /dev/pts/22
    lr-x------ 1 root root 64  4月  1 15:48 3 -> /home/tocy/project/test.cpp
    

    总结

    了解下系统提供的文件操作接口还是不错的,以后遇到问题最起码知道去哪里跟踪。

    主要参考:

    1. linux用户手册
    2. http://stackoverflow.com/questions/16117610/using-file-descriptors-with-readlink
    3. http://www.cnblogs.com/carekee/articles/1749655.html
  • 相关阅读:
    路径变量@PathVariable/请求参数@RequestParam的绑定以及@RequestBody
    JSR303后端校验详细笔记
    创建ssm项目步骤
    利用 R 绘制拟合曲线
    在 Linux 中将 Caps 根据是否为修饰键分别映射到 esc 和 Ctrl
    Master Transcription Factors and Mediator Establish Super-Enhancers at Key Cell Identity Genes
    Genomic Evidence for Complex Domestication History of the Cultivated Tomato in Latin America
    Variation Revealed by SNP Genotyping and Morphology Provides Insight into the Origin of the Tomato
    The genetic, developmental, and molecular bases of fruit size and shape variation in tomato
    微信支付jsapi
  • 原文地址:https://www.cnblogs.com/tocy/p/linux-file-descriptor-intro.html
Copyright © 2011-2022 走看看