起因

近期在调试一个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

linux下文件描述符的查看及分析的更多相关文章

  1. Linux下文件描述符

    http://blog.csdn.net/kumu_linux/article/details/7877770 文件描述符是一个简单的整数,用以标明每一个被进程所打开的文件和socket.第一个打开的 ...

  2. linux下文件描述符的介绍

    当某个程序打开文件时,操作系统返回相应的文件描述符,程序为了处理该文件必须引用此描述符.所谓的文件描述符是一个低级的正整数.最前面的三个文件描述符(0,1,2)分别与标准输入(stdin),标准输出( ...

  3. [ 总结 ] Linux 下文件描述符

    1.概述: 文件描述符是内核为了高效管理已被打开的文件所创建的索引.是一个非负整数,用于代指被打开的文件.所有通过I/O操作的系统调用都通过文件描述符. 文件描述符用以表明每一个被进程所打开的文件和s ...

  4. linux 最大文件描述符fd

    使用四种框架分别实现百万websocket常连接的服务器 著名的 C10K 问题提出的时候, 正是 2001 年.这篇文章可以说是高性能服务器开发的一个标志性文档,它讨论的就是单机为1万个连接提供服务 ...

  5. Linux Shell 文件描述符 及 stdin stdout stderr 重定向

    Abstract: 1) Linux Shell 命令的标准输入.标准输出.标准错误,及其重定位: 2)Linux Shell 操作自定义文件描述符: 文件描述符是与文件相关联的一些整数,他们保持与已 ...

  6. 【详解】Linux的文件描述符fd与文件指针FILE*互相转换

    使用系统调用的时候用文件描述符(file descriptor,简称fd)的时候比较多,但是操作比较原始.C库函数在I/O上提供了一些方便的包装(比如格式化I/O.重定向),但是对细节的控制不够. 如 ...

  7. Linux的文件描述符

    (1).文件描述符的定义 文件描述符是内核为了高效管理已被打开的文件所创建的索引,用于指向被打开的文件,所有执行I/O操作的系统调用都通过文件描述符:文件描述符是一个简单的非负整数,用以表明每个被进程 ...

  8. 对于Linux中文件描述符的疑问以及解决

    问题 ​ 每次web服务器或者是几乎所有Linux服务器都需要对文件描述符进行调整,我使用ulimit -n来查看当前用户的最多能打开的文件,默认设置的是1024个,但是系统运行起来以及开启一些简单的 ...

  9. Linux中文件描述符fd和文件指针flip的理解

    转自:http://www.cnblogs.com/Jezze/archive/2011/12/23/2299861.html 简单归纳:fd只是一个整数,在open时产生.起到一个索引的作用,进程通 ...

随机推荐

  1. MATLAB 向量

    MATLAB 向量: 1.MATLAB 行向量: 创建行向量括在方括号中的元素的集合,用空格或逗号分隔的元素. 2.MATLAB 列向量: 创建列向量括在方括号中的元素的集合,使用分号来分隔的元素. ...

  2. struts2基本配置详解2

    接上篇struts2基本配置详解,还有一些配置没有讲到,下面将继续. struts.xml <package name="com.amos.web.action" names ...

  3. Linux下mysql的远程连接(转)

    转载:http://www.cnblogs.com/fnlingnzb-learner/p/5830661.html 如果Mysql是按上篇的方法进行安装和设置的话,那进行远程连接就会稍微简单一点.我 ...

  4. windbg-.foreach循环输入(windbg script)

    .foreach .foreach 关键字分析一个或多个命令的输出并将该输出中每一个值作为另一个或多个命令的输入 .foreach [Options] ( Variable  { InCommands ...

  5. Rust 之 cargo(项目构建和包管理工具)

    如果食用cargo来进行项目构建: 1. 执行 cargo new hello_cargo --bin ,执行完上面的操作之后,我们切换到hell_cargo目录下,可以看到一个文件(Cargo.to ...

  6. magento登陆

    magento判断用户登录 Magento 登陆之后返回登录之前的页面 magento 在登陆后一般会自动跳转到 My Account 页面 但是经常会有需求 就是登陆自动跳转到 之前的页面里面 工具 ...

  7. jQuery UI API - 可拖拽小部件(Draggable Widget)(转)

    所属类别 交互(Interactions) 用法 描述:允许使用鼠标移动元素. 版本新增:1.0 依赖: UI 核心(UI Core) 部件库(Widget Factory) 鼠标交互(Mouse I ...

  8. 【C#】反编译C#应用程序

    下面的使用的使用是笔者使用的一个简单的程序,就是弹出一个消息框. 这里通过 ildasm 反编译 应用程序得到 il 文件,然后对 il 文件进行修改,修改内容后,再通过ilasm编译为应用程序.il ...

  9. libevent的问题

    问题: nginx error while loading shared libraries: libevent-2.0.so.5: cannot open shared object file: N ...

  10. MySQL Cluster

    MySQL Cluster MySQL集群一个非共享(shared nothing).分布式.分区系统,使用同步复制机制提供高可用和高性能. MySQL集群使用的是NDB引擎.NDB存储引擎会在节点间 ...