unix编程基础(2)--文件IO

Unix 文件IO

文件描述符号

对于内核而言,所有打开的文件通过文件描述符引用,是一个非负整数.
在POSIX1中,在<unistd.h>中定义了
STDIN_FILENO(0): 标准输入
STDOUT_FILENO(1): 标准输出
STDERR_FILENO(2): 标准错误输出

文件描述符号的范围是{0-OPEN_MAX-1}

函数open和openat

man 2 open
man 2 openat

1
2
3
4
5
6
7
8
9
10
11
12
SYNOPSIS
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
SYNOPSIS
#include <fcntl.h>
int openat(int dirfd, const char *pathname, int flags);
int openat(int dirfd, const char *pathname, int flags, mode_t mode);

  • pathname: 打开或者是创建文件的名字
  • flags: 标识,使用下面的常量,多个可以使用

    • O_RDONLY 只读打开(0)
    • O_WRONLY 只写打开(1)
    • O_RDWR 读写打开

    • OS_EXEC 只执行打开

    • OS_SEARCH 搜索打开(不支持)

      以上的这5个常量必须指定一个而且只能指定一个,下面的产量是可选的.

    • O_APPEND
    • O_CLOEXEC 把FD_CLOEXEC设置为文件描述符标志
    • O_CREAT 若文件不存在,则创建它,此时需要第三个参数(openat第四个)mode,指定权限位以及可以使用进程的umask修改
    • O_DIRECTORY 如果不是一个目录就会出错
    • O_EXCL 如果指定了O_CREAT并且文件存在则会出错
    • O_NOCTTY 如果path引用的是终端设备,则不将该设备分配作为该进程的控制终端.
    • O_NOFOLLOW 如果path引用的是符号链接,则出错
    • O_NONBLOCK 如果path引用的是一FIFO,一个块特殊文件或者一个字符特殊文件,则此选项为文件的本次打开操作后和后续的I/O操作设置非阻塞方式.
    • O_SYNC 使每次write等待物理I/O操作完成
    • O_TRUNC 如果此文件存在,而且为只写(或者读写)成功打开,则将其长度截断为0
    • O_TTY_INIT 如果打开一个还未打开的终端设备,设置非标准termios参数.
      下面两个参数是可选择的
    • O_DSYNC
    • O_RSYNC

creat

创建一个文件,相当于
open(path, O_WRONLY| O_CREAT | O_TRUNC, mode)

close

关闭一个文件,并且释放该进程加在该文件记录上的所有的锁

lseek

每打开一个文件都有一个与其相关联的当前文件偏移量

1
2
3
4
5
SYNOPSIS
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);

read

1
2
3
4
SYNOPSIS
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);

read函数需要搞清楚的几个问题在于
返回值

  • 返回读取到的字节数
  • 若到了文件末尾,返回0
  • 出错,返回-1

write

返回值

  • 返回写的字节数
  • 出错,返回-1

I/O的效率

在linux的ext4文件系统下,磁盘长度为4096字节
read ahead预读技术

文件共享

内核使用3种数据结构标示打开文件

  • 1) 每个进程在进程表中都有一个记录项,记录项中包含了一张打开的文件描述符表,每个文件描述符占有一项,与每个文件描速符相关联的是:
    • a) 文件状态标志
    • b) 指向文件表项的指针
  • 2) 内核为所有打开文件维持一张文件表,每个文件表项包含
    • a) 文件状态标志(读,写)
    • b) 当前文件偏移量
    • c) 指向文件v节点的指针
  • 3) 每个打开的文件(或设备)都有一个v节点

进程控制文件

当两个进程打开同一个文件
两个进程打开一个文件

fcntl

1
2
3
4
5
6
SYNOPSIS
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ );
该函数可以改变打开的文件的属性

fcntl函数有以下5个功能

  • (1) 复制一个已有的描述符(cmd=F_DUPFDF_DUPFD_CLOEXEC)
  • (2) 获取/设置文件描述符标志(cmd=F_GETFEF_SETFD)
  • (3) 获取/设置文件状态标志(cmd=F_GETFLF_SETFL)
  • (4) 获取/设置异步I/O所有权(cmd=F_GETOWNF_SETOWN)
  • (5) 获取/设置记录锁(cmd=F_GETLK,F_SETLKF_SETLWK)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {
int val;
if (argc != 2) {
printf("Usage: a.out <descriptor>\n");
exit(-1);
}
if ((val = fcntl(atoi(argv[1]), F_GETFL, 0)) < 0 ) {
printf("fcntl error for fd %d\n", atoi(argv[1]));
}
switch (val & O_ACCMODE) {
case O_RDONLY:
printf("read only\n");
break;
case O_WRONLY:
printf("write only\n");
break;
case O_RDWR:
printf("read write\n");
break;
default:
printf("unkonw access mode");
}
if (val & O_APPEND)
printf(", append\n");
if (val & O_NONBLOCK)
printf(", nonblocking\n");
if (val & O_SYNC) {
printf(", synchronous writes\n");
}
#if !defined(_POSIX_C_SOURCE) && defined(O_FSYNC) && (O_FSYNC != O_SYNC)
if (val & O_FSYNC)
printf(", synchronous writes\n");
#endif
putchar('\n');
return 0;
}
坚持技术分享,您的支持将鼓励我继续创作!