Linux文件I/O的lseek,fcntl和ioctl函数
2015-09-29 10:35
726 查看
lseek函数
每个打开的文件都记录着当前读写位置,打开文件时读写位置是0,表示文件开头,通常读写多少个字节就会将读写位置往后移多少个字节。但是有一个例外,如果以O_APPEND方式打开,每次写操作都会在文件末尾追加数据,然后将读写位置移到新的文件末尾。lseek和标准I/O库的fseek函数类似,可以移动当前读写位置(或者叫偏移量)。#include <sys/types.h> #include <unistd.h> off_t lseek(int fd, off_t offset, int whence);
参数offset和whence的含义和fseek函数完全相同。只不过第一个参数换成了文件描述符。和fseek一样,偏移量允许超过文件末尾,这种情况下对该文件的下一次写操作将延长文件,中间空洞的部分读出来都是0。
若lseek成功执行,则返回新的偏移量,因此可用以下方法确定一个打开文件的当前偏移量:
off_t currpos; currpos = lseek(fd, 0, SEEK_CUR);
这种方法也可用来确定文件或设备是否可以设置偏移量,常规文件都可以设置偏移量,而设备一般是不可以设置偏移量的。如果设备不支持lseek,则lseek返回-1,并将errno设置为ESPIPE。注意fseek和lseek在返回值上有细微的差别,fseek成功时返回0失败时返回-1,要返回当前偏移量需调用ftell,而lseek成功时返回当前偏移量失败时返回-1。
#include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> #include <errno.h> #include <stdlib.h> #include <unistd.h> int main(void) { int fd = open("aaa", O_RDWR); if (fd < 0) { perror("aaa"); exit(1); } //拓展一个文件,一定要有一次写的操作 lseek(fd, 0x1000, SEEK_SET); write(fd, "c", 1); close(fd); fd = open("hello", O_RDWR); if (fd < 0) { perror("hello"); exit(1); } //求文件的大小 printf("hello size:%d\n", lseek(fd, 0, SEEK_END)); close(fd); return 0; }
fcntl函数
先前我们以read终端设备为例介绍了非阻塞I/O,为什么我们不直接对STDIN_FILENO做非阻塞read,而要重新open一遍/dev/tty呢?因为STDIN_FILENO在程序启动时已经被自动打开了,而我们需要在调用open时指定O_NONBLOCK标志。这里介绍另外一种办法,可以fcntl函数改变一个已打开的文件的属性,可以重新设置读、写、追加、非阻塞等标志(这些标志称为File Status Flag),而不必重新open文件。#include <unistd.h> #include <fcntl.h> int fcntl(int fd, int cmd); int fcntl(int fd, int cmd, long arg); int fcntl(int fd, int cmd, struct flock *lock);
这个函数和open一样,也是用可变参数实现的,可变参数的类型和个数取决于前面的cmd参数。下面的例子使用F_GETFL和F_SETFL这两种fcntl命令改变STDIN_FILENO的属性,加上O_NONBLOCK选项,实现和例 28.3 “非阻塞读终端”同样的功能。
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #define MSG_AGN "try again\n" int main(void) { char buf[10] = {0}; int n = 0; int flags = 0; flags = fcntl(STDIN_FILENO, F_GETFL); flags |= O_NONBLOCK; if (fcntl(STDIN_FILENO, F_SETFL, flags) == -1) { perror("fcntl"); exit(1); } tryagain: n = read(STDIN_FILENO, buf, 10); if (n < 0) { if (errno == EAGAIN) { sleep(5); write(STDOUT_FILENO, MSG_AGN, strlen(MSG_AGN)); goto tryagain; } printf("errno num: %d\n", errno); perror("read stdin"); exit(1); } write(STDOUT_FILENO, buf, n); printf("\n"); close(flags); return 0; }
ioctl函数
ioctl用于向设备发控制和配置命令,有些命令也需要读写一些数据,但这些数据是不能用read/write读写的,称为Out-of-band数据。也就是说,read/write读写的数据是in-band数据,是I/O操作的主体,而ioctl命令传送的是控制信息,其中的数据是辅助的数据。例如,在串口线上收发数据通过read/write操作,而串口的波特率、校验位、停止位通过ioctl设置,A/D转换的结果通过read读取,而A/D转换的精度和工作频率通过ioctl设置。#include <sys/ioctl.h> int ioctl(int d, int request, ...);
d是某个设备的文件描述符。request是ioctl的命令,可变参数取决于request,通常是
一个指向变量或结构体的指针。若出错则返回-1,若成功则返回其他值,返回值也是取决于request。
代码:使用TIOCGWINSZ命令获得终端设备的窗口大小
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/ioctl.h> int main(void) { struct winsize size; if (isatty(STDOUT_FILENO) == 0) { exit(1); } if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &size) < 0) { perror("ioctl TIOCGWINSZ error"); exit(1); } printf("%d rows, %d columns\n", size.ws_row, size.ws_col); return 0; }
相关文章推荐
- ubuntu中sudo man的tab自动补齐
- linux 添加用户、权限
- linux 防火墙arp问题
- linux上SVN解决冲突命令
- arm+linux交叉编译libxml2
- 双系统卸载linux和装双系统的方法
- Linux 网桥配置命令:brctl
- Linux中find常见用法示例
- Linux swap分区
- 用xmanager登陆Linux图形界面
- CentOS7升级内核到3.12.48
- 修改Linux最大连接数
- Linux 检测网卡与网线连接状态
- Linux strace命令
- error while loading shared libraries: libssl.so.4问题解决方法
- 每天一个linux命令(9):touch 命令
- 给新手的 10 个有用 Linux 命令行技巧
- centos 阶段复习 2015-4-6 dd命令 hosts.allow和hosts.deny 啊铭的myssh脚本 清空history命令历史 /dev/zero 零发生器 /dev/null 黑洞 /dev/random 生成随机数 第十一节课
- 从零自学Hadoop(04):Linux准备下
- Linux Objdump Command Examples (Disassemble a Binary File)