Linux中文件I/O函数
Posted XNQC
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux中文件I/O函数相关的知识,希望对你有一定的参考价值。
一、lseek函数
每个打开文件都有一个与其相关联的“当前文件偏移量”。它通常是一个非负整数,用以度量从文件开始处
计算的字节数。通常,读、写操作都从当前文件偏移量处开始,并使偏移量增加所读写的字节数。当打开一个文
件时,除非指定O_APPEND选项(调用open函数时使用了O_APPEND),否则该偏移量被设置为0。调用lseek函数显
式地为一个打开文件设置偏移量。该函数原型为:
#include <unistd.h> off_t lseek(int fd,off_t offset,int whence);
该函数若成功则返回新的文件偏移量,若出错返回-1。参数fd为打开的文件描述符,参数offset与参数whence
的值有关:
- 若whence是SEEK_SET,则将该文件的偏移量设置为距文件开始处offset个字节。
- 若whence是SEEK_CUR,则将该文件的偏移量设置为其当前值加offset,offset可为正为负。
- 若whence是SEEK_END,则将该文件的偏移量设置为文件长度加offset,offset可正可负。
若lseek成功执行,则返回新的文件偏移量。如果文件描述符指向的是一个管道、FIFO或网络套接字,则lseek
返回-1,并将errno设置为ESPIPE。
二、dup和dup2函数
dup和dup2系统调用都可以用来复制文件描述符,函数的原型为:
#include <unistd.h> int dup(int fd); int dup2(int fd,int fd2);
函数成功执行返回新的文件描述符,若出错返回-1。由dup返回的新文件描述符一定是当前可用文件描述符中的
最小数值。对于dup2,可以用fd2参数指定新的文件描述符的值。如果fd2已经打开,则先将其关闭。若fd等于fd2,
则dup2返回fd2,而不关闭它。否则,fd2的FD_CLOEXEC文件描述符标志就被清除,这样fd2在进程调用exec时是打
开状态。
dup(fd);等效于fcntl(fd,F_DUPFD,0); dup2(fd,fd2);等效于close(fd2);fcntl(fd,F_DUPFD,fd2);
而dup2并不完全等同于close加上fcntl。它们之间的区别有:
(1)dup2是一个原子操作,而close和fcntl包括了两个函数调用。有可能在close和fcntl之间调用了信号捕获
函数,它可能修改文件描述符。如果不同的线程改变了文件描述符的话也会出现同样的问题。
(2)dup2和fcntl有一些不同的errno。
三、fcntl函数
fcntl系统调用可以用来对已经打开的文件描述符进行各种控制操作以改变已打开文件的各种属性,该函数的
原型为:
#include <unistd.h> #include <fcntl.h> int fcntl(int fd,int cmd,.../*int arg*/);
fcntl函数有以下5种功能:
(1)复制一个已有的文件描述符(cmd=F_DUPFD或F_DUPFD_CLOEXEC)。
(2)获取/设置文件描述符标志(cmd=F_GETFD或F_SETFD)。
(3)获取/设置文件状态标志(cmd=F_GETFL或F_SETFL)。
(4)获取/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN)。
(5)获取/设置记录锁(cmd=F_GETLK、F_SETLK或F_SETLKW)。
F_DUPFD:复制文件描述符fd。新文件描述符作为函数值返回。它是尚未打开的各描述符中大于或等于第3个
参数值(取为整型值)中各值的最小值。新文件描述符与fd共享同一文件表项。但是新的文件描述符有它自己的
一套文件描述符标志,其FD_CLOEXEC文件描述符标志被清除(这表示该文件描述符在exec时仍保持有效)。
F_DUPFD_CLOEXEC:复制文件描述符,设置与新的文件描述符关联的FD_CLOEXEC文件描述符标志的值,返回
新的文件描述符。
F_GETFD:对应于fd的文件描述符标志作为函数值返回当前只定义了一个文件描述符标志FD_CLOEXEC。
F_SETFD:对于fd设置文件描述符标志。新标志值按第3个参数(取为整型值)设置。
F_GETFL:对应于fd的文件状态标志作为函数值返回。
F_SETFL:将文件状态标志设置为第3个参数的值(取为整型值)。可以更改的几个标志为:O_APPEND、O_NO
NBLOCK、O_SYNC、O_DSYNC、O_RSYNC、O_FSYNC和O_ASYNC。
F_GETOWN:获取当前接收SIGIO和SIGURG信号的进程ID或进程组ID。
F_SETOWN:设置接收SIGIO和SIGURG信号的进程ID或进程组ID。正的arg指定一个进程ID,负的arg表示等于
arg绝对值的一个进程组ID。
fcntl的返回值与命令有关。如果出错,所有命令都返回-1,如果成功则返回某个其它值。F_DUPFD返回新的
文件描述符,F_GETFD和F_GETFL返回相应的标志,F_GETOWN返回一个正的进程ID或负的进程组ID。
四、ioctl函数
ioctl系统调用通常用来控制设备,不能用其它函数进行的控制操作都可以用ioctl来进行,该函数原型为:
#include <unistd.h> #include <sys/ioctl.h> int ioctl(int fd,int request,...);
ioctl用来控制特殊设备文件的属性,第一个参数fd必须是一个已经打开的文件描述符,第三个参数一般为
char *argp,它随第二个参数request的不同而不同。参数request决定了参数argp是向ioctl传递数据还是从io
ctl获取数据。
五、readv和writev函数
readv函数将数据从文件描述符读到分散的内存块中,即分散读;writev函数则将多块分散的内存数据一并
写入文件描述符中,即集中写。函数的原型为:
#include <sys/uio.h> ssize_t readv(int fd,const struct iovec *iov,int count); ssize_t writev(int fd,const struct iovec *iov,int count);
函数中fd参数是被操作的目标文件描述符。iov参数的类型是iovec结构数组,该结构体描述一块内存区。参
数count是iov数组的长度,即有多少块内存数据需要从fd读出或写到fd。readv和writev函数在成功时返回读出/
写入fd的字节数,失败则返回-1并设置errno。
六、sendfile函数
sendfile函数在两个文件描述符之间直接传递数据(完全在内核中操作),从而避免了内核缓冲区和用户缓
冲区之间的数据拷贝,效率很高,被称为零拷贝。该函数的原型为:
#include <sys/sendfile.h> ssize_t sendfile(int out_fd,int in_fd,off_t *offset,size_t count);
函数中in_fd参数是待读出内容的文件描述符,out_fd参数是待写入内容的文件描述符。offset参数指定从读
入文件流的哪个位置开始读,如果为空,则使用读入文件流默认的起始位置。count参数指定在文件描述符in_fd
和out_fd之间传输的字节数。sendfile函数成功时返回传输的字节数,失败则返回-1并设置errno。in_fd必须是一
个支持类似mmap函数的文件描述符,即它必须指向真实的文件,不能是socket和管道;而out_fd则必须是一个soc
ket。因此,sendfile函数几乎是专门为在网络上传输文件而设计的。
七、mmap和munmap函数
mmap函数用于申请一段内存空间,可以将这段内存空间作为进程间通信的共享内存,也可以将文件直接映射
到其中。munmap函数则释放由mmap创建的这段内存空间。函数原型为:
#include <sys/mman.h> void* mmap(void *start,size_t length,int prot,int flags,int fd,off_t offset); int munmap(void *start,size_t length);
函数中start参数允许用户使用某个特定的地址作为这段内存的起始地址,如果它被设置成NULL,则系统自动
分配一个地址。length参数指定内存段的长度。prot参数用来设置内存段的访问权限,它可以取以下几个值的按位
或:(1)PROT_READ:内存段可读。(2)PROT_WRITE:内存段可写。(3)PROT_EXEC:内存段可执行。(4)PRO
T_NONE:内存段不能被访问。
flags参数控制内存段内容被修改后程序的行为,它可以被设置为某些值的按位或(其中MAP_SHARED和MAP_PRIVATE
是互斥的,不能同时指定)。flags参数的常用值有:MAP_SHARED、MAP_PRIVATE、MAP_ANONYMOUS、MAP_FIXED、MA
P_HUGETLB。fd参数是被映射文件对应的文件描述符,它一般通过open系统调用获得。offset参数设置从文件的何
处开始映射(对于不需要读入整个文件的情况)。
mmap函数成功时返回指向目标内存区域的指针,失败则返回MAP_FAILED((void*)-1)并设置errno。munmap函数
成功时返回0,失败则返回-1并设置errno。
八、splice函数
splice函数用于在两个文件描述符之间移动数据,也是零拷贝操作。该函数的原型为:
#include <fcntl.h> ssize_t splice(int fd_in,loff_t *off_in,int fd_out,loff_t *off_out,size_t len,unsigned int flags);
函数中fd_in参数是待输入数据的文件描述符,如果fd_in是一个管道文件描述符,那么off_in参数必须被设置
为NULL,如果fd_in不是一个管道文件描述符,那么off_in表示从输入数据流的何处开始读取数据,此时若off_in
被设置为NULL,则表示从输入数据流的当前偏移位置读入;若off_in不为NULL,则它将指出具体的偏移位置。fd_o
ut/off_out参数的含义与fd_in/off_in相同,不过用于输出数据流。len参数指定移动数据的长度;flags参数则控
制数据如何移动,它可以被设置为某些值的按位或,常用值为:SPLICE_F_MOVE、SPLICE_F_NONBLOCK、SPLICE_F_
MORE、SPLICE_F_GIFT。使用splice函数时,fd_in和fd_out必须至少有一个是管道文件描述符。splice函数调用成
功时返回移动字节的数量,它可能返回0,表示没有数据需要移动,这发生在从管道中读取数据(fd_in是管道文件
描述符)而该管道没有被写入任何数据时。splice函数失败时返回-1并设置errno。
九、tee函数
tee函数在两个管道文件描述符之间复制数据,也是零拷贝操作。它不消耗数据,因此源文件描述符上的数据仍
然可以用于后续的读操作。该函数原型为:
#include <fcntl.h> ssize_t tee(int fd_in,int fd_out,size_t len,unsigned int flags);
该函数的参数的含义与splice相同(但fd_in和fd_out必须都是管道文件描述符)。tee函数成功时返回在两个文
件描述符之间复制的数据数量(字节数)。返回0表示没有复制任何数据。tee失败时返回-1并设置errno。
以上是关于Linux中文件I/O函数的主要内容,如果未能解决你的问题,请参考以下文章