前言
本篇文章我们来讲解Linux中的文件编程,这篇文章会先介绍open read write函数。
一、open函数
open 函数是一个在 POSIX 标准中定义的函数,用于打开文件或者创建新文件。它是文件操作中很常用的一个函数,主要用于在程序中访问文件系统。
下面是 open 函数的原型:
#include <fcntl.h> int open(const char *pathname, int flags, mode_t mode);
参数说明:
pathname:要打开或创建的文件路径名。
flags:文件打开的标志,用来指定打开方式和操作权限。
mode:新创建文件的权限(仅在创建新文件时有效)。
open 函数返回一个非负整数,称为文件描述符,它是用于标识打开文件的唯一值。如果函数执行失败,则返回 -1,表示出错。
下面是一些常用的 flags 参数的取值:
O_RDONLY:只读方式打开文件。
O_WRONLY:只写方式打开文件。
O_RDWR:读写方式打开文件。
O_CREAT:如果文件不存在,则创建文件。
O_APPEND:追加方式打开文件,写入内容会追加到文件末尾。
O_TRUNC:如果文件存在,将其内容截断为0,即清空文件。
下面是一些常用的 mode 参数的取值:
S_IRUSR:用户具有读权限。
S_IWUSR:用户具有写权限。
S_IXUSR:用户具有执行权限。
S_IRGRP:用户组具有读权限。
S_IWGRP:用户组具有写权限。
S_IXGRP:用户组具有执行权限。
S_IROTH:其他用户具有读权限。
S_IWOTH:其他用户具有写权限。
S_IXOTH:其他用户具有执行权限。
当前目录下不存在1.txt文件,我们使用open函数来创建并打开一个1.txt文件:
示例代码:
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main(void) { int fd = 0; fd = open("1.txt", O_RDWR | O_CREAT); if(fd == -1) { printf("open 1.txt is err\n"); return -1; } return 0; }
运行结果:
在这里我们可以看到整个文件是红色的,因为我们在创建时没有给任何权限,那么这里我们在创建文件时给这个文件一些权限:
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main(void) { int fd = 0; fd = open("1.txt", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IXUSR); if(fd == -1) { printf("open 1.txt is err\n"); return -1; } return 0; }
二、read函数
read 函数是一个系统调用,用于从已打开文件的文件描述符中读取数据。它的原型如下:
#include <unistd.h> ssize_t read(int fd, void *buffer, size_t count);
参数解释:
fd 是文件描述符,指定要读取数据的文件或设备。
buffer 是一个指针,指向用于存储读取数据的缓冲区。
count 是要读取的字节数,指定要从文件中读取的数据量。
read 函数从文件描述符指定的文件位置开始读取数据,并将其存储到提供的缓冲区中。它返回实际读取的字节数。如果 read 函数返回0,表示已到达文件末尾。如果返回负值,表示读取出错,具体的错误码会存储在全局变量 errno 中供用户程序查询。
使用 read 函数时,需要注意以下几点:
1.在调用 read 之前,应确保文件已经被成功打开并获得有效的文件描述符。
2.缓冲区的大小应足够大,以容纳要读取的数据。
3.返回的字节数可能小于请求的字节数 count,特别是在读取非阻塞文件描述符时,这是正常的情况。
4.多次调用 read 函数可以连续读取更多的数据,直到返回值为0,表示已读取完文件。
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int main(void) { int fd = 0; char buf[1024]; int len = 0; fd = open("1.txt", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); //打开文件,如果不存在则创建文件为可读可写 if(fd == -1) { printf("open 1.txt is err\n"); return -1; } len = read(fd, buf, 1024); if(len != -1) { printf("read buf : %s len : %d\n", buf, len); } return 0; }
先在vi编辑器中修改文件中的内容:
运行结果:
三、write函数
write 函数是一个系统调用,用于将数据写入已打开文件的文件描述符中。它的原型如下:
#include <unistd.h> ssize_t write(int fd, const void *buffer, size_t count);
参数解释:
fd 是文件描述符,指定要写入数据的文件或设备。
buffer 是一个指针,指向包含要写入的数据的缓冲区。
count 是要写入的字节数,指定要写入的数据量。
write 函数将 count 字节的数据从 buffer 中写入到文件描述符指定的文件中。它返回实际写入的字节数。如果返回负值,表示写入出错,具体的错误码会存储在全局变量 errno 中供用户程序查询。
使用 write 函数时,需要注意以下几点:
1.在调用 write 之前,应确保文件已经被成功打开并获得有效的文件描述符。
2.缓冲区中至少包含 count 字节的数据。
3.返回的字节数可能小于请求的字节数 count,特别是在写入非阻塞文件描述符时,这是正常的情况。
4.可以多次调用 write 函数来连续写入更多的数据。
示例代码:
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <string.h> int main(void) { int fd = 0; char buf[1024]; int len = 0; fd = open("1.txt", O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); //打开文件,如果不存在则创建文件为可读可写,并且将文件内容全部清除 if(fd == -1) { printf("open 1.txt is err\n"); return -1; } len = write(fd, "Hello", 6); if(len == -1) { printf("write err\n"); } lseek(fd, 0, SEEK_SET); len = read(fd, buf, 1024); if(len != -1) { printf("read buf : %s len : %d\n", buf, len); } return 0; }
运行结果:
四、open read write函数本质
当调用 open、read、write 等文件相关的函数时,通常会触发系统调用(syscall),进入操作系统内核执行相应的操作。系统调用是用户程序与操作系统之间的接口,允许用户程序请求操作系统提供的服务和资源。
在执行系统调用时,用户程序通过特殊的指令(例如 x86 架构上的 int 0x80 或 syscall 指令)将控制权转移到操作系统内核。操作系统根据系统调用号识别用户请求的具体服务,并执行相应的操作。
系统调用的执行通常会引起特权转换(从用户模式切换到内核模式),因为内核需要访问受保护的资源和执行特权指令。在这个过程中,会触发一个异常(如 ARM 架构中的 SVC 异常),将控制权转移到内核中的异常处理程序。
异常处理程序在内核中执行所需的操作,例如打开文件、读写文件等。一旦操作完成,控制权将返回到用户程序,继续执行后续的指令。
因此,当调用 open、read、write 等文件相关函数时,通常会触发 SVC 异常,并进入内核执行相应的文件操作。
五、close函数
close 函数用于关闭打开的文件描述符。在使用完文件后,应该调用 close 函数来释放系统资源并确保数据的完整性。
函数原型如下:
#include <unistd.h> int close(int fd);
其中,fd 是需要关闭的文件描述符。如果关闭成功,返回值为0;如果发生错误,返回值为-1。
close 函数的主要作用如下:
1.释放文件描述符:调用 close 函数会释放文件描述符,并使其可供其他程序使用。这是一种良好的资源管理实践,避免文件描述符的浪费。
2.刷新缓冲区:在关闭文件之前,close 函数会自动刷新文件的缓冲区,确保所有的数据都被写入到磁盘中。这样可以避免数据的丢失或不一致。
3.断开与文件的连接:通过关闭文件描述符,close 函数会断开程序与文件之间的连接,不再对文件进行读写操作。这是确保文件的安全性以及其他进程能够访问文件的重要步骤。
总结
本篇文章就讲解到这里,下篇文章继续讲解Linux文件编程。