文件IO
以内存为主体,看待输入输出;
标准C库IO函数带有缓冲区,效率较高;
虚拟地址空间
虚拟地址空间是不存在的,一个应用程序运行期间对应一个虚拟地址空间;
虚拟地址空间的大小由CPU决定,位数不同,大小不同;
32位的机器,虚拟地址空间如下所示:
虚拟空间会被逻辑管理单元MMU映射到真实的物理内存;
调用linux系统API(系统调用)对内核区进行操作;
文件描述符
位于进程的内核区,由PCB管理,用于定位文件,通过其实现对文件的操作;
PCB中通过数组管理文件描述符,最大为1024,前三个为0-标准输入、1-标准输出、2-标准错误,默认打开;
标准输入、标准输出、标准错位指向当前同一终端(设备文件);
不同的文件描述符对应不同的文件,一个文件可以被打开多次,多次打开的文件描述符不同;
文件描述符的分配,去文件描述符表中找最小的没有分配的文件描述符进行分配即可;
Linux系统的IO函数
open打开文件
/*
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
// flags标记 宏定义在前两个头文件
// 打开一个已经存在的文件
int open(const char *pathname, int flags);
参数:
pathname - 文件路径
flags - 对文件的操作权限设置和其他设置
只读、只写、读写 设置互斥( O_RDONLY, O_WRONLY, or O_RDWR)
返回值:返回文件描述符,调用失败返回-1
errno:属于Linux系统函数库,库里的全局变量,记录最近的错误号
可以通过perror打印errno对应的错误描述
// 创建一个新的文件
int open(const char *pathname, int flags, mode_t mode);
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(){
int fd = open("a.txt" , O_RDONLY);
if(fd==-1){
perror("open");
}
// 关闭文件描述符 使其不指向任何文件
close(fd);
return 0;
}
open创建新文件
/*
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags, mode_t mode);
新参数:
flags - 操作权限和其他设置;
必选项:操作权限;
可选项:O_CREATE 文件不存在创建新文件
int类型 32位 每一位都是一个标志位
mode - 8进制的数,表示用户对创建出新的文件的操作权限
最终权限:mode&~umask(不同用户umask不同,可以改)
eg. 0777 - 最高权限 0777&~0002->0775
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(){
//创建新的文件
int fd = open("create.txt" , O_RDWR|O_CREAT , 0777);
if(fd==-1){
perror("open");
}
close(fd);
return 0;
}
read、write函数
/*
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
参数:
fd - 文件描述符 open得到 通过文件描述符操作文件
buf - 读取数据存放的地方 数组的地址
count - 指定数组大小
返回值
成功
>0 实际读取字节数
=0 文件读取完了
失败
-1 并设置errno
ssize_t write(int fd, const void *buf, size_t count);
参数:
buf - 往磁盘写入的数据
count - 要写的数据实际大小
*/
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(){
// 1. open开
int sfd = open("english.txt" , O_RDONLY);
if(sfd==-1){
perror("open");
return -1;
}
// 2. 创建新的文件
int dfd = open("cpy.txt" , O_WRONLY | O_CREAT , 0664);
if(dfd==-1){
perror("open");
return -1;
}
// 3. 读写
char buf[1024] = {0};
int len = 0;
while((len = read(sfd, buf, sizeof(buf)))>0){
write(dfd , buf , len);
}
// 4. 关闭文件
close(dfd);
close(sfd);
return 0;
}
lseek函数
/*
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
参数:
fd - 文件描述符
offset - 偏移量
whence - 指定标记:
SEEK_SET
设置文件指针偏移量
SEEK_CUR
从当前位置设置偏移量
SEEK_END
从文件结尾设置偏移量
返回值:文件指针的位置
作用:
1. 移动文件指针到头文件
lseek(fd , 0 , SEEK_SET);
2. 获取当前文件指针位置
lseek(fd , 0 , SEEK_CUR);
3. 获取文件长度
lseek(fd , 0 , SEEK_END);
4. 拓展文件长度 10b->110b
lseek(fd , 100 , SEEK_END);
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main(){
int fd = open("hello.txt" , O_RDWR);
if(fd == -1){
perror("open");
return -1;
}
// 扩展文件长度
int cnt = lseek(fd , 100 , SEEK_END);
if(cnt == -1){
perror("lseek");
return -1;
}
// 写入空数据
write(fd , " " , 1);
close(fd);
return 0;
}
stat、lstat函数
/*
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *pathname, struct stat *statbuf);
作用:获取文件相关信息
参数:
pathname - 操作文件路径
statbuf - 结构体变量(传出参数) 用于保存获取到的文件信息
返回值:
成功 - 0
失败 - -1 设置errno
int lstat(const char *pathname, struct stat *statbuf);
参数:
pathname - 操作文件路径
statbuf - 结构体变量(传出参数) 用于保存获取到的文件信息
返回值:
成功 - 0
失败 - -1 设置errno
*/
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
int main(){
struct stat statbuf;
int cnt = stat("a.txt" , &statbuf);
if(cnt==-1){
perror("stat");
return -1;
}
printf("size: %ld\n" , statbuf.st_size);
return 0;
}
stat和lstat的区别在于软链接情况;stat通过软链接查源头文件,lstat不会;
模拟实现ls -l命令
列出当前目录下的文件信息;
// 模拟实现 ls-l指令
// -rw-rw-r-- 1 nowcoder nowcoder 10 8月 28 11:02 a.txt
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <string.h>
// argc>=1 - 文件名称
int main(int argc , char* argv[]){
// 判断输入参数的正确性
if(argc<2){
printf("%s filename\n" , argv[0]);
return -1;
}
// 通过stat获取传入文件信息
struct stat st;
int cnt = stat(argv[1] , &st);
if(cnt == -1){
perror("stat");
return -1;
}
// 获取文件类型和文件权限
char perms[11] = {0}; // 用于保存文件类型和文件权限
// 文件类型
switch(st.st_mode & __S_IFMT){
case __S_IFLNK:
perms[0] = 'l';
break;
case __S_IFDIR:
perms[0] = 'd';
break;
case __S_IFREG:
perms[0] = '-';
break;
case __S_IFBLK:
perms[0] = 'b';
break;
case __S_IFCHR:
perms[0] = 'c';
break;
case __S_IFSOCK:
perms[0] = 's';
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':'-';
// OTHERS
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 ln = st.st_nlink;
// 文件所有者
char* ur = getpwuid(st.st_uid)->pw_name;
// 文件所在组
char* grp = getgrgid(st.st_gid)->gr_name;
// 文件大小
long int filesize = st.st_size;
// 获取修改时间
char* time = ctime(&st.st_mtime);
char mtime[512] = {0};
strncpy(mtime , time , strlen(time) - 1);
char buf[1024];
printf("%s\n" , argv[0]);
sprintf(buf , "%s %d %s %s %ld %s %s" , perms , ln , ur , grp , filesize , mtime , argv[1]);
printf("%s\n" , buf);
return 0;
}
文件属性操作函数
acess - 判断文件权限
/*
#include <unistd.h>
int access(const char *pathname, int mode);
作用:判断文件是否由每个权限 或文件是否存在
参数:
pathname - 文件路径
mode
F_OK 文件是否存在
R_OK 是否有读权限
W_OK 是否有写权限
X_OK 是否有执行权限
返回值
成功 - 0
失败 - -1
*/
#include <unistd.h>
#include <stdio.h>
int main(){
int ret = access("a.txt" , F_OK);
if(ret == -1){
perror("access");
return -1;
}
printf("文件存在\n");
return 0;
}
chmod - 修改文件权限
/*
#include <sys/stat.h>
int chmod(const char *pathname, mode_t mode);
参数:
mode - 需要修改的权限值 八进制rwx
返回值:
成功 - 0
失败 - -1
*/
#include <sys/stat.h>
#include <stdio.h>
int main(){
int ret = chmod("a.txt" , 0775);
if(ret == -1){
perror("chmod");
return -1;
}
printf("修改成功\n");
return 0;
}
chown - 修改文件所有者/所有组
//通过 vim /etc/group 查所属组 和 id
// id 用户名可查uid 和 gid
#include <unistd.h>
int chown(const char *pathname, uid_t owner, gid_t group);
truncate - 缩减/扩展文件大小
/*
#include <unistd.h>
#include <sys/types.h>
int truncate(const char *path, off_t length);
作用:缩减/扩展文件至指定大小
参数:
path - 文件路径
length - 指定最终文件大小
返回值:
成功 - 0
失败 - -1
*/
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
int main(){
int ret = truncate("b.txt" , 20);
if(ret==-1){
perror("truncate");
return -1;
}
printf("修改文件大小成功");
return 0;
}
目录操作函数
mkdir
/*
#include <sys/stat.h>
#include <sys/types.h>
int mkdir(const char *pathname, mode_t mode);
作用:创建目录
参数:
pathname - 创建的目录路径
mode - 权限 八进制
返回值:
成功 - 0
失败 - -1
*/
#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>
int main(){
int ret = mkdir("aaa" , 0777);
if(ret == -1){
perror("mkdir");
return -1;
}
printf("创建目录成功\n");
return 0;
}
rmdir - 只能删除空目录
rename - 文件的重命名
chdir - 修改当前工作目录
getcwd - 获取当前工作路径
/*
#include <unistd.h>
int chdir(const char *path);
作用:修改进程的工作目录
参数:
path - 需要修改的工作目录
#include <unistd.h>
char *getcwd(char *buf, size_t size);
作用:获取当前工作目录
参数:
buf - 存储路径,指向数组
size - 数组大小
返回值:
指向的一块内存 这个数据就是第一个参数
*/
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(){
// 获取当前路径
char buf[128];
getcwd(buf , sizeof(buf));
printf("当前工作目录:%s\n" , buf);
// 改目录
int ret = chdir("/home/nowcoder/linux/lesson13");
if(ret == -1){
perror("chdir");
return -1;
}
printf("已切换目录\n");
int fd = open("chdir.txt" , O_RDWR|O_CREAT , 0664);
if(fd == -1){
perror("open");
return -1;
}
close(fd);
char buf1[128];
getcwd(buf1 , sizeof(buf1));
printf("当前工作目录:%s\n" , buf1);
return 0;
}
目录遍历函数
opendir
readdir
closedir
/*
// 打开一个目录
#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *name);
参数:
name - 打开目录名
返回值:
DIR* - 目录流信息
错误返回NULL
// 读取目录中的数据
#include <dirent.h>
struct dirent *readdir(DIR *dirp);
参数:
dirp - opendir返回的结果
返回值:
struct dirent * - 读取到的文件信息
读到末尾/失败 返回NULL
// 关闭目录
#include <sys/types.h>
#include <dirent.h>
int closedir(DIR *dirp);
参数:opendir的返回值
*/
#define _DEFAULT_SOURCE
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int getnum(const char* path);
// 读某个目录下所有普通文件的个数
int main(int argc , char *argv[]){
if(argc<2){
printf("%s path\n" , argv[0]);
return -1;
}
int num = getnum(argv[1]);
printf("普通文件个数:%d\n" , num);
return 0;
}
// 用于获取目录下所有普通文件个数
int getnum(const char* path){
// 1. 打开目录
DIR* dir = opendir(path);
if(dir == NULL){
perror("opendir");
return -1;
}
// 2. 读取
struct dirent *ptr;
// 普通文件个数
int tol = 0;
while((ptr = readdir(dir))!=NULL){
// 获取名称
char* dname = ptr->d_name;
// 忽略. ..
if(strcmp(dname,".")==0 || strcmp(dname,"..")==0){
continue;
}
// 判断是否是普通文件
if(ptr->d_type == DT_DIR){
// 目录
char newpath[256];
sprintf(newpath , "%s/%s" , path , dname);
tol += getnum(newpath);
}
if(ptr->d_type == DT_REG){
// 普通文件
tol++;
}
}
closedir(dir);
return tol;
}
dup、dup2函数
dup - 复制文件描述符
/*
#include <unistd.h>
int dup(int oldfd);
作用:复制一个新的文件描述符(指向同一文件)
int dup2(int oldfd, int newfd);
*/
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main(){
int fd = open("a.txt" , O_RDWR|O_CREAT , 0664);
int fd1 = dup(fd);
if(fd1 == -1){
perror("dup");
return -1;
}
printf("fd: %d , fd1: %d\n" , fd , fd1);
close(fd);
char* str = "hello";
int ret = write(fd1 , str , strlen(str));
if(ret == -1){
perror("write");
return -1;
}
close(fd1);
return 0;
}
dup2 - 重定向文件描述符
/*
#include <unistd.h>
int dup2(int oldfd, int newfd);
作用:重定向文件描述符 若newfd被使用 会先关闭在指
若oldfd==newfd 不做任何操作
*/
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main(){
int fd = open("1.txt" , O_RDWR|O_CREAT , 0664);
if(fd == -1){
perror("open");
return -1;
}
int fd1 = open("2.txt" , O_RDWR|O_CREAT , 0664);
if(fd1 == -1){
perror("open");
return -1;
}
printf("fd: %d , fd1: %d\n" , fd , fd1);
int fd2 = dup2(fd , fd1);
if(fd2 == -1){
perror("open");
return -1;
}
// 通过fd1写数据
char* str = "hello";
int len = write(fd1 , str , strlen(str));
if(len == -1){
perror("write");
return -1;
}
printf("fd: %d , fd1: %d , fd2: %d\n" , fd , fd1 , fd2);
close(fd);
close(fd1);
return 0;
}
fcntl函数
1. 复制文件描述符;2. 设置/获取文件状态标志
/*
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... );
参数:
fd - 需要操作的文件描述符
cmd - 对文件描述符的操作
1. 复制文件描述符 F_DUPFD
2. 获取指定文件描述符文件状态flag(同open) F_GETFL
3. 设置指定文件描述符文件状态 F_SETFL
O_APPEND 追加数据
O_NOnBLOCK 设置成非阻塞
*/
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
int main(){
// 复制文件描述符
// int fd = open("1.txt" , O_RDONLY);
// int ret = fcntl(fd , F_DUPFD);
// 2. 修改/获取文件状态标志
int fd = open("1.txt" , O_RDWR);
if(fd == -1){
perror("open");
return -1;
}
// 获取flag
int flag = fcntl(fd , F_GETFL);
if(flag == -1){
perror("fcntl");
return -1;
}
flag |= O_APPEND;
// 修改flag
int ret = fcntl(fd , F_SETFL , flag);
if(ret == -1){
perror("fcntl");
return -1;
}
char* str = "nihao";
write(fd , str , strlen(str));
close(fd);
return 0;
}
Node: 一定要注意 如果一个文件的权限是只读 即使通过fcntl设置了O_APPEND还是不能往文件里写