说明
参考:《UNIX环境高级编程》中文版第三版
编程环境:OS X 10.11.5 Xcode 7.3.1
文件I/O
下面介绍各类文件I/O操作。
#include <fcntl.h>
int open(const char * path, int oflag, ...);
int openat(int fd, const char * path, int oflag, ...);
用于打开或者创建文件(创建文件时需要注意flag必须有O_CREAT这个选项,并指定访问权限(通过后面的不定参数)),成功返回文件描述符,它是一个非负整数,失败返回-1。
path是需要打开或者创建的文件的名字。如果path是绝对路径,open()和openat()没有区别,openat()中的fd参数无效。如果path是相对路径,对于openat(),fd参数指定了起始路径,fd也是个文件描述符,它通过打开相对路径所在的目录来获得;对于open(),则指定当前目录为起始路径。
oflag是一系列的选项,确定打开方式,如只读、只写、读写等等:
#define O_ACCMODE 0003
#define O_RDONLY 00
#define O_WRONLY 01
#define O_RDWR 02
#define O_CREAT 0100 /* not fcntl */
#define O_EXCL 0200 /* not fcntl */
#define O_NOCTTY 0400 /* not fcntl */
#define O_TRUNC 01000 /* not fcntl */
#define O_APPEND 02000
#define O_NONBLOCK 04000
#define O_NDELAY O_NONBLOCK
#define O_SYNC 010000
#define FASYNC 020000 /* fcntl, for BSD compatibility */
#define O_DIRECT 040000 /* direct disk access hint */
#define O_LARGEFILE 0100000
#define O_DIRECTORY 0200000 /* must be a directory */
#define O_NOFOLLOW 0400000 /* don't follow links */
#define O_NOATIME 01000000
下面是一个例子:
#include <stdio.h>
#include <fcntl.h>
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
int main()
{
int fd = 0;
if( (fd = open("open_test", O_RDWR | O_CREAT, FILE_MODE)) != -1 )
{
printf("open file fd: %d\n", fd);
}
else
{
printf("open file failed\n");
}
return 0;
}
执行结果:
open file fd: 3
Program ended with exit code: 0
上例中创建文件的方式可以用另外一个函数来代替:
#include <fcntl.h>
int creat(const char * path, mode_t mode);
成功时返回文件描述符,失败时返回-1。它相当于:
open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);
关于mode_t,它表示的是文件的访问权限,有如下的值:
/* File mode */
/* Read, write, execute/search by owner */
#define S_IRWXU 0000700 /* [XSI] RWX mask for owner */
#define S_IRUSR 0000400 /* [XSI] R for owner */
#define S_IWUSR 0000200 /* [XSI] W for owner */
#define S_IXUSR 0000100 /* [XSI] X for owner */
/* Read, write, execute/search by group */
#define S_IRWXG 0000070 /* [XSI] RWX mask for group */
#define S_IRGRP 0000040 /* [XSI] R for group */
#define S_IWGRP 0000020 /* [XSI] W for group */
#define S_IXGRP 0000010 /* [XSI] X for group */
/* Read, write, execute/search by others */
#define S_IRWXO 0000007 /* [XSI] RWX mask for other */
#define S_IROTH 0000004 /* [XSI] R for other */
#define S_IWOTH 0000002 /* [XSI] W for other */
#define S_IXOTH 0000001 /* [XSI] X for other */
关闭文件使用的函数:
#include <unistd.h>
int close(int fd);
需要注意使用了不同的头文件。成功时返回0,出错时返回-1。
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
lseek()显式地为一个打开文件设置偏移量。
通常,读写操作都从当前文件偏移量开始。
默认情况下,当打开一个文件时,除非指定了O_APPEND选项,否则该偏移量被设置为0。
fd表示文件描述符,对应特定的文件。
offset和whence配合使用:
1)whence是SEEK_SET时,设置偏移量为从文件开始处offset个字节;
2)whence是SEEK_CUR时,设置偏移量为从文件当前偏移位置开始处offset个字节,offset可正可负;
3)whence是SEEK_END时,设置偏移量为文件长度加offset个字节,offset可正可负。
成功时,返回新的文件偏移量;出错时返回-1。
下面是获取当前文件偏移量的一个方法:
off_t offset = lseek(fd, 0, SEEK_CUR);
printf("offset : %lld\n", offset);
注意,lseek只负责记录文件偏移量,并不会引起任何的I/O操作。
下面两个函数才是具体的I/O操作:
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t nbytes);
ssize_t write(int fd, const void *buf, size_t nbytes);
read()成功返回读到的字节,它小于等于nbytes;失败返回-1。
write()成功返回写入的字节;失败返回-1。
一个完整的示例:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
int main()
{
int fd;
off_t offset;
char *tmp = "hello world";
char s[20] = {0};
if( (fd = open("open_test", O_RDWR | O_CREAT, FILE_MODE)) != -1 )
{
printf("open file fd: %d\n", fd);
}
else
{
printf("open file failed\n");
return 1;
}
offset = lseek(fd, 0, SEEK_CUR);
printf("offset : %lld\n", offset);
if( 12 == write(fd, tmp, 12))
{
printf("write file success\n");
}
if ( (0 == lseek(fd, 0, SEEK_SET)) && (read(fd, &s, 100) != -1) )
{
printf("read from file: %s\n", s);
}
close(fd);
return 0;
}
结果:
open file fd: 3
offset : 0
write file success
read from file: hello world
Program ended with exit code: 0
#include <unistd.h>
int dup(int fd);
int dup2(int fd, int fd2);
复制一个现有的文件描述符。成功则返回新的文件描述符,失败则返回-1。
对于dup2(),如果fd2已经打开,则先关闭;如果fd等于fd2,则dup2()返回fd2,而不是关闭它。
下面的例子复制了标准输出,之后再使用write写文件的话,相当于写到了标准输出上:
#include <stdio.h>
#include <unistd.h>
int main()
{
int tmp = dup(STDOUT_FILENO);
write(tmp, "Hello world\n", 12);
return 0;
}
#include <fcntl.h>
int fcntl(int fd, int cmd, ...);
用于改变已经打开的文件的属性。
根据cmd的不同,fcntl()函数有不同的功能:
1)复制已有的文件描述符,功能同dup、dup2;
2)获取/设置文件描述符标志;
3)获取/设置文件状态标志;
4)获取/设置异步I/O所有权;
5)获取/设置记录锁。
#include <unistd.h>
#include <sys/ioctl.h>
int ioctl(int fd, int request, ...);
这个函数包含其他函数无法应用到的I/O操作。这里不特别介绍了。