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\n", __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\n", 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\n", buf_size, errno);
   } 
   else 
        printf("readlink() returned '%s' for '%s'\n", 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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM