Linux 文件与I/O

Linux 文件的概念,以及对文件I/O操作的接口。

什么是I/O?

  • 输入/输出是主存和外部设备之间拷贝数据的过程
    • 设备->内存 (输入操作)
    • 内存->设备 (输出操作)
  • 高级I/O
    • ANSI C提供的标准I/O库称为高级I/O,通常也称为带缓冲的I/O
    • 低级I/O
  • 通常也称为不带缓冲的I/O

文件描述符

  • 对于Linux而言,所有对设备或文件的操作都是通过文件描述符进行的。
  • 当打开或者创建一个文件的时候,内核向进程返回一个文件描述符(非负整数)。后续对文件的操作只需通过该文件描述符,内核记录有关这个打开文件的信息。
  • 一个进程启动时,默认打开了3个文件,标准输入、标准输出、标准错误,对应文件描述符是0(STDIN_FILENO)、1(STDOUT_FILENO)、2(STDERR_FILENO),这些常量定义在unistd.h头文件中。

文件共享




文件描述符与文件指针转换

  • fileno:将文件指针转换为文件描述符
  • fdopen:将文件描述符转换为文件指针

实例:

1
2
3
4
5
6
7
8
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
printf("fileno(stdin) = %d\n", fileno(stdin));
return 0;
}


文件系统调用

open函数

  • 功能:打开或者打开并创建文件
  • 原型:

    1
    2
    int open(const char *path, int flags);
    int open(const char *path, int flags,mode_t mode);
  • 参数:

    • path :文件的名称,可以包含(绝对和相对)路径
    • flags:文件打开模式
    • mode: 用来规定对该文件的所有者,文件的用户组及系统中其他用户的访问权限
  • 返回值:打开成功,返回文件描述符;打开失败,返回-1

打开文件的方式:

打开方式 描述
O_RDONLY 打开一个供读取的文件
O_WRONLY 打开一个供写入的文件
O_RDWR 打开一个可供读写的文件
O_APPEND 写入的所有数据将被追加到文件的末尾
O_CREAT 打开文件,如果文件不存在则建立文件
O_EXCL 如果已经置O_CREAT且文件存在,则强制open()失败
O_TRUNC 在open()时,将文件的内容清空

访问权限:

打开方式 描述
S_IRUSR 文件所有者的读权限位
S_IWUSR 文件所有者的写权限位
S_IXUSR 文件所有者的执行权限位
S_IRWXU S_IRUSR S_IWUSR S_IXUSR
S_IRGRP 文件用户组的读权限位
S_IWGRP 文件用户组的写权限位
S_IXGRP 文件用户组的执行权限位
S_IRWXG S_IRGRP S_IWGRP S_IXGRP
S_IROTH 文件其他用户的读权限位
S_IWOTH 文件其他用户的写权限位
S_IXOTH 文件其他用户的执行权限位
S_IRWXO S_IROTH S_IWOTH S_IXOTH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
int main() {
int fd = open("test", O_RDONLY | O_CREAT, 0666);
if (fd == -1) {
ERR_EXIT("open");
}
return 0;
}

close函数

  • 功能:释放打开的文件描述符
  • 原型:

    1
    int close(int fd);
  • 参数:

    • fd :要关闭的文件的文件描述符
  • 返回值:如果出现错误,返回-1;调用成功返回0

create函数

  • 功能:为了维持与早期的UNIX系统的向后兼容性,Linux也提供可选的创建文件的系统调用,它称为creat()。
  • 原型:

    1
    int creat(const char *path, mode_t mode);
  • 参数:

    • path :文件的名称,可以包含(绝对和相对)路径
    • mode: 用来规定对该文件的所有者,文件的用户组及系统中其他用户的访问权限
  • 返回值:打开成功,返回文件描述符;打开失败,返回-1

read函数

  • 从文件中读取字节
  • 原型:

    1
    ssize_t read(int fd, void *buf, size_t count);
  • 参数:

    • fd :想要读的文件的文件描述符
    • buf : 指向内存块的指针,从文件中读取来的字节放到这个内存块中
    • count : 从该文件复制到buf中的字节个数
  • 返回值 如果出现错误,返回-1,读文件结束,返回0,否则返回从该文件复制到规定的缓冲区中的字节数

write函数

  • 将数据写到一个文件中
  • 原型:

    1
    ssize_t write(int fd, const void *buf, size_t count);
  • 参数:

    • fd:要写入的文件的文件描述符
    • buf: 指向内存块的指针,从这个内存块中读取数据写入到文件中
    • count: 要写入文件的字节个数
  • 返回值:如果出现错误,返回-1,如果写入成功,则返回写入到文件中的字节个数

复制文件实例:

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
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
int main(int argc, char *argv[]) {
if (argc != 3) {
fprintf(stderr, "Usage : %s <sourcefilename> <targetfilename>\n", argv[0]);
exit(EXIT_FAILURE);
}
int infd;
int outfd;
if ((infd = open(argv[1], O_RDONLY)) < 0) {
ERR_EXIT("open");
}
if ((outfd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0) {
ERR_EXIT("open");
}
char buf[1024];
int nready;
while ((nready = read(infd, buf, sizeof(buf))) > 0) {
write(outfd, buf, nready);
}
close(infd);
close(outfd);
return 0;
}

lseek函数

  • 功能说明:通过指定相对于开始位置、当前位置或末尾位置的字节数来重定位文件指针
  • 原型:

    1
    off_t lseek (int fd, off_t offset, int base);
  • 参数:

    • fd:文件描述符
    • offset:需要的偏移量
    • base:起始的位置
  • 返回值:返回新的文件偏移值

base取值:

base 文件位置
SEEK_SET 从文件开始处计算偏移
SEEK_CUR 从当前文件的偏移值计算偏移
SEEK_END 从文件的结束处计算偏移

opendir函数

  • 功能:打开一个目录
  • 原型:

    1
    DIR *opendir(char *pathname);
  • 参数:

    • pathname:文件的路径名
  • 返回值:打开成功,返回一个目录指针,打开失败,则返回0

readdir函数

  • 功能:访问指定目录中下一个连接的细节
  • 原型:

    1
    2
    3
    4
    5
    6
    7
    8
    struct dirent* readdir(DIR *dirptr);
    struct dirent {
    long d_ino; /* inode number */
    off_t d_off; /* offset to this dirent */
    unsigned short d_reclen; /* length of this d_name */
    char d_name [NAME_MAX+1]; /* file name (null-terminated) */
    }
  • 参数:

    • dirptr:目录指针
  • 返回值:返回一个指向dirent结构的指针,它包含指定目录中下一个连接的细节;
    没有更多连接时,返回0

简单版的ls.c

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
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
int main() {
DIR *dir = opendir(".");
struct dirent *de;
while ((de = readdir(dir)) != NULL) {
if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) {
continue;
}
printf("%s\n", de->d_name);
}
closedir(dir);
return 0;
}


closedir函数

  • 功能:关闭一个已经打开的目录
  • 原型:

    1
    int closedir (DIR *dirptr);
  • 参数:

    • dirptr:目录指针
  • 返回值:调用成功返回0,失败返回-1

mkdir函数

  • 功能:用来创建新目录
  • 原型:

    1
    int mkdir(char *pathname,mode_t mode);
  • 参数:

    • pathname:文件的路径名
    • mode:权限位
  • 返回值:调用成功返回0,失败返回-1

rmdir函数

  • 功能说明:删除一个空目录
  • 原型:

    1
    int rmdir(char *pathname);
  • 参数:

    • pathname:文件的路径名
  • 返回值:调用成功返回0,失败返回-1

chmod函数

  • 功能说明:改变文件的权限位
  • 原型:

    1
    2
    int chmod (char *pathname, mode_t mode);
    int fchmod (int fd, mode_t mode);
  • 参数:

    • pathname:文件路径名
    • mode:权限位
  • 返回值:调用成功返回0,失败返回-1

chown函数

  • 功能:用来改变文件所有者的识别号(owner id)或者它的用户组识别号(group ID)
  • 原型:

    1
    2
    int chown (char *pathname, uid_t owner,gid_t group);
    int fchown (int fd, uid_t owner,gid_t group);
  • 参数:

    • pathname:文件的路径名
    • owner:所有者识别号
    • group:用户组识别号
  • 返回值:调用成功返回0,失败返回-1

stat函数

功能:读取文件元数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int stat(const char *path, struct stat *buf);
int fstat(int fd, struct stat *buf);
int lstat(const char *path, struct stat *buf);
struct stat {
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* inode number */
mode_t st_mode; /* protection */
nlink_t st_nlink; /* number of hard links */
uid_t st_uid; /* user ID of owner */
gid_t st_gid; /* group ID of owner */
dev_t st_rdev; /* device ID (if special file) */
off_t st_size; /* total size, in bytes */
blksize_t st_blksize; /* blocksize for file system I/O */
blkcnt_t st_blocks; /* number of 512B blocks allocated */
time_t st_atime; /* time of last access */
time_t st_mtime; /* time of last modification */
time_t st_ctime; /* time of last status change */
};

查看文件属性:

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
#define MAJOR(a) (int)((unsigned short)a >> 8)
#define MINOR(a) (int)((unsigned short)a && 0xFF)
int filetype(struct stat *buf);
void fileperm(struct stat *buf, char *perm);
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s file\n", argv[0]);
exit(EXIT_FAILURE);
}
printf("Filename: %s\n", argv[1]);
struct stat sbuf;
if (stat(argv[1], &sbuf) == -1) {
ERR_EXIT("stat");
}
printf("File number:major %d,minor %d inode %d\n", MAJOR(sbuf.st_dev), MINOR(sbuf.st_dev), (int)sbuf.st_ino);
if (filetype(&sbuf)) {
printf("Device number:major %d,minor %d\n", MAJOR(sbuf.st_rdev), MINOR(sbuf.st_rdev));
}
char perm[11] = {0};
fileperm(&sbuf, perm);
printf("File permission bits=%o %s\n", sbuf.st_mode & 07777, perm);
return 0;
}
int filetype(struct stat *buf) {
int flag = 0;
printf("Filetype:");
mode_t mode;
mode = buf->st_mode;
switch (mode & S_IFMT) {
case S_IFSOCK:
printf("socket\n");
break;
case S_IFLNK:
printf("symbolic link\n");
break;
case S_IFREG:
printf("regular file\n");
break;
case S_IFBLK:
printf("block device\n");
flag = 1;
break;
case S_IFDIR:
printf("directory\n");
break;
case S_IFCHR:
printf("character device\n");
flag = 1;
break;
case S_IFIFO:
printf("FIFO\n");
break;
default:
printf("unknown file type\n");
break;
}
return flag;
}
void fileperm(struct stat *buf, char *perm) {
strcpy(perm, "----------");
perm[0] = '?';
mode_t mode;
mode = buf->st_mode;
switch (mode & S_IFMT)
{
case S_IFSOCK:
perm[0] = 's';
break;
case S_IFLNK:
perm[0] = 'l';
break;
case S_IFREG:
perm[0] = '-';
break;
case S_IFBLK:
perm[0] = 'b';
break;
case S_IFDIR:
perm[0] = 'd';
break;
case S_IFCHR:
perm[0] = 'c';
break;
case S_IFIFO:
perm[0] = 'p';
break;
}
if (mode & S_IRUSR)
perm[1] = 'r';
if (mode & S_IWUSR)
perm[2] = 'w';
if (mode & S_IXUSR)
perm[3] = 'x';
if (mode & S_IRGRP)
perm[4] = 'r';
if (mode & S_IWGRP)
perm[5] = 'w';
if (mode & S_IXGRP)
perm[6] = 'x';
if (mode & S_IROTH)
perm[7] = 'r';
if (mode & S_IWOTH)
perm[8] = 'w';
if (mode & S_IXOTH)
perm[9] = 'x';
perm[10] = '\0';
}


dup函数

  • 功能:复制文件描述符
  • 原型:

    1
    2
    3
    int dup(int oldfd);
    int dup2(int oldfd, int newfd);
    int dup3(int oldfd, int newfd, int flags);
  • 参数:

    • oldfd:被复制的文件描述符
    • newfd:指定复制的文件描述符
    • flags:指定O_CLOEXEC标志
  • 返回值:成功返回复制的描述符,失败返回-1
    执行dup前后

dup实现IO重定向:

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
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
int main() {
int fd;
fd = open("test.txt", O_WRONLY);
if (fd == -1) {
ERR_EXIT("open");
}
close(1);
dup(fd);
printf("hello\n");
return 0;
}

fcntl函数

  • 功能:操纵文件描述符,改变已打开的文件的属性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    int fcntl(int fd, int cmd, ... /* arg */ );
    /*文件锁结构体*/
    struct flock {
    ...
    short l_type; /* Type of lock: F_RDLCK,
    F_WRLCK, F_UNLCK */
    short l_whence; /* How to interpret l_start:
    SEEK_SET, SEEK_CUR, SEEK_END */
    off_t l_start; /* Starting offset for lock */
    off_t l_len; /* Number of bytes to lock */
    pid_t l_pid; /* PID of process blocking our lock
    (F_GETLK only) */
    ...
    };
  • 参数:

    • fd:文件描述符
    • cmd:指定的命令
  • 返回值:失败返回-1

cmd参数:

  • 复制文件描述符
    • F_DUPFD (long)
  • 文件描述符标志
    • F_GETFD (void)
    • F_SETFD (long)
  • 文件状态标志
    • F_GETFL (void)
    • F_SETFL (long)
  • 文件锁
    • F_GETLK
    • F_SETLK,F_SETLKW

更改状态信息:

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
45
46
47
48
49
50
51
52
53
54
55
56
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
void set_flag(int fd, int flags);
void clr_flag(int fd, int flags);
int main() {
set_flag(0, O_NONBLOCK);
int ret;
char buf[1024];
ret = read(0, buf, 1024);
if (ret == -1) {
ERR_EXIT("read");
}
printf("buf = %s\n", buf);
return 0;
}
void set_flag(int fd, int flags) {
int val;
val = fcntl(fd, F_GETFL, 0);
if (val == -1) {
ERR_EXIT("fcntl");
}
int ret;
ret = fcntl(fd, F_SETFL, val | flags);
if (ret == -1) {
ERR_EXIT("fcntl");
}
}
void clr_flag(int fd, int flags) {
int val;
val = fcntl(fd, F_GETFL, 0);
if (val == -1) {
ERR_EXIT("fcntl");
}
int ret;
ret = fcntl(fd, F_SETFL, val & ~flags);
if (ret == -1) {
ERR_EXIT("fcntl");
}
}

文件锁:

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
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
int main() {
int fd;
if ((fd = open("test2.txt", O_CREAT | O_RDWR | O_TRUNC, 0666)) == -1) {
ERR_EXIT("open");
}
struct flock lock;
memset(&lock, 0, sizeof(lock));
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
if ((fcntl(fd, F_SETLK, &lock)) == 0) {
printf("lock success\n");
printf("press any key to unlock\n");
getchar();
lock.l_type = F_UNLCK;
if (fcntl(fd, F_SETLK, &lock) == 0) {
printf("unlock success\n");
} else {
ERR_EXIT("unlock fail");
}
} else {
ERR_EXIT("lock fail");
}
return 0;
}