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
总结
了解下系统提供的文件操作接口还是不错的,以后遇到问题最起码知道去哪里跟踪。
主要参考:
- linux用户手册
- http://stackoverflow.com/questions/16117610/using-file-descriptors-with-readlink
- http://www.cnblogs.com/carekee/articles/1749655.html
linux下文件描述符的查看及分析的更多相关文章
- Linux下文件描述符
http://blog.csdn.net/kumu_linux/article/details/7877770 文件描述符是一个简单的整数,用以标明每一个被进程所打开的文件和socket.第一个打开的 ...
- linux下文件描述符的介绍
当某个程序打开文件时,操作系统返回相应的文件描述符,程序为了处理该文件必须引用此描述符.所谓的文件描述符是一个低级的正整数.最前面的三个文件描述符(0,1,2)分别与标准输入(stdin),标准输出( ...
- [ 总结 ] Linux 下文件描述符
1.概述: 文件描述符是内核为了高效管理已被打开的文件所创建的索引.是一个非负整数,用于代指被打开的文件.所有通过I/O操作的系统调用都通过文件描述符. 文件描述符用以表明每一个被进程所打开的文件和s ...
- linux 最大文件描述符fd
使用四种框架分别实现百万websocket常连接的服务器 著名的 C10K 问题提出的时候, 正是 2001 年.这篇文章可以说是高性能服务器开发的一个标志性文档,它讨论的就是单机为1万个连接提供服务 ...
- Linux Shell 文件描述符 及 stdin stdout stderr 重定向
Abstract: 1) Linux Shell 命令的标准输入.标准输出.标准错误,及其重定位: 2)Linux Shell 操作自定义文件描述符: 文件描述符是与文件相关联的一些整数,他们保持与已 ...
- 【详解】Linux的文件描述符fd与文件指针FILE*互相转换
使用系统调用的时候用文件描述符(file descriptor,简称fd)的时候比较多,但是操作比较原始.C库函数在I/O上提供了一些方便的包装(比如格式化I/O.重定向),但是对细节的控制不够. 如 ...
- Linux的文件描述符
(1).文件描述符的定义 文件描述符是内核为了高效管理已被打开的文件所创建的索引,用于指向被打开的文件,所有执行I/O操作的系统调用都通过文件描述符:文件描述符是一个简单的非负整数,用以表明每个被进程 ...
- 对于Linux中文件描述符的疑问以及解决
问题 每次web服务器或者是几乎所有Linux服务器都需要对文件描述符进行调整,我使用ulimit -n来查看当前用户的最多能打开的文件,默认设置的是1024个,但是系统运行起来以及开启一些简单的 ...
- Linux中文件描述符fd和文件指针flip的理解
转自:http://www.cnblogs.com/Jezze/archive/2011/12/23/2299861.html 简单归纳:fd只是一个整数,在open时产生.起到一个索引的作用,进程通 ...
随机推荐
- VI 基本可视模式
可视模式让你可以选择文件的一部分内容,以便作比如删除,复制等工作. 进入可视模式 v 用v命令进入可视模式.当光标移动时,就能看到有一些文本被高亮显示了,它们就是被选中的内容. 三种可视模式 v 一个 ...
- Redis学习之路(003)- hiredis安装及测试
一. hiredis下载地址及C API github下载:https://github.com/redis/hiredis 安装脚本: #!/bin/zsh git clone https://g ...
- mysql insert if not exists防止插入重复记录的方法(转)
MySQL 当记录不存在时插入(insert if not exists) 在 MySQL 中,插入(insert)一条记录很简单,但是一些特殊应用,在插入记录前,需要检查这条记录是否已经存在,只有当 ...
- sql 2005出现错误:数据库 'Twitter' 的事务日志已满。若要查明无法重用日志中的空间的原因,请参阅 sys.databases 中的 log_reuse_wait_desc 列。
--先备份数据库 --截断事务日志 backup log Twitter with no_loggo --收缩数据库 dbcc shrinkdatabase(Twitter) go O ...
- HTTP 请求头 Header
HTTP 请求头 Header HTTP请求头概述 (HttpServletRequest) HTTP客户程序(例如浏览器),向服务器发送请求的时候必须指明请求类型(一般是GET或者POST或者HEA ...
- 【MongoDB】MongoDB的安装教程
1,MongoDB简介 MongoDB也是一种数据库,只不过它既不是关系型数据库,也不是非关系型数据库(NoSQL),而是一种介于关系型数据库和NoSQL之间的一种数据库.如果说非关系型数据库是轻量级 ...
- Firefox 网络调试工具
1.Firefox 简介 Firefox 官网下载地址 Firefox 其它下载地址 Firefox 58.0.2 for Mac,密码:346y. Firefox 36.0.4 for Mac,密码 ...
- 【struts2】名为redirect的ResultType
1)基本使用 名称为“redirect”的ResultType,在struts-default.xml里的配置如下: <result-type name="redirect" ...
- Jquery 选择器 详解 js 判断字符串是否包含另外一个字符串
Jquery 选择器 详解 在线文档地址:http://tool.oschina.net/apidocs/apidoc?api=jquery 各种在线工具地址:http://www.ostools ...
- C# 连接 mySQL 出现 GUID 应包含带 4 个短划线的 32 位数 问题
C# 连接 mySQL 出现 GUID 应包含带 4 个短划线的 32 位数 问题 在连接字符串中加入 Old Guids=true; 如:server=localhost;userid=root;p ...