基础IO流
# open
函数描述:
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
2
3
4
参数:
- pathname:文件路径
- 以路径的方式给出,则当需要创建该文件时,就在pathname路径下进行创建
- 以文件名的方式给出,默认在当前路径下进行创建
- flags:打开文件的方式
- O_RDONLY:只读
- O_WRONLY:只写
- O_RDWR:读写
- O_CREAT:如果文件不存在,就创建
- O_TRUNC:如果文件存在,就将文件清空
- O_APPEND:如果文件存在,就将文件指针指向文件末尾
注意:O_RDONLY、O_WRONLY和O_RDWR有且只能有一个,多个选项传入时,可以使用
|
运算符进行组合
- mode:文件的权限,默认为0666
返回值:
- 成功:返回文件描述符,Linux进程默认情况下会有3个缺省打开的文件描述符,分别就是标准输入0、标准输出1、标准错误2
- 失败:返回-1
# write
函数描述:
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
2
3
我们可以使用write函数,将buf位置开始向后count字节的数据写入文件描述符为fd的文件当中。
参数:
- fd:文件描述符
- buf:要写入的数据
- count:要写入的数据长度
返回值:
- 如果数据写入成功,实际写入数据的字节个数被返回
- 如果数据写入失败,返回-1
# read
函数描述:
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
2
3
我们可以使用read函数,从文件描述符为fd的文件读取count字节的数据到buf位置当中。
参数:
- fd:文件描述符
- buf:要读取的数据
- count:要读取的数据长度
返回值:
- 如果数据读取成功,实际读取数据的字节个数被返回
- 如果数据读取失败,返回-1
示例:
#include <fcntl.h>
#include <unistd.h> // 包含 close, lseek, read, write 的声明
#include <cstring> // 包含 strlen 的声明
int main() {
int fd = open("log.txt", O_RDWR | O_CREAT, 0666);
if (fd == -1) {
// 错误处理:无法打开或创建文件
return 1;
}
const char* mes = "我们是冠军!";
ssize_t wrsize = write(fd, mes, strlen(mes));
if(wrsize == -1){
// 错误处理:写入失败
close(fd);
return 1;
}
// 将文件位置指示器移到文件开头
if(lseek(fd, 0, SEEK_SET) == -1){
// 错误处理:定位失败
close(fd);
return 1;
}
char ch;
while (1){
ssize_t s = read(fd, &ch, 1);
if (s <= 0){
break;
}
write(1, &ch, 1); // 向文件描述符为1的文件写入数据,即向显示器写入数据
}
close(fd); // 关闭文件描述符
return 0;
}
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
# close
函数描述:
int close(int fd);
使用close函数关闭文件
# fd
文件描述符是Linux系统对文件的一种抽象,它表示了一个打开的文件,每个文件描述符都对应一个打开的文件,并且每个文件描述符都是唯一的。 文件描述符在Linux系统下,一般以数字表示,从0开始,依次递增,从0到1023是系统保留的,从1024开始才是用户自定义的。 在Linux系统下,文件描述符的分配机制是动态的,即当程序打开一个文件时,系统会分配一个文件描述符给该文件,当程序关闭该文件时,系统会释放该文件描述符。
# 分配规则
进程创建的时候会默认打开0、1、2,0就是标准输入流,对应键盘;1就是标准输出流,对应显示器;2就是标准错误流,也是对应显示器。 文件描述符的分配规则:在files_struct数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。
int main() {
close(0);
int fd = open("a.txt", O_WRONLY | O_CREAT | O_TRUNC);
cout << fd << endl;
close(fd); // 0 被关闭了,fd = 0
return 0;
}
2
3
4
5
6
7
8
# 重定向
int main() {
close(1);
int fd = open("a.txt", O_RDWR | O_CREAT, 0666);
fflush(stdout);
cout << fd << endl;
close(fd);
return 0;
}
2
3
4
5
6
7
8
9
最终在我们发现,本来应该输出到显示器上的内容,输出到了文件a.txt当中,其中,fd=1。这种现象叫做输出重定向。
# dup2
要完成重定向我们只需进行文件指针数组当中元素的拷贝即可
#include <unistd.h>
int dup(int oldfd);
int dup2(int oldfd, int newfd);
2
3
4
函数描述: dup2:复制文件描述符,将oldfd复制到一个新的文件描述符,返回新的文件描述符,如果newfd已经存在,就将oldfd复制到newfd,并关闭newfd 参数:
- oldfd:要复制的文件描述符
- newfd:要复制到的文件描述符 返回值:
- 成功:返回新的文件描述符
- 失败:返回-1
int main() {
int fd = open("a.txt", O_RDWR | O_CREAT, 0666);
dup2(fd, 1);
cout << fd << endl;
close(fd);
return 0;
}
2
3
4
5
6
7
最终在文件a.txt当中,输出了3
int main() {
int fd = open("a.txt", O_RDWR | O_CREAT);
dup2(fd, 0);
char line[64];
while (1) {
if (fgets(line, sizeof(line), stdin) == NULL)
break;
cout << line << endl;
}
close(fd);
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
#
# FILE
# FILE当中的文件描述符
FILE实际上是struct _IO_FILE
结构体的一个别名。
因为IO相关函数与系统调用接口对应,并且库函数封装系统调用,所以本质上,访问文件都是通过fd访
问的,结构体中_fileno
就是fd
struct _IO_FILE {
int _flags; /* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags
//缓冲区相关
/* The following pointers correspond to the C++ streambuf protocol. */
/* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
char* _IO_read_ptr; /* Current read pointer */
char* _IO_read_end; /* End of get area. */
char* _IO_read_base; /* Start of putback+get area. */
char* _IO_write_base; /* Start of put area. */
char* _IO_write_ptr; /* Current put pointer. */
char* _IO_write_end; /* End of put area. */
char* _IO_buf_base; /* Start of reserve area. */
char* _IO_buf_end; /* End of reserve area. */
/* The following fields are used to support backing up and undo. */
char *_IO_save_base; /* Pointer to start of non-current get area. */
char *_IO_backup_base; /* Pointer to first valid character of backup area */
char *_IO_save_end; /* Pointer to end of non-current get area. */
struct _IO_marker *_markers;
struct _IO_FILE *_chain;
int _fileno; //封装的文件描述符
#if 0
int _blksize;
#else
int _flags2;
#endif
_IO_off_t _old_offset; /* This used to be _offset but it's too small. */
#define __HAVE_COLUMN /* temporary */
/* 1+column number of pbase(); 0 is unknown. */
unsigned short _cur_column;
signed char _vtable_offset;
char _shortbuf[1];
/* char* _save_gptr; char* _save_egptr; */
_IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};
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
# FILE当中的缓冲区
示例:
#include <stdio.h>
#include <string.h>
int main() {
const char *msg0 = "hello printf\n";
const char *msg1 = "hello fwrite\n";
const char *msg2 = "hello write\n";
printf("%s", msg0);
fwrite(msg1, strlen(msg0), 1, stdout);
write(1, msg2, strlen(msg2));
fork();
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
运行:
[cen@VM-4-9-opencloudos lesson01-基础IO]$ make
g++ test.cpp -o test
[cen@VM-4-9-opencloudos lesson01-基础IO]$ ./test
hello printf
hello fwrite
hello write
[cen@VM-4-9-opencloudos lesson01-基础IO]$ touch file;
[cen@VM-4-9-opencloudos lesson01-基础IO]$ ./test > file # > 将命令输出写入到文件或设备中
[cen@VM-4-9-opencloudos lesson01-基础IO]$ cat file
hello write
hello printf
hello fwrite
hello printf
hello fwrite
2
3
4
5
6
7
8
9
10
11
12
13
14
原因:
printf fwrite
库函数会自带缓冲区,而write
系统调用没有带缓冲区。另外,我们这里所说的缓冲区,
都是用户级缓冲区。printf fwrite
是库函数, write
是系统调用,库函数在系统调用的“上层”, 是对系统
调用的“封装”,但是 write
没有缓冲区,而 printf fwrite
有,足以说明,该缓冲区是二次加上的,又因为是
C++,所以由C++标准库提供。
# 文件系统
磁盘文件由两部分构成,分别是文件内容和文件属性。文件内容就是文件当中存储的数据,文件属性就是文件的一些基本信息,文件属性又被称为元信息。
在Linux操作系统中,文件的元信息和内容是分离存储的,其中保存元信息的结构称之为inode,因为系统当中可能存在大量的文件,所以我们需要给每个文件的属性集起一个唯一的编号,即inode号。
[cen@VM-4-9-opencloudos lesson01-基础IO]$ ls -i
134218353 file 134218355 Makefile 134218309 test 134218352 test.cpp
2
# 软硬链接
# 软链接
命令:
ln -s file sort_file.link
# 硬链接
ln file hard_file.link
示例:
[cen@VM-4-9-opencloudos lesson01-基础IO]$ ll -li
total 32
134218353 -rw-r--r-- 1 cen cen 64 Mar 17 19:29 file
134218355 -rw-r--r-- 1 cen cen 71 Mar 12 10:28 Makefile
134218309 -rwxr-xr-x 1 cen cen 16816 Mar 17 19:25 test
134218352 -rw-r--r-- 1 cen cen 544 Mar 17 19:25 test.cpp
[cen@VM-4-9-opencloudos lesson01-基础IO]$
[cen@VM-4-9-opencloudos lesson01-基础IO]$ ln -s file sort_file.link
[cen@VM-4-9-opencloudos lesson01-基础IO]$ ll -li
total 32
134218353 -rw-r--r-- 1 cen cen 64 Mar 17 19:29 file
134218355 -rw-r--r-- 1 cen cen 71 Mar 12 10:28 Makefile
134218354 lrwxrwxrwx 1 cen cen 4 Mar 17 21:13 sort_file.link -> file
134218309 -rwxr-xr-x 1 cen cen 16816 Mar 17 19:25 test
134218352 -rw-r--r-- 1 cen cen 544 Mar 17 19:25 test.cpp
[cen@VM-4-9-opencloudos lesson01-基础IO]$ ln file hard_file.link
[cen@VM-4-9-opencloudos lesson01-基础IO]$ ll -li
total 36
134218353 -rw-r--r-- 2 cen cen 64 Mar 17 19:29 file
134218353 -rw-r--r-- 2 cen cen 64 Mar 17 19:29 hard_file.link
134218355 -rw-r--r-- 1 cen cen 71 Mar 12 10:28 Makefile
134218354 lrwxrwxrwx 1 cen cen 4 Mar 17 21:13 sort_file.link -> file
134218309 -rwxr-xr-x 1 cen cen 16816 Mar 17 19:25 test
134218352 -rw-r--r-- 1 cen cen 544 Mar 17 19:25 test.cpp
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
结论:
- 软链接文件相对于源文件来说是一个独立的文件,该文件有自己的inode号,但是该文件只包含了源文件的路径名
- 软链接文件只是其源文件的一个标记,当删除了源文件后,链接文件不能独立存在,虽然仍保留文件名,但却不能执行或是查看软链接的内容了
- 硬链接文件就是源文件的一个别名,一个文件有几个文件名,该文件的硬链接数就是几
- 当硬链接的源文件被删除后,硬链接文件仍能正常执行,只是文件的链接数减少了一个,因为此时该文件的文件名少了一个
- 软链接是一个独立的文件,有独立的inode,而硬链接没有独立的inode。
- 软链接相当于快捷方式,硬链接本质没有创建文件,只是建立了一个文件名和已有的inode的映射关系,并写入当前目录
# 文件的时间
文件的三个时间信息:
- Access: 文件最后被访问的时间
- Modify: 文件内容最后的修改时间
- Change: 文件属性最后的修改时间