文章目录
前言
Linux下的常用文件I/O操作函数
一、Linux下文件类型有哪些?
Linux系统中把一切都看做文件,Linux有7种类型文件:普通文件、目录文件、符号(link)链接、字符(character)设备文件、块(block)设备文件、管道(pipe)文件、套接字(socket)文件。其中文件、目录、符号链接会占用磁盘空间来存储,而块设备、字符设备、套接字、管道是伪文件,并不占用磁盘空间。
在Linux命令行中,我们可以通过 ls-l 命令来查看文件的相应类型
二、文件描述符
三、文件I/O操作
在开始讲解之前,我们首先来看看下面的示例代码,通过代码更好的理解文件I/O的系统调用,改程序将调用open()系统调用打开一个叫做test.txt的文件(如果不存在则会创建该文件),然后调用write()系统调用将字符串MSG_STR写入到该文件中,之后调用read()系统调用读出该文件里的内容
实例代码:
1、open()系统调用
int open(const char *path, int oflag, … /mode_t mode/);
open()系统调用用来打开一个文件,并返回一个文件描述符(file description), 并且该文件描述符是当前进程最小、未使用的
文件描述符数值。
参数: path: 要打开的文件、设备的路径
oflag: 由多个选项进行“或”运算构造oflag参数 。
必选: O_RDONLY (只读)、 O_WRONLY(只写)、 O_RDWR(读写)
可选: O_APPEND 每次写时都追加到文件的尾端。
O_CREAT 文件不存在则创建它,使用该选项需要第三个参数mode
O_TRUNC 如果文件存在,而且为只写或读写成功打开,则将其长度截取为0;
O_NONBLOCK 如果path是一个FIFO、块设备、字符特殊文件则此选项为文件的本次打开和后续的I/O操作
设置非阻塞模式方式。
O_EXEC、O_SEARCH、O_CLOEXEC、O_NOCTTY…
mode: oflag带O_CREAT选项时可以用来创建文件,这时必须带该参数用来指定创建文件的权限模式,如066。 否则不
需要。使用示例代码:
int fd;
fd = open(“text.txt”, O_RDWR|O_CREAT|O_TRUNC, 0666);
fd = open(“text.txt”, O_WRONLY|O_APPEND);
2、create()系统调用
int creat(const char *path, mode_t mode);
此函数用来创建一个新文件并返回其fd。它等价于 open(path, O_WRONLY|O_CREAT|O_TRUNC, mode);
int fd;
fd=creat(“text.txt”, 0644);
3、close()系统调用
int close(int fd);
该函数用来关闭一个打开的文件描述符,关闭一个文件时还会释放该进程加在该文件上的所有记录锁。当一个进程终止时,
内核将会自动关闭它所有打开的文件。
write()系统
4、write()系统调用
ssize_t write(int fd, const void *buf, size_t nbytes);
write()函数用来往打开的文件描述符fd指向的文件中写入buf指向的数据,其中nbytes指定要写入的数据大小。如果返回值
<0则说明写入出错,譬如尝试往一个只读的文件中写入则会抛错,错误的原因系统会保存到errno变量中去。如果>0则为实
际写入的数据大小。
5、lseek()系统调用
off_t lseek(int fd, off_t offset, int whence);
我们在从文件里读出内容,或往文件写如内容的时候都有一个起始地址,这个起始地址就是当前文件偏移量,当我们对文件
进行读写的时候都会使文件偏移量往后偏移。这点就类似于我们打开记事本开始编辑文本时的光标,我们读或写入时从光标
所在位置开始读写,每读写一个字节都会使光标往后偏移。通过lseek()这个函数我们可以调整文件偏移量的地址。
其中 whence 可以是以下三个值:
而offset就是相对于whence 的偏移量,譬如:
lseek(fd, 0, SEEK_SET); 将文件偏移量设置到了文件开始的第一个字节上;
lseek(fd, 0, SEEK_END); 将文件偏移量设置到文件最后一个字节上;
lseek(fd, -1, SEEK_END); 将文件偏移量设置到文件最后的倒数第一个字节上;
6、read()系统调用
ssize_t read(int fd, void *buf, size_t nbytes);
read()函数用来从打开的文件描述符对应的文件中读取数据放到buf指向的内存空间中去,最多不要超过nbytes个字节,这里
的nbytes一般是buf剩余的空间大小。如read成功,则返回实际读到的字节数(由nbytes或读到文件尾决定,其中EOF宏用
来判断是否到了文件尾),如果返回值小于0则表示出错,如尝试读一个没有权限读的文件时就会抛错。
7、dup()和dup2()系统调用
int dup(int fd);
int dup2(int fd, int fd2);
这两个函数都可以用来复制一个新的文件描述符来指向fd对应的文件。这两个系统调用经常用在标准输入、标准输出、标准
出错重定向。
dup()返回的新文件描述符一定是当前可用文件描述符中的最小数值;
dup2()可以用fd2参数来指定新的文件描述符。如果fd2已经打开,则先关闭。如fd等于fd2, 则dup2返回fd2, 而不关闭它。
在程序运行时我们可以看到printf()打印的信息在执行时并没有打印,而是输出到文件std.txt中去了,这是因为标准输出已经被重定向了。
8、stat()和fstat()系统调用
int stat(const char * restrict path, struct stat *restrict buf);
int fstat(int fd, struct stat *buf);
这两个函数都是用来返回文件或目录的相关信息,只是stat()的第一个参数是文件名,而fstat()的第一个参数是文件打开的相
应文件描述符。其中struct stat结构体的定义如下
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 filesystem 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 */
};
9、access()系统调用
int access(const char *path, int mode);
access()可以用来测试文件是否存在或测试其权限位,其中第一个参数path是相应的文件路径名,第二个参数是要测试的模
式。其中mode说明如下:
10、unlink()系统调用
int unlink(const char *pathname);
该系统调用可以用来删除文件,其本质是让文件的链接记数自减。调用该函数将path指定的文件的链接数减1,如果对该文
件还有其他链接存在,则仍可以通过其他链接访问该文件的数据。只有当链接记数达到0时,该文件的内容才可被删除。如果
有进程打开了该文件,其内容也不能被删除。关闭一个文件时,内核首先检查打开该文件的进程个数,如果这个记数达到0,
内核再去检查它的链接记数,如果记数也是0,那么就删除该文件内容。
11、rename()系统调用
int rename(const char *oldname, const char *newname);
该系统调用用来将文件重命名。
四、文件夹操作相关系统调用
其中readdir()系统调用的struct dirent定义如下:
struct dirent
{
long d_ino; /* inode number 索引节点号 */
off_t d_off; /* offset to this dirent 在目录文件中的偏移 */
unsigned short d_reclen; /* length of this d_name 文件名长 */
unsigned char d_type; /* the type of d_name 文件类型 */
char d_name [NAME_MAX+1]; /* file name (null-terminated) 文件名,最长255字符 */
}
#include <stdio.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#define TEST_DIR "dir"
int main(int argc, char **argv)
{
int rv;
int fd1;
int fd2;
DIR *dirp;
struct dirent *direntp;
/* 创建文件夹dir, 并设置文件夹权限为755 */
if( mkdir(TEST_DIR, 0755)<0 )
{
printf("create directory '%s' failure: %s\n", TEST_DIR, strerror(errno));
return -1;
}
/* 更改当前工作路径到文件夹dir下去 */
if( chdir(TEST_DIR)<0 )
{
printf("Change directory to '%s' failure: %s\n", TEST_DIR, strerror(errno));
rv = -2;
goto cleanup;
}
/* 在dir文件夹下 创建普通文本文件file1.txt,并设置其权限位为644 */
if( (fd1=creat("file1.txt", 0644)) < 0 ){
printf("Create file1.txt failure: %s\n", strerror(errno));
rv = -3;
goto cleanup;
}
/* 在dir文件夹下 创建普通文本文件file2.txt,并设置其权限位为644 */
if( (fd2=creat("file2.txt", 0644)) < 0 )
{
printf("Create file2.txt failure: %s\n", strerror(errno));
rv = -4;
goto cleanup;
}
/* 更改当前工作路径到父目录去 */
if( chdir("../")<0 )
{
printf("Change directory to '%s' failure: %s\n", TEST_DIR, strerror(errno));
rv = -5;
goto cleanup;
}
/* 打开dir文件夹 */
if((dirp=opendir(TEST_DIR)) == NULL)
{
rv = -6;
printf("opendir %s error: %s\n", TEST_DIR, strerror(errno));
goto cleanup;
}
/* 列出dir里面的所有文件和文件夹 */
while((direntp = readdir(dirp)) != NULL)
{
printf("Find file: %s\n", direntp->d_name);
}
/* 关闭所有打开的文件夹 */
closedir(dirp);
cleanup:
if(fd1 >= 0)
{
close(fd1);
}
if(fd2 >= 0)
{
close(fd2);
}
}
五、总结
以上是我经过学习总结的一些关于Linux文件I/O系统调用的知识,最后给出一个知识网图,希望对你有帮助。