1、系统限制与功能选项
编译时限制定义于/usr/include/sys/limits.h
如:CHAR_BIT //char类型包含几个bit
编译时选项定义于/usr/include/unistd.h
如:_POSIX_JOB_CONTROL //是否支持任务控制
long sysconf(int name) //形如_SC_OPEN_MAX
long pathconf(const char *pathname,int name)
long fpathconf(int fildes,int name) //形如_PC_PATH_MAX
2、函数open() --打开或创建文件
O_RDONLY //只读
O_WRONLY //只写
O_RDWR //读写
其他常用选项
O_APPEND //追加
O_CREAT //文件不存在时创建
O_EXCL //如果同时指定O_CREAT且文件已存在,出错
O_TRUNC //截断,文件内容清空
3、函数creat() --创建文件
open (pathname, O_WRONLY | O_CREAT | O_TRUNC, mode);
通过creat()创建的文件是“只写”的
建议使用上面等价的open语句创建文件
4、函数close() --关闭文件
5、函数lseek() --偏移定位
SEEK_SET //相对于文件开头,设置
SEEK_CUR //相对于当前偏移,offset可正可负
SEEK_END //相对于文件结尾,offset可正可负
#include <stdio.h>
#include <fcntl.h>
char buf1[] = "abcdefghij";
char buf2[] = "ABCDEFGHIJ";
int main(void)
{
int fd;
/* 创建文件 */
if( (fd = creat("file.hole", 0777)) < 0 )
{
perror("creat");
exit(1);
}
/* 写入10个字节 */
if( write(fd, buf1, 10) != 10 )
{
perror("buf1 write");
exit(1);
}
/* offset now = 10 */
/* 设置文件偏移,越过文件结尾
* 因为位移量可能是负值,所以在比较 lseek的返回值时应当谨慎,
* 不要测试它是否小于0,而要测试它是否等于-1
*/
if( lseek(fd, 16384, SEEK_SET) == -1 )
{
perror("lseek");
exit(1);
}
/* offset now = 16384 */
/* 再次写入10个字节 */
if( write(fd, buf2, 10) != 10 )
{
perror("buf2 write");
exit(1);
}
/* offset now = 16394 */
exit(0);
}
6、函数read() --读取数据
读取普通文件时,且在读取指定字节数之前已到达文件结尾;
读取终端设备时,通常每次只能读取一行;
从网络读取时,缓冲机制的作用;
读取管道或FIFO时,且管道中数据少于指定读取的字节数;
读取面向记录的设备时,例如磁带,每次只能读取一个记录大小;
读取部分数据后被信号中断;
7、函数write() --写入数据
常见原因是:磁盘已满,或者超过当前进程的文件长度限制
#include <stdio.h>
#include <unistd.h>
int main(void)
{
int n;
char buf[BUFSIZ];
/* 读取标准输入 */
while( (n = read(STDIN_FILENO, buf, BUFSIZ)) > 0 )
{
/* 写入标准输出 */
if( write(STDOUT_FILENO, buf, n) != n )
{
perror("write");
exit(-1);
}
}
if( n < 0 )
{
perror("read");
}
exit(0);
}
使用文件系统的块大小(通常为4096或8192)
具体数字在后面章节描述
测试数据略
9、文件共享
内核中关于打开文件的数据结构
如果当前偏移大于文件大小,V-节点表中文件大小设置为当前偏移
文件表的文件状态标志中设置相应标志
写入之前,文件表中当前偏移首先被设置为V-节点表中当前文件大小
仅仅将文件表中当前偏移设置为V-节点表中当前文件大小
注意:与O_APPEND选项打开文件不同
lseek(fd, 0L, SEEK_END); //position to EOF
write(fd,buf, 100); //and write
存在问题:并非原子操作
ssize_t pread(intfildes, void *buf,size_t nbytes,off_t offset);
原子操作,并不移动当前文件偏移
if( open(…) < 0 )creat(…); //存在问题:并非原子操作
正确做法示例:open (…, O_WRONLY | O_CREAT | O_EXCL, mode);
dup返回当前可用的最小描述符;
dup2可以指定目标描述符fildes2,如果fildes2已经打开,它将首先被关闭(fildes与fildes2相等时除外)
调用返回后,存在两个等价的文件描述符
dup与dup2对于实现输入/输出重定向非常有用
sync仅对修改过的内核缓冲区排队,并不等待数据写入磁盘
fsync只作用于单个文件,等待数据确实写入磁盘后返回
fdatasync与fsync类似,但是只影响文件的数据部分,并不维护文件属性
13、函数fcntl() --修改文件特性
复制描述符,cmd = F_DUPFD,与dup()类似
获取/设置描述符标志,F_GETFD / F_SETFD
获取/设置状态标志,F_GETFL / F_SETFL
获取/设置异步I/O属主,F_GETOWN / F_SETOWN
获取/设置记录锁,F_GETLK / F_SETLK / F_SETLKW
#include <stdio.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
int val;
if( argc != 2 )
{
printf("usage: %s <descriptor#>\n", argv[1]);
exit(0);
}
/* 获取文件状态标志 */
if( (val = fcntl(atoi(argv[1]), F_GETFL, 0)) < 0 )
{
printf("fcntl error for fd %d\n", atoi(argv[1]));
exit(0);
}
/* 检查访问模式 */
switch( val & O_ACCMODE )
{
case O_RDONLY:
printf("read only");
break;
case O_WRONLY:
printf("write only");
break;
case O_RDWR:
printf("read write");
break;
default:
printf("unknown access mode");
break;
}
/* 检查其他标志 */
if( val & O_APPEND )
printf(", append");
if( val & O_NONBLOCK )
printf(", nonblocking");
#if defined(O_SYNC)
if( val & O_SYNC )
printf(", synchronous writes");
#endif
#if !defined(_POSIX_C_SOURCE) && defined(O_FSYNC)
if( val & O_FSYNC )
printf(", synchronous writes");
#endif
putchar('\n');
exit(0);
}
/*
执行示例:
$ ./a.out 0 < /dev/tty
read only
$ ./a.out 1 > temp.foo
$ cat temp.foo
write only
$ ./a.out 2 2>>temp.foo
write only, append
$ ./a.out 5 5<>temp.foo
read write
*/
不能用本章中其他函数表示的I/O操作通常都能用ioctl表示
终端I/O是ioctl的最大用途
fd =open("/dev/fd/0", mode);
等价于
fd =dup(0);
cat file1 /dev/fd/0 >file.out