库函数 vs 系统调用
标准C库函数带有缓冲区,能够提高效率。而系统调用,每调用一次就会去读/写一次磁盘。
所有,在不同的场景要选择使用库函数还是系统调用。如,网络通信的时候,避免数据一直在缓冲区中,所以要使用系统调用。而操作磁盘时,使用库函数。
- 标准C库IO函数
相关概念
虚拟地址空间
文件描述符
每个进程默认能同时打开的文件个数为1024个,前3个(标准输入、标准输出、标准错误)默认是打开的,并且这3个是指向同一个终端。
不同的文件描述符可以指向同一个文件。
相关函数
open、close
#includ e <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
功能
- 打开一个已经存在的文件
返回值
- 成功:返回一个新的文件描述符
- 失败:返回 -1,并设置 errno
参数
- pathname:要打开的文件路径
- flags:对文件的操作权限的设置和其他设置
- 必选项:O_RDONLY,O_WRONLY,O_RDWR(3选1)
- 可选项:O_CREAT(文件不存在,创建新文件)等(与必选项按位或)
#includ e <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags, mode_t mode);
功能
- 创建一个新的文件
返回值
- 同上
参数
- pathname:同上
- flags:同上
- mode:八进制的数,表示创建出的新的文件的操作权限。如:0775,最终的权限是
mode & ~umask
的值。umask的作用就是抹去某些权限。
// 关闭文件描述符
int close(int fd);
read、write
-
read
#include <unistd.h> ssize_t read(int fd, void *buf, size_t count);
功能
- 读数据
返回值
- 成功:返回读取到的字节数,如果为0表示文件读完了
- 失败:-1,设置errno
参数
- fd:文件描述符,通过open得到的,可通过fd操作这个文件
- buf:将读取的数据存放到buf,数组的地址(传出参数)
- count:指定的数组的大小
-
write
#include <unistd.h> ssize_t write(int fd, const void *buf, size_t count);
功能
- 写数据
返回值
- 成功:返回实际写入的字节数
- 失败:-1,设置errno
参数
- fd:文件描述符
- buf:要往磁盘写入的数据,数组
- count:要写的数据的实际的大小
lseek
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
功能
- 移动文件指针
返回值
- 成功:返回偏移后文件指针的位置
- 失败:-1,设置errno
参数
- fd:文件描述符
- offset:偏移量
- whence:
- SEEK_SET:从文件头开始偏移offset
- SEEK_CUR:从文件当前位置开始偏移offset
- SEEK_END:从文件尾开始偏移offset
// lseek的常见作用:
// 1、移动文件指针到文件头
lseek(fd, 0, SEEK_SET);
// 2、获取当前文件指针的位置
lseek(fd, 0, SEEK_CUR);
// 3、获取文件长度
lseek(fd, 0, SEEK_END);
// 4、拓展文件的长度,当前文件10B,要拓展100B,拓展的是空字符
lseek(fd, 100, SEEK_END);
stat、lstat
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
// 获取一个文件的相关信息,保存到statbuf中
int stat(const char *pathname, struct stat *statbuf);
// 用来获取一个软链接文件的信息
int lstat(const char *pathname, struct stat *statbuf);
返回值
- 成功:0
- 失败:-1,设置errno
参数
- pathname:操作的文件的路径
- statbuf:结构体变量,用于保存获取到的文件的信息(传出参数)
- stat结构体
模拟实现 ls -l 命令
#include<sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <string.h>
// 模拟实现 ls -l 指令
// -rw-rw-r-- 1 zengyq zengyq 43 Mar 2 21:16 a.txt
int main(int argc, char* argv[]) {
// 1. 判断输入的参数是否正确
if(argc < 2) {
printf("%s filename\n", argv[0]);
return -1;
}
// 2. 通过 stat 获取用户传入的文件信息
struct stat st;
int ret = stat(argv[1], &st);
if(ret == -1) {
perror("stat");
return -1;
}
// 3. 获取文件类型和文件权限
// (字符串最后一个是空字符'\0',所以长度是11)
char perms[11] = {0}; // 用于保存文件类型和权限
// 文件类型
switch (st.st_mode & S_IFMT)
{
case S_IFSOCK:
perms[0] = 's';
break;
case S_IFLNK:
perms[0] = 'l';
break;
case S_IFREG:
perms[0] = '-';
break;
case S_IFBLK:
perms[0] = 'b';
break;
case S_IFDIR:
perms[0] = 'd';
break;
case S_IFCHR:
perms[0] = 'c';
break;
case S_IFIFO:
perms[0] = 'p';
break;
default:
perms[0] = '?';
break;
}
// 文件的访问权限
// 文件所有者
perms[1] = st.st_mode & S_IRUSR ? 'r' : '-';
perms[2] = st.st_mode & S_IWUSR ? 'w' : '-';
perms[3] = st.st_mode & S_IXUSR ? 'x' : '-';
// 文件所在组
perms[4] = st.st_mode & S_IRGRP ? 'r' : '-';
perms[5] = st.st_mode & S_IWGRP ? 'w' : '-';
perms[6] = st.st_mode & S_IXGRP ? 'x' : '-';
// 其他人
perms[7] = st.st_mode & S_IROTH ? 'r' : '-';
perms[8] = st.st_mode & S_IWOTH ? 'w' : '-';
perms[9] = st.st_mode & S_IXOTH ? 'x' : '-';
// 硬链接数
int linkNum = st.st_nlink;
// 文件所有者
char *fileUser = getpwuid(st.st_uid)->pw_name;
// 文件所在组
char *fileGrp = getgrgid(st.st_gid)->gr_name;
// 文件大小
off_t fileSize = st.st_size;
// 获取修改时间
char *time = ctime(&st.st_mtime);
char mtime[512] = {0};
strncpy(mtime, time, strlen(time) - 1); // 去掉time末尾的换行
char buf[1024];
sprintf(buf, "%s %d %s %s %ld %s %s", perms, linkNum, fileUser, fileGrp, fileSize, mtime, argv[1]);
printf("%s\n", buf);
return 0;
执行结果如下:
文件属性操作函数
access、chmod、truncate
-
access
#include <unistd.h> int access(const char *pathname, int mode);
功能
- 判断当前进程对某个文件是否有某个权限,或者判断文件是否存在
返回值
- 成功:0
- 失败:-1,并设置errno
参数
- pathname:判断的文件的路径
- mode:
- R_OK:判断是否有读权限
- W_OK:判断是否有写权限
- X_OK:判断是否有执行权限
- F_OK:判断文件是否存在
-
chmod
#include <sys/stat.h> int chmod(const char *pathname, mode_t mode);
功能
- 修改文件的权限
返回值
- 成功:0
- 失败:-1,并设置errno
参数
- pathname:需要修改的文件的路径
- mode:需要修改的权限值,八进制的数,如:0777
-
truncatre
#include <unistd.h> #include <sys/types.h> int truncate(const char *path, off_t length);
功能
- 缩减或扩展文件的大小到指定的大小
返回值
- 成功:0
- 失败:-1,并设置errno
参数
- path:需要修改的文件的路径
- length:需要最终文件变成的大小
目录操作函数
mkdir、rmdir、rename、chdir、getcwd
#include <sys/stat.h>
#include <sys/types.h>
int mkdir(const char *pathname, mode_t mode);
功能
- 创建一个目录
返回值
- 成功:0
- 失败:-1,并设置errno
参数
- pathname:创建的目录的路径
- mode:权限,八进制的数(一个目录必须有可执行权限才能进入这个目录)
// 删除一个空目录
int redir(const char *pathname);
// 修改文件的名称和位置
int rename(const char *oldpath, const char *newpath);
// 修改进程当前的工作目录
int chdir(const char *path);
// 获取进程当前的工作目录,保存到大小为size的数组buf中
char *getcwd(char *buf, size_t size);
dup、dup2
#include <unistd.h>
// 复制文件描述符oldfd,返回一个最小的可用的新文件描述符
int dup(int oldfd);
// 重定向文件描述符:newfd 原来指向的文件close,然后指向oldfd指向的文件。
// oldfd 必须是一个有效的文件描述符
int dup2(int oldfd, int newfd);
fcntl
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ );
功能
- 操作文件描述符
返回值
- 成功:0
- 失败:-1,并设置errno
参数
- fd:需要操作的文件描述符
- cmd:表示对fd进行什么操作
- F_DUPFD:复制文件描述符,复制的是第一个参数fd,得到一个新的文件描述符
- F_GETFL:获取指定文件描述符的文件状态,获取的flag和通过open函数传递的flag是相同的东西
- FSETFL:设置文件描述符的状态flag
- …
- …:(可变参数)cmd所表示的操作需要的参数,不同的操作需要的参数个数可能不同
评论区