open
用于打开或创建文件,并返回一个文件描述符。需要包含头文件:#include <unistd.h>
;其参数二的flags需要包含头文件:#include <fcntl.h>
函数原型:
int open(char *pathname, int flags)
int open(char *pathname, int flags, mode_t mode)
参数:
- pathname:欲打开的文件路径名
- flags:文件打开方式,例如
O_RDONLY
(只读)、O_WRONLY
(只写)、O_RDWR
(读写)、O_CREAT
(创建新文件)、O_APPEND
(附加)、O_TRUNC
(文件截断)、O_EXCL
(文件不存在)、O_NONBLOCK
(非阻塞)…… - mode:参数3使用的前提,参2指定了
O_CREAT
。取值8进制数,用来描述新创建文件的访问权限。创建文件最终权限 = mode & ~umask。
返回值:
- 成功:打开文件所得到对应的文件描述符(整数)
- 失败:-1,并设置errno
close
用于释放程序中打开的文件描述符,头文件和open一样。
函数原型:
int close(int fd);
truncate/ftruncate
用于修改文件的大小,可以将文件截断为指定的长度,或者扩展文件到指定的长度。当截断文件时,超出指定长度的部分将被删除;当扩展文件时,文件内容可以保持不变,新增的部分将会被填充为零字节或未定义的内容。需要包含头文件<unistd.h>
函数原型:
int truncate(const char *path, off_t length);
int ftruncate(int fd, off_t length);
参数:
-
path
:要操作的文件的路径名。 -
length
:指定的文件大小,截断文件时表示新的文件大小,扩展文件时表示文件新的大小。 -
fd
:要操作的文件描述符。
返回值:
- 成功:0
- 失败:-1,并设置error
read
用于从文件描述符中读取数据,需要包含头文件:#include <unistd.h>
函数原型:
ssize_t read(int fd, void *buf, size_t count);
参数:
- fd:文件描述符
- buf:存数据的缓冲区
- count:缓冲区大小
返回值:
- 0:读到文件末尾
- 成功:> 0,读到的字节数
- 失败:-1,设置errno
- -1并且 errno = EAGIN 或 EWOULDBLOCK:说明不是read失败,而是read在以非阻塞方式读一个设备文件(网络文件),并且文件无数据。
write
用于向文件描述符中写入数据,需要包含头文件:#include <unistd.h>
函数原型:
ssize_t write(int fd, const void *buf, size_t count);
参数:
- fd:文件描述符
- buf:待写出数据的缓冲区
- count:数据大小
返回值:
- 成功:写入的字节数。
- 失败:-1,设置 errno
例:运用上面四个系统调用实现linux的cp命令
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
using namespace std;
int main(int argc, char *argv[])
{
char buf[1024]; //缓冲区
int fd1 = open(argv[1], O_RDONLY);
if (fd1 == -1) //错误处理
{
perror("open argv1 error");
exit(1);
}
int fd2 = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd2 == -1) //错误处理
{
perror("open argv2 error");
exit(1);
}
int n;
while ((n = read(fd1, buf, 1024)) > 0)
{
if (n == -1)
break;
write(fd2, buf, n);
}
close(fd1);
close(fd2);
return 0;
}
fcntl
fcntl
是UNIX系统提供的一个系统调用,用于对文件描述符进行各种操作。它可以用于执行各种文件描述符相关的控制操作,如修改文件状态标志、获取/设置文件描述符属性、锁定文件等。需要包含头文件:#include <fcntl.h>
函数原型:
int fcntl(int fd, int cmd, ... /* arg */);
参数:
-
fd
:文件描述符,表示要进行操作的文件描述符。 -
cmd
:操作命令,可以指定要执行的操作类型,掌握F_GETFL
(获取文件状态)、F_SETFL
(设置文件状态)。 -
arg
:根据不同的命令类型,arg
可能是一个整数值,也可能是一个结构体指针。
返回值的含义取决于执行的具体操作。一般情况下,fcntl
调用的返回值可以有以下几种情况:
- 成功执行:如果
fcntl
调用成功执行,返回值通常是与操作相关的具体数值。例如,对于获取文件状态标志 (F_GETFL
) 操作,返回的是文件状态标志的值。 - 失败:如果
fcntl
调用执行失败,返回值通常是 -1,并且会设置全局变量errno
来指示具体的错误原因。可以使用perror
或其他错误处理函数来获取错误信息。
例:
int flags = fcntl(STDIN_FILENO, F_GETFL); //获取stdin属性信息
flags |= O_NONBLOCK; //设置非阻塞
int ret = fcntl(STDIN_FILENO, F_SETFL, flags); //更新stdin属性信息
lseek
用于在文件描述符上设置文件偏移量(file offset)。文件偏移量是用来指示当前读/写操作在文件中的位置的一个指针。需要包含头文件#include<unistd.h>
。
函数原型:
off_t lseek(int fd, off_t offset, int whence);
参数:
- fd:文件描述符
- offset:偏移量,就是将读写指针从whence指定位置向后偏移offset个单位
- whence:起始偏移位置,包括
SEEK_SET
/SEEK_CUR
/SEEK_END
返回值:
- 成功:较起始位置偏移量
- 失败:-1,并设置errno
应用场景:
- 文件的“读”、“写”使用同一偏移位置。
- 使用
lseek
获取文件大小(返回值接收),int length = lseek(filepath, 0, SEEK_END);
- 使用
lseek
拓展文件大小:要想使文件大小真正拓展,必须引起IO操作。
例:写一个句子到空白文件,然后调整光标位置,读取刚才写那个文件。
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <cstring>
using namespace std;
int main()
{
char msg[] = "It is a test for lseek.\n";
int fd = open("test.txt", O_RDWR | O_CREAT, 0644);
if (fd < 0)
{
perror("open file error");
exit(1);
}
write(fd, msg, strlen(msg));
// 修改文件读写指针位置,位于文件开头。
lseek(fd, 0, SEEK_SET);
char ch;
int n;
while ((n = read(fd, &ch, 1)) > 0)
{
if (n < 0)
{
perror("read error");
exit(1);
}
write(STDOUT_FILENO, &ch, n);
}
close(fd);
return 0;
}
stat/lstat
用于获取文件的状态信息,比如文件大小、创建时间、修改时间等。如果文件是一个符号链接,stat
函数会进行符号穿透,即返回符号链接文件所指向的文件的信息;而lstat
函数将返回符号链接文件本身的信息,而不会解引用符号链接。需要包含头文件#include <sys/stat.h>
函数原型:
int stat(const char *path, struct stat *buf);
int lstat(const char *path, struct stat *buf);
参数:
- path:文件路径
- buf:(传出参数)存放文件属性,inode结构体指针。
返回值:
- 成功: 0
- 失败: -1,并设置errno
struct stat {
dev_t st_dev; // ID of device containing file
ino_t st_ino; // Inode number
mode_t st_mode; // File type and mode
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; // Block size for filesystem I/O
blkcnt_t st_blocks; // Number of 512B blocks allocated
struct timespec st_atim; // Time of last access
struct timespec st_mtim; // Time of last modification
struct timespec st_ctim; // Time of last status change
};
获取文件大小: buf.st_size
获取文件类型: buf.st_mode
获取文件权限: buf.st_mode
例:
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <cstdlib>
using namespace std;
int main()
{
struct stat buf;
int ret = stat("./test.txt", &buf);
if (ret == -1)
{
perror("stat error");
exit(1);
}
cout << "file size: " << buf.st_size << endl;
return 0;
}
检查文件类型的宏函数:
-
S_ISDIR(mode)
:检查文件是否为目录文件。 -
S_ISREG(mode)
:检查文件是否为普通文件。 -
S_ISCHR(mode)
:检查文件是否为字符特殊文件(字符设备)。 -
S_ISBLK(mode)
:检查文件是否为块特殊文件(块设备)。 -
S_ISLNK(mode)
:检查文件是否为符号链接。 -
S_ISFIFO(mode)
:检查文件是否为FIFO(命名管道)。 -
S_ISSOCK(mode)
:检查文件是否为套接字。
link/unlink
link
函数用于创建一个新的硬链接,将oldpath
指定的文件链接到newpath
指定的路径上。
unlink
函数用于删除指定路径的文件。unlink
清除文件时,如果文件的硬链接数到0了,没有dentry对应,但该文件仍不会马上被释放,要等到所有打开文件的进程关闭该文件,系统才会挑时间将该文件释放掉。
需要头文件#include<unistd.h>
函数原型:
int link(const char *oldpath, const char *newpath);
int unlink(const char *pathname);
返回值:
- 0:成功
- -1:失败
opendir/closedir
opendir
函数用于打开一个目录并返回一个指向该目录的指针,以便后续对目录进行读取操作。
closedir
函数用于关闭通过 opendir
函数打开的目录流,并释放与该目录流相关的资源。
需要包含头文件#include<dirent.h>
函数原型:
DIR *opendir(constchar *name);
int closedir(DIR *dir);
返回值:
- 0:成功关闭目录
- -1:关闭目录失败
readdir
readdir
函数用于读取目录流中的下一个目录条目,并返回一个指向dirent
结构体的指针,该结构体包含有关读取的目录条目的信息。需要包含头文件#include<dirent.h>
函数原型:
struct dirent *readdir(DIR *dir);
参数dir
是通过opendir
函数打开的目录流指针。
函数返回一个指向dirent
结构体的指针,该结构体包含有关下一个目录条目的信息。如果到达目录流的末尾或者发生错误,readdir
将返回NULL
。
dirent
结构体定义如下:
struct dirent {
ino_t d_ino; // inode number
off_t d_off; // offset to the next dirent
unsigned short d_reclen; // length of this record
unsigned char d_type; // type of file
char d_name[256]; // filename
};
例:递归遍历目录打印文件名和大小
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <cstdlib>
#include <dirent.h>
#include <cstring>
using namespace std;
bool isdir(const char *name)
{
struct stat buf;
if (stat(name, &buf) == 0)
{
cout << name << ": " << buf.st_size << endl;
if (S_ISDIR(buf.st_mode))
return true;
return false;
}
else
{
perror("get stat error");
exit(1);
}
}
void fetchdir(const char *name)
{
if (!isdir(name))
return;
DIR *dir = opendir(name);
if (dir == NULL)
{
perror("open dir error");
exit(1);
}
struct dirent *entry;
while ((entry = readdir(dir)) != NULL)
{
char *filename = entry->d_name;
if (strcmp(filename, ".") != 0 && strcmp(filename, "..") != 0)
{
char fullname[256];
sprintf(fullname, "%s/%s", name, filename); // 拼接出完整路径
fetchdir(fullname);
}
}
closedir(dir);
}
int main(int argc, char *argv[])
{
if (argc == 1)
fetchdir(".");
else
{
for (int i = 1; i <= argc; i++)
fetchdir(argv[i]);
}
return 0;
}
dup/dup2
dup
函数用于复制文件描述符,返回最小的尚未被使用的文件描述符。
dup2
函数也用于复制文件描述符,但是可以指定新的文件描述符,将oldfd
拷贝给newfd
。使用dup2
可以实现重定向的功能。
函数原型:int dup(int oldfd);
参数:oldfd
:已有文件描述符
返回值:成功时返回新的文件描述符,新文件描述符和oldfd
指向相同内容;失败时返回 -1。
函数原型:int dup2(int oldfd, int newfd);
参数:
-
oldfd
:要复制的文件描述符。 -
newfd
:指定的新文件描述符。
返回值:成功时返回新的文件描述符(即newfd
),失败时返回 -1。
例:
int fd1 = open(file1, O_RDWR);
int fd2 = open(file2, O_RDWR);
int fdret = dup2(fd1, fd2); //将fd1复制给fd2,最终fd2也指向file1
// 重定向
dup2(fd1, STDOUT_FILENO); //将fd1复制给标准输出,最终标准输出也指向file1