一、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; }
在图形界面的终端里多次改变终端窗口的大小并运行该程序,观察结果。
二、文件的随机读写
到目前为止的所有文件访问都是顺序访问。这是因为所有的读和写都从当前文件的偏移位置开始,然后文件偏移值自动地增加到刚好超出读或写结束时的位置,使它为下一次访问作好准备。
有个文件偏移这样的机制,在Linux系统中,随机访问就变得很简单,你所需做的只是将当前文件偏移值改变到有关的位置,它将迫使下一次read()或write()发生在这一位置。(除非文件打开时标志有 O_APPEND,在这种情况下,任何write调用仍将发生在文件结束处)
lseek系统调用:
功能说明:通过指定相对于开始位置、当前位置或末尾位置的字节数来重定位,这取决于 lseek() 函数中指定的位置
函数原型:off_t lseek (int fd, off_t offset, int base);
函数参数:
fd:需要设置的文件描述符
offset:偏移量
base:偏移基位置
返回值:返回新的文件偏移值
base 表示搜索的起始位置,有以下几个值:(这些值定义在<unistd.h>)
base 文件位置
SEEK_SET 从文件开始处计算偏移
SEEK_CUR 从当前文件的偏移值计算偏移
SEEK_END 从文件的结束处计算偏移
注意:管道和socket是不能lseek的,否则返回ESPIPE错误(Invalid seek)。
创建空洞文件实验
示例程序如下:
#include<sys/types.h> #include<sys/stat.h> #include<unistd.h> #include<fcntl.h> #include<stdio.h> #include<stdlib.h> #include<errno.h> #include<string.h> #define ERR_EXIT(m) do { perror(m); exit(EXIT_FAILURE); } while(0) int main(int argc, char *argv[]) { int infd; int outfd; if (argc != 3) { fprintf(stderr, "Usage %s src dest\n", argv[0]); exit(EXIT_FAILURE); } infd = open(argv[1], O_RDONLY); if (infd == -1) ERR_EXIT("open src error"); if ((outfd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0664)) == -1) ERR_EXIT("open dest error"); char buf[1024]; ssize_t nread; while ((nread = read(infd, buf, 1024)) > 0) write(outfd, buf, nread); // 可以调用fsync同步内核缓冲区的数据到磁盘文件 // 或者打开文件时标志为O_SYNC close(infd); close(outfd); int fd = open("test.txt", O_RDONLY); if (fd == -1) ERR_EXIT("open error"); char buf2[1024] = {0}; int ret = read(fd, buf2, 5); if (ret == -1) ERR_EXIT("read error"); ret = lseek(fd, 0, SEEK_CUR); // 从当前位置偏移0个字节 if (ret == -1) ERR_EXIT("lseek"); printf("current offset=%d\n", ret); fd = open("hole.txt", O_WRONLY | O_CREAT | O_TRUNC, 0664); if (fd == -1) ERR_EXIT("open error"); write(fd, "ABCDE", 5); ret = lseek(fd, 1012 * 1024 * 1024, SEEK_CUR); if (ret == -1) ERR_EXIT("lseek error"); write(fd, "hello", 5); /* 中间的空字符不占用磁盘空间,如ls -lh hole.txt 与 du -h hole.txt * 看到的文件大小不一样*/ close(fd); return 0; }