当前位置: 首页 > news >正文

操作系统---文件管理

一、系统调用(系统API)

什么是系统调用
  • 由操作系统向应用程序提供的程序接口信息,本质上就是应用程序与操作系统之间交互的接口。

  • 操作系统的主要功能是为了管理硬件资源和为应用软件的开发人员提供一个良好的环境,使得应用程序具有更好的兼容性,为了达到这个目的,内核提供一套统一的具有一定功能的内核接口函数,称为系统调用\系统函数,以C语言函数的格式提供给用户

  • 操作系统负责把应用程序的请求传给内核,由内核调用具体内核功能完成所需请求,结束后把结果通过操作系统的接口函数的返回值传递给调用者

  • UNIX\Linux大部分系统中的系统功能都是通过系统调用来实现的,相当于调用系统函数,但是它们不是真正意义的函数,当调用它们时,进程立即进入内核态,当调用结束,会重新转入回用户态

time ./<可执行文件名> #   统计该进程的时间分布
real    0m0.003s    #   总时间
user    0m0.001s    #   用户态执行时间
sys     0m0.000s    #   内核态执行之间
总时间 = 用户态 + 内核态 + 用户态\内核态切换耗时 + 休眠时间
​
strace ./可执行文件名  #  追踪函数的底层调用过程,以此了解标准库函数中用了哪些系统调用
普通函数与系统函数(系统调用)的区别:
普通函数的调用步骤:
  • 调用者会把要传递的参数压入栈内存

  • 根据函数名也就是该函数的代码段地址,跳转到该函数代码段中执行

  • 从栈内存中弹出传递的参数数据

  • 定义的相关局部变量会入栈到该函数的栈内存进行扩展,并执行相关代码

  • 返回执行结果

  • 销毁该函数的栈内存

  • 返回调用语句处继续执行

系统函数的调用过程:
  • 当执行到系统函数的位置时,会触发软件中断机制

  • 然后进程会转入内核态执行,由内核负责把参数从用户空间拷贝到内核空间

  • 然后内核根据中断编号来执行相应的操作

  • 等执行完毕后,内核再把执行结果从内核空间再拷贝回用户空间中

  • 返回到中断触发位置,转换回用户态继续执行进程

二、一切皆文件

  • 在UNIX\Linux系统中,操作系统把所有服务、设备都抽象成了文件,因为这样可以给各种设备、服务提供同一套统一而简单的操作接口,程序就可以像访问普通文件一样,控制串口、网络、打印机等设备

  • 因此在UNIX\Linux系统中对文件就具有特别重要的意义,一切皆文件,所以大多数情况下,只需要五个基本系统函数操作 open/close/read/write/ioctl,既可以完成对各种设备的输入、输出控制

文件分类:
  • 普通文件 - 包括文本文件、二进制文件、各种压缩包文件等

  • 目录文件 d 类似Windows的文件夹

  • 块设备文件 b 用于保存大块数据的设备,例如磁盘

  • 字符设备文件 c 用于对字符处理的服务、设备,例如 键盘

  • 链接文件 l 类似于Windows的快捷方式

  • 管道文件 p 用于早期进程通信

  • Socket文件\套接字文件 s 用于网络通信

三、文件描述符

什么是文件描述符:
  • 文件描述符是一种非负的整数,用于表示一个打开了的文件

  • 由系统调用(open\creat)返回值,在后续操作文件时可以被内核空间引用去操作对应的文件

  • 它代表了一个内核对象(类似于FILE*),因为内核不能暴露它的内存地址,因此不能返回真正的文件地址给用户

  • 内核中有一张表格,记录了所有被打开的文件对象,文件描述符就是访问这张表格的下标,文件描述符也叫做句柄,是用户操作文件的凭证

  • 内核只要启动,一定会给每个进程打开三个文件描述符,并且是一直默认打开,除非自己手动关闭

//  在<unistd.h> 定义了三个宏
​
#define STDIN_FILENO    0   //  标准输入 文件指针 stdin 
#define STDOUT_FILENO   1   //  标准输出 文件指针 stdout
#define STDERR_FILENO   2   /* 标准输入 文件指针 stderr
​
文件描述符与文件指针:
  • 在Linux系统中打开文件后,内存中(内核区间)就会有一个内核对象,也就是记录了该文件相关信息的结构变量,但是内核为了自己的安全不能把它的地址返回给用户,而且内核区间用户是无法直接访问的,就算返回也无权限访问。

  • 而且一个进程可以同时打开多份文件,所以操作系统会在内核区间创造一份索引表,表中的每一项都指向了一个打开的文件内核对象,而用户拿到的文件描述符就是该索引表的下标(主键),因此不同进程之间直接交互文件描述符是没有意义的。

  • C语言标准函数中,使用文件指针代表文件,文件指针指向的区间是进程的用户 区间的一个结构变量(文件结构变量FILE类型),里面记录了该文件的文件描述符还有一些文件缓冲区,因此某种程度上可以理解文件指针就是文件描述符,只是不同的形式,给不同的对象使用而已

int fileno(FILE *stream);
功能:把文件指针转换成文件描述符
​
FILE *fdopen(int fd, const char *mode);
功能:把文件描述符转换成文件指针

四、文件的创建与打开

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
​
int open(const char *pathname, int flags);
功能:打开文件
pathname:文件路径
flags:打开文件的方式
返回值:文件描述符
​
int open(const char *pathname, int flags, mode_t mode);
功能:打开或创建文件
pathname:文件路径
flags:打开文件的方式
mode:创建文件时的权限
返回值:成功返回文件描述符,失败返回负数
​
注意:open\creat所返回的一定是当前未使用过的最小的文件描述符
注意:一个进程可以同时打开多个文件描述符,最大的数量受limit.h中宏控制数量,在不同的系统标准中不一样,POSIX中不低于16,传统UNIX63个,现代的Linux系统255个
​
int creat(const char *pathname, mode_t mode);
功能:专门用于创建文件,但是基本不用,因为open可以覆盖它的功能
​
flags:O_RDONLY    只读权限O_WRONLY    只写权限O_RDWR      读写权限O_APPEND    打开文件后位置指针指向末尾O_CREAT     文件不存在会创建O_EXCL      如果文件存在则创建失败,如果没有该flags,文件存在直接打开O_TRUNC     清空内容打开O_ASYNC     当文件描述符可读/可写时,会向调用进程发送信号SIGIO
​
mode: 提供一个三位八进制数表示权限,当flags为O_CREAT时必须提供宏名     权限码S_IRWXU  00700  用户权限码S_IRUSR  00400     读权限S_IWUSR  00200     写权限S_IXUSR  00100     执行权限S_IRWXG  00070  同组其它用户权限S_IRGRP  00040 S_IWGRP  00020 S_IXGRP  00010 S_IRWXO  00007 除了同组的用户外,其它用户的权限S_IROTH  00004 S_IWOTH  00002 S_IXOTH  00001 
​
#include <unistd.h>
int close(int fd);
功能:关闭文件,成功0 失败-1
问题1:C语言可不可以定义重名函数?
可以,但是需要在不同作用域下才可以重名,同一作用域下不可以
情况1:在不同的源文件中,static声明的函数可以与其他源文件中的普通函数重名
情况2:在函数内定义的函数可以与普通函数重名
问题2: 系统调用为什么可以重名?
因为系统调用本身就不是真正的函数,而是借助了软中断实现内核执行对应的系统操作,而决定执行哪个系统调用操作是由中断编号决定的,而不是名字
问题3:标准库函数中的 r \ r+ \ w \ w+ \ a \ a+ 分别对应系统调用中的flags的哪些标志?
strace ./a.out
r       O_RDONLY
r+      O_RDWR
w       O_WRONLY|O_CREAT|O_TRUNC  0666
w+      O_RDWR|O_CREAT|O_TRUNC  0666
a       O_WRONLY|O_CREAT|O_APPEND, 0666
a+      O_RDWR|O_CREAT|O_APPEND, 0666

五、文件读写

ssize_t write(int fd, const void *buf, size_t count);
功能:写入文件内容
fd:文件描述符
buf:要写入的数据的内存首地址
count:要写入的字节数
返回值:成功写入的字节数
​
ssize_t read(int fd, void *buf, size_t count);
功能:从文件中读取数据到内存
fd:文件描述符
buf:存储读取到的数据的内存首地址
count:想要读取的字节数,一般就是buf的大小
返回值:成功读取到的字节数
​
注意:它们与标准C的 fwrite/fread 很像,但是它们更纯粹直接

练习1:以二进制形式写入10000000个整数到文件中,分别使用标准IO和系统IO来完成,比较它们的速度谁更快,为什么?

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
​
void std_io(void)
{FILE* fp = fopen("test.txt","w");if(NULL == fp){perror("fopen");return;}
​for(int i=0; i<10000000; i++){int num = rand();fwrite(&num,sizeof(num),1,fp);}
​fclose(fp);
}
​
void sys_io(void)
{int fd = open("test.txt",O_WRONLY|O_CREAT|O_TRUNC,0644);if(0 > fd){perror("open");return;}
​for(int i=0; i<10000000; i++){int num = rand();write(fd,&num,sizeof(num));}
​close(fd);
}
​
int main(int argc,const char* argv[])
{//std_io();                                                                                  sys_io();
}
​

六、系统IO与标准IO

  • 当系统调用被执行时,需要从用户态转换成内核态,执行完毕后又要转回用户态,如果频繁来回切换会导致性能丢失

  • 在标准IO中,内部维护一个缓冲区(1k,1024字节),要写入的数据先存储到缓冲区中,只有当满足特定条件时才会把缓冲区中数据全部通过系统调用write写入文件,从而降低了系统调用的使用频率,减少了状态的切换,因此标准IO的效率要比直接使用系统IO要快

  • 如果想要提高系统IO的速度,可以尝试自己维护一个缓冲区,先把数据存储到缓冲区,等满后再调用write,这样提高速度

  • 普通情况下建议使用标准IO,因为更快,如果对速度还有更高的要求,可以自己使用系统IO+大缓冲区

void sys_io(void)
{int fd = open("test.txt",O_WRONLY|O_CREAT|O_TRUNC,0644);                                                               if(0 > fd){perror("open");return;}
​int buf[1024] = {};
​for(int i=0; i<10000000; i++){int num = rand();buf[i%1024] = num;if(0 == (i+1)%1024){write(fd,buf,sizeof(buf));}}
​close(fd);
}
​
  • UNIX/Linux只有一套读写文件的系统调用,没有像标准C中的文本读写 ,那么可以先把数据转换成字符串(sprintf/sscanf),然后再通过write\read读写,从而实现文本读写的效果

七、文件位置指针

  • 与标准IO的文件读写位置指针一样,系统IO时也会有一个表示位置的指针在移动,会随着读写操作的执行向后自动移动

  • 当需要随机位置进行读写操作时,那么需要移动位置指针的位置

off_t lseek(int fd, off_t offset, int whence);
功能:调整文件指针的位置
fd:要调整的文件描述符
offset:偏移值
whence:基础位置SEEK_SET    文件开头SEEK_CUR    当前位置SEEK_END    文件末尾
返回值:返回当前位置指针的位置 从文件开头计算该位置的字节数
​
注意:系统IO中不需要ftell函数的系统调用,因为lseek就能够完成获取位置指针位置的功能
  • 当超过文件末尾的位置再写入数据时,原末尾与最后的数据之间会有一个“数据黑洞”,但是黑洞不占用磁盘大小,但是会计算成文件的大小,不会影响后序的读写

八、文件同步

  • 大多数磁盘I/O都有缓冲区机制,写入文件其实先写入缓冲区,直到缓冲区满才将其排入写队列。降低写操作的次数,提高写操作效率,但是可能会导致磁盘文件与缓冲区数据不同步,可以借助系统调用来强制让磁盘与缓冲区的内容同步:

void sync(void);
功能:将所有被修改过的缓冲区中的数据排入写队列,立即返回,不等待写入磁盘的完成
int fsync(int fd);
功能:只针对文件fd,并且会等待写入完成才返回
int fdatasync(int fd);
功能:只同步文件fd的数据,不同步文件属性,等待写入完成才返回

九、文件描述符的状态标志

#include <unistd.h>
#include <fcntl.h>
​
int fcntl(int fd, int cmd, ... /* arg */ );
功能:设置或获取文件描述符的状态标志
cmd:F_GETFD     获取文件描述符状态标志F_SETFD     设置文件描述符状态标志F_GETFL     获取文件状态标志其中 O_CREAT\O_EXCL\O_TRUNC 获取不了F_SETFL     追加文件状态标志

十、文件锁

文件锁的意义:
  • 当多个进程同时访问同一个文件时,就有可能造成文件的数据读写混乱,为了解决这一问题可以在读写文件前给文件尝试并加锁

文件锁的限制:
  • 一般情况下,系统提供的文件锁都是劝解锁,内核主要负责文件的加锁和检查是否上锁,而不直接参与锁的控制以及协同操作,这类锁就需要程序员每次用之前都要检查是否被别的进程加锁,再实现并发操作

int fcntl(int fd, int cmd, flock* lock );
功能:对文件的某一部分进行锁操作
struct flock {short l_type;  /* 锁的类型F_RDLCK,读锁F_WRLCK, 写锁F_UNLCK 解锁*/short l_whence;  /* 偏移起点SEEK_SET, SEEK_CUR, SEEK_END */off_t l_start;   /* 偏移值  锁区起始位置:l_whence+l_start */off_t l_len;     /* 锁区长度 0表示锁到文件末尾*/pid_t l_pid;     /* 加锁的进程id -1表示让内核自动设置 */};
cmd可选:F_GETLK     测试lock所表示的锁能否加锁如果可以加则讲lock.l_type设置为F_UNLCK否则会通过lock.l_type返回当前锁信息F_SETLK     设置文件的锁定状态为lock.l_type成功返回0,失败返回-1,如果有其他进程持有该锁导致加锁失败,返回EACCES or EAGAINF_SETLKW    设置文件的锁定状态为lock.l_type成功返回0,否则一直等待,除非被其它信号打断返回-1

进程A:读锁 进程B:读锁 可共享

进程A:写锁 进程B:读锁 互斥

进程A:读锁 进程B:写锁 互斥

进程A:写锁 进程B:写锁 互斥

十一、复制文件描述符

int dup(int oldfd);
功能:复制文件描述符
oldfd:已经打开的要复制的文件描述符
返回值:返回一个新的文件描述符,是当前可用的文件描述符中最小值,失败-1
​
int dup2(int oldfd, int newfd);
功能:复制oldfd文件描述符成指定的文件描述符newfd
如果newfd原来已经被占用,则会把它关闭重新复制
返回值:成功0 失败-1注意:复制成功后,相当于两个文件描述符对应同一个打开的文件
复制文件描述符的意义:
  • 复制成功后,相当于两个文件描述符对应同一个打开的文件,可以以此实现很多奇特的操作,例如重定向文件读写、重定向命令 ls >> file

  • 通过把一个已经打开了的文件描述符fd,通过dup2重定向为标准输入0或者标准输出1,此时就会先把0、1文件关闭,然后0\1指向我们刚刚的文件fd,此后,通过输出语句执行时,相当于把本来要输出到屏幕的内容,直接写如到文件fd中,相当于执行write,如果执行输入语句,相当于把本来要从键盘中输入的内容,直接从文件fd中读取,相当于执行了read操作

十二、获取文件属性

int stat(const char *pathname, struct stat *buf);
功能:根据文件路径获取该文件的属性
int fstat(int fd, struct stat *buf);
功能:根据文件描述符获取该文件的属性
int lstat(const char *pathname, struct stat *buf);
功能:根据文件路径获取链接文件的属性
​
struct stat {dev_t  st_dev;         /* 文件的设备ID*/ino_t  st_ino;         /* 文件的inode节点号*/mode_t st_mode;        /* 文件的类型和权限 */nlink_t st_nlink;      /* 硬链接数量 */uid_t   st_uid;       /* 属主ID */gid_t     st_gid;     /* 属组ID */dev_t     st_rdev;    /* 特殊设备ID */off_t     st_size;    /* 文件的总字节数 */blksize_t st_blksize; /* 文件的IO块数量*/blkcnt_t  st_blocks;  /* 以512字节为一块,该文件占了几块*/struct timespec st_atim;  /* 最后访问时间*/struct timespec st_mtim;  /* 最后内容修改时间*/struct timespec st_ctim;  /* 最后文件状态属性修改时间*/#define st_atime st_atim.tv_sec 时间换算成总秒数     #define st_mtime st_mtim.tv_sec#define st_ctime st_ctim.tv_sec};
​
st_mode 记录了文件类型和权限S_IFMT     0170000   获取文件类型的掩码S_IFSOCK   0140000   socket文件    S_IFLNK    0120000   软链接文件S_IFREG    0100000   普通文件S_IFBLK    0060000   块设备文件S_IFDIR    0040000   目录S_IFCHR    0020000   字符设备文件S_IFIFO    0010000   FIFO    管道文件/*stat(pathname, &sb);if ((sb.st_mode & S_IFMT) == S_IFREG) {/* Handle regular file */}*/
或者借助提供的宏函数来判断文件类型:S_ISREG(m)  是否是普通文件S_ISDIR(m)  目录S_ISCHR(m)  字符设备文件S_ISBLK(m)  块设备文件S_ISFIFO(m) 管道文件S_ISLNK(m)  软链接文件S_ISSOCK(m) socket文件判断权限:S_IRWXU     00700   判断属主的读写执行权限的权限码S_IRUSR     00400   owner has read permissionS_IWUSR     00200   owner has write permissionS_IXUSR     00100   owner has execute permission
​S_IRWXG     00070   判断属组其他用户的读写执行权限的权限码S_IRGRP     00040   group has read permissionS_IWGRP     00020   group has write permissionS_IXGRP     00010   group has execute permission
​S_IRWXO     00007   判断其它用户的读写执行权限的权限码S_IROTH     00004   others have read permissionS_IWOTH     00002   others have write permissionS_IXOTH     00001   others have execute permission
​
​
#include <stdio.h>                                                                                                                                                                                      
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
​
//  显示文件类型和权限
const char* mtos(mode_t m)
{static char s[11];
​if(S_ISREG(m))strcpy(s,"-");else if(S_ISDIR(m))strcpy(s,"d");else if(S_ISCHR(m))strcpy(s,"c");else if(S_ISBLK(m))strcpy(s,"b");else if(S_ISFIFO(m))strcpy(s,"p");else if(S_ISLNK(m))strcpy(s,"l");else if(S_ISSOCK(m))strcpy(s,"s");
​strcat(s,m & S_IRUSR ? "r":"-");strcat(s,m & S_IWUSR ? "w":"-");strcat(s,m & S_IXUSR ? "x":"-");
​strcat(s,m & S_IRGRP ? "r":"-");strcat(s,m & S_IWGRP ? "w":"-");strcat(s,m & S_IXGRP ? "x":"-");
​strcat(s,m & S_IROTH ? "r":"-");strcat(s,m & S_IWOTH ? "w":"-");strcat(s,m & S_IXOTH ? "x":"-");
​return s;
}
​
const char* ttos(time_t t)
{static char s[20];struct tm* time = localtime(&t);sprintf(s,"%04d-%02d-%02d %02d:%02d:%02d",time->tm_year+1900,time->tm_mon+1,time->tm_mday,time->tm_hour,time->tm_min,time->tm_sec);return s;
}
​
int main(int argc,const char* argv[])
{if(2 > argc){printf("User:./a.out <path>\n");return 0;}
​struct stat st;if(-1 == stat(argv[1],&st)){perror("stat");return -1;}
​printf("设备ID:%llu\n",st.st_dev);printf("inode节点号:%lu\n",st.st_ino);printf("文件类型:%s\n",mtos(st.st_mode));printf("最后访问时间:%s\n",ttos(st.st_atime));printf("最后修改时间:%s\n",ttos(st.st_mtime));printf("最后状态修改时间:%s\n",ttos(st.st_ctime));
}
​

十三、文件的权限

测试文件的权限:
int access(const char *pathname, int mode);
功能:测试当前用户对该文件的权限
pathname:要测试的文件路径
mode:R_OK    读权限W_OK    写权限X_OK    执行权限F_OK    测试文件是否存在
返回值:存在权限返回0 否则返回-1
修改文件权限:
int chmod(const char *pathname, mode_t mode);
int fchmod(int fd, mode_t mode);
功能:修改文件的权限为mode,mode可以使用提供的宏,或者直接使用一个八进制数表示三组权限
注意:权限都可以修改
文件的权限屏蔽码:
  • 当使用open\creat 创建文件时,无论给什么权限创建都会成功,但是系统中记录有一个权限屏蔽码会对用户创建的文件的权限进行过滤屏蔽,最终创建出来的文件权限要除去文件屏蔽码

//  可以通过命令 umask 查看当前用户的权限屏蔽码
//  可以通过命令 umask 0xxx 修改当前终端这一次的权限屏蔽码为0xxx
​
mode_t umask(mode_t mask);
功能:给当前进程设置权限屏蔽码
mask:新的屏蔽码
返回值:旧的屏蔽码
注意:只对当前进程有效
​
注意:屏蔽码只影响open、creat ,对于chmod、fchmod 不受影响

十四、修改文件大小

int truncate(const char *path, off_t length);
int ftruncate(int fd, off_t length);
length:想要修改成的文件字节数
成功返回0,失败返回-1
截短末尾丢弃,加长末尾添0
#include <stdio.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
​
int main(int argc,const char* argv[])
{int fd = open("trunc.txt",O_RDWR|O_CREAT|O_TRUNC,0644);if(-1 == fd){perror("open");return -1;}
​//  要映射新文件前,需要让文件大小超过0 才能映射并输入、输出if(-1 == ftruncate(fd,100)){perror("ftruncate");close(fd);return -1;}
​char* str = mmap(NULL,100,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_FILE,fd,0);if((void*)-1 == str){perror("mmap");return 0;}//  往映射后的虚拟内存写入数据 //printf("----%s\n",str);sprintf(str,"hello worldxxxxxxxxxxiid\n");printf("----%s\n",str);
//  str[0] = 'a';   //  取消映射munmap(str,100);
​close(fd);
}    

十五、链接文件

Linux的文件系统会把磁盘分区成主要的两大部分
  • inode信息块

    • 默认128B,里面主要记录文件的权限、大小、所有者、修改时间等基本信息

  • block数据块

    • 默认4Kb,记录了文件名和真正的文件数据内容

  • 每个文件必须拥有一个唯一的inode以及若干个block组成,读取文件需要借助文件所在目录的block中记录的文件inode号,找到该文件的inode,inode中记录了该文件的block位置,从而最终读取文件

什么是软、硬链接文件?
硬链接文件
  • 硬链接文件没有自己inode和block,只是在不同的目录下复制了一份源文件的inode信息,可以通过该inode找到同一份源文件的block

软链接文件
  • 软链接文件会创建自己的新的inode和block,它的inode也是为了找到自己的block,而在它的block中存储的是链接源文件的文件名和inode信息

区别
  • 删除源文件,只是删除源文件的inode块,但是硬链接文件不受影响,而软链接文件就无法访问了

  • 当一个文件的硬链接数删除成0时,文件才被真正的删除

  • 修改硬链接文件内容,源文件也会被修改;而修改软链接的block,不会改变源文件的内容,反而会让软链接无法找到源文件

  • 硬链接不能链接目录,软链接可以

硬链接文件的创建和删除:
int link(const char *oldpath, const char *newpath);
功能:创建硬链接文件
与命令 link \ ln 功能一样
​
int unlink(const char *pathname);
功能:删除文件的硬链接,文件的硬链接数-1int remove(const char *pathname);
功能:与unlink一致,都可以删除普通文件以及硬链接文件
注意:如果删除的文件正在被打开,则会等待文件关闭后删除
注意:如果删除的是软链接文件,则会只删除软链接文件本身,而不会对源文件有任何影响,而且没有任何一个可以借助软链接来删除链接对象文件的函数
软链接文件的创建与读取:
int symlink(const char *target, const char *linkpath);
功能:创建软链接文件
对应命令:ln -s 
​
ssize_t readlink(const char *pathname, char *buf, size_t bufsiz);
功能:获取到软链接文件自身的block内容,也就是它链接对象的文件名,而不会获取到链接对象的文件内容
如果想要读取链接对象的文件内容,还是通过read\write进行

十六、工作目录

  • 工作目录指的是当前进程所在的目录,它是相对路径的起点,在操作文件时,如果没有提供文件的绝对路径信息,那么会操作工作目录下的文件,一般默认下工作目录就是当前进程的目录

char *getcwd(char *buf, size_t size);
功能:获取当前进程的工作路径,相当于命令 pwd
​
int chdir(const char *path);
int fchdir(int fd);
功能:修改工作路径,相当于cd

十七、创建、删除、读取目录

int mkdir(const char *pathname, mode_t mode);
功能:创建空白目录
mode:目录的权限,目录必须有执行权限次才能进入
​
int rmdir(const char *pathname);
功能:删除目录,只能删除空白目录
​
int chdir(const char *path);
int fchdir(int fd);
功能:修改工作路径,相当于cd
​
DIR *opendir(const char *name);
DIR *fdopendir(int fd);
功能:打开目录文件
返回值:成功返回目录流指针,失败返回NULL
​
struct dirent *readdir(DIR *dirp);
功能:从目录流对象中读取一条条目信息
注意:读取完一条条目后,会自动的往后移动,只需要再次调用该函数,即可以读下一条目录
返回值:返回该条目录信息的结构指针或者NULL(读取失败或者读到了目录末尾结束)
结构体 struct dirent里面存储了目录中某个文件的信息
struct dirent {ino_t          d_ino;       /* inode节点号*/off_t          d_off;       /* 下一条条目的偏移量 注意是磁盘偏移量,而非内存地址*/unsigned short d_reclen;    /* 当前条目的长度 */unsigned char  d_type;      /* 文件类型 */char           d_name[256]; /* 文件名 */
};
d_type的取值:DT_BLK  块设备文件DT_CHR  字符设备文件DT_DIR  目录DT_FIFO 管道文件DT_LNK  软链接文件DT_REG  普通文件DT_SOCK socket文件DT_UNKNOWN  未知
​
void rewinddir(DIR *dirp);
功能:复位目录流,设置目录流的位置指针回到开头
​
long telldir(DIR *dirp);
功能:获取当前目录流的位置
​
void seekdir(DIR *dirp, long loc);
功能:设置目录流的读取位置,这样可以进行任意条目的获取
​
int closedir(DIR *dirp);
功能:关闭目录文件

相关文章:

操作系统---文件管理

一、系统调用&#xff08;系统API&#xff09; 什么是系统调用 由操作系统向应用程序提供的程序接口信息&#xff0c;本质上就是应用程序与操作系统之间交互的接口。 操作系统的主要功能是为了管理硬件资源和为应用软件的开发人员提供一个良好的环境&#xff0c;使得应用程序…...

C语言指针详解(三)目录版

C语言指针详解&#xff08;三&#xff09;目录版 1、字符指针变量1.1、字符指针变量的一般应用1.2、常量字符串1.3、常量字符串与普通字符串的区别1.3.1 常量字符串的不可修改性1.3.2 常量字符串的存储 2、数组指针变量2.1、数组指针变量定义2.2、数组指针变量的初始化 3、二维…...

【AI资讯早报】AI科技前沿资讯概览:2024年8月6日早报

【AI资讯早报&#xff0c;感知未来】AI科技前沿资讯概览&#xff0c;涵盖了行业大会、技术创新、应用场景、行业动态等多个方面&#xff0c;全面展现了AI领域的最新发展动态和未来趋势。 1.【图像生成技术再突破】Midjourney V6.1震撼发布&#xff0c;人像生成质量跃上新台阶 …...

等保测评中的密码技术与密钥管理

在信息安全领域&#xff0c;等保测评&#xff08;信息安全等级保护测评&#xff09;是一项重要的安全评估活动&#xff0c;旨在评估信息系统的安全性&#xff0c;并根据评估结果给予相应的安全等级。这一过程中&#xff0c;密码技术与密钥管理发挥着至关重要的作用。本文将详细…...

go语言flag库学习

文章目录 flag基本创建使用方法正常声明全局变量指针短写 flag 基本创建使用方法 func String(name string, value string, usage string) *string func StringVar(p *string, name string, value string, usage string) 正常声明全局变量 package mainimport ("flag…...

2024年必备技能:智联招聘岗位信息采集技巧全解析

随着大数据时代的发展&#xff0c;精准定位职业机会成为程序员求职的关键。本文将深入解析如何利用Python高效采集智联招聘上的岗位信息&#xff0c;助你在2024年的职场竞争中脱颖而出。通过实战代码示例&#xff0c;揭示网络爬虫背后的秘密&#xff0c;让你轻松掌握这一必备技…...

《机器学习by周志华》学习笔记-决策树-02

1、剪枝处理(Pruning) 1.1、背景概念 上文「决策树01」的学习中,我们了解了著名的3种决策树算法ID3、C4.5、CART。这3种决策树算法最根本的核心就是根据特征选择离散属性作为节点来搭建树结构,运用搭好的结构进行推理。 剪枝(pruning)则就是将搭好的决策树去掉一些「非叶节…...

centos Python3.6升级3.8

CentOS系统上升级Python3.6到3.8版本。 步骤 1. 更新系统 在开始升级Python之前&#xff0c;首先需要确保系统是最新的。可以使用以下命令更新CentOS系统&#xff1a; sudo yum update 2. 安装依赖项 升级Python之前&#xff0c;需要安装一些依赖项。运行以下命令安装这些依赖…...

文章解读与仿真程序复现思路——电网技术EI\CSCD\北大核心《基于竞价空间预测的虚拟电厂日前竞价策略》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…...

Simulink模型开发中的一些自动化方法

随着Simulink模型的产品化开发进程&#xff0c;许多模型开发人员会关心模型的建模自动化问题。比如如何对模型中的元素进行批量查找和修改&#xff1b;如何构建自己的建模规则对模型进行检查&#xff1b;如何实现测试自动化等。在这些使用场景中我们都需要了解一些Simulink函数…...

RabbitMQ消费者消费消息失败处理

在我们开发分布式系统的过程中&#xff0c;RabbitMQ这样的消息队列无疑是实现微服务间通信的利器。然而&#xff0c;消息处理失败在所难免。当我们面临消费消息失败的情况时&#xff0c;该如何应对呢&#xff1f;在这篇博客中&#xff0c;我将带你深入探讨RabbitMQ消费者的消息…...

Apache Kylin分布式的分析数据仓库

Apache Kylin 是一个分布式的分析数据仓库&#xff0c;用于大数据上的超快在线分析处理 (OLAP)。它能够在大规模数据集上提供亚秒级的查询响应时间&#xff0c;并支持标准的 ANSI SQL 查询接口。Kylin 最初由 eBay 开发&#xff0c;后来捐赠给 Apache 软件基金会&#xff0c;现…...

informer中DeltaFIFO机制的实现分析与源码解读

informer中的DeltaFIFO机制的实现分析与源码解读 DeltaFIFO作为informer中重要组件&#xff0c;本文从源码层面了解是如何DelatFIFO是实现的。 DeltaFIFO的定义 找到delta_fifo.go的源码&#xff0c;位于client-go/tools/cache/delta_fifo.go 代码结构大致如下: store定义…...

树莓派下,centos7amr64下,搭建目标检测开发环境,java语言

在树莓派(Raspberry Pi)上使用CentOS 7 ARM64搭建基于Java的目标检测开发环境,可以按照以下步骤进行。需要注意的是,CentOS 7 ARM64的官方镜像可能不支持树莓派的某些硬件,因此你可能需要寻找第三方镜像或进行一些额外的配置。 1. 安装CentOS 7 ARM64 首先,确保你已经正…...

SpringBoot+Redis 发布与订阅

两个应用都引入 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artif…...

huggingface无法访问怎么办?一招教你解决,使用hf-mirror.com镜像站快速下载各种大模型

huggingface.co无法访问怎么办&#xff1f; 请访问 https://hf-mirror.com/ hf-mirror.com是一个旨在为中国国内的人工智能开发者提供更快更稳定下载服务的镜像站点&#xff0c;它镜像了Hugging Face的huggingface.co网站上的模型和数据集资源。由于网络环境和地理限制&#xf…...

如何用密码保护你的 WordPress 管理员 (wp-admin) 目录

在维护 WordPress 网站时&#xff0c;确保 wp-admin 目录安全是非常重要的。为该目录添加密码保护可以有效提高网站安全性&#xff0c;防止未经授权的访问。这篇文章将介绍实现这一目标的两种方法。 1.为什么要为 wp-admin 目录添加密码保护 WordPress 管理员后台是网站的核心…...

java 程序包org.junit.jupiter.api不存在

问题描述 正常启动springboot报错java 程序包org.junit.jupiter.api不存在。 问题分析 pom文件中缺少junit依赖&#xff0c;但是添加后问题仍然没解决&#xff0c;后面把test部分删掉解决问题。 解决方法 添加junit依赖 <dependency><groupId>junit</group…...

简单的docker学习 第4章 docker容器

第4章 Docker容器 4.1 容器基础 4.1.1 容器启动流程 通过 docker run 命令可以启动运行一个容器。该命令在执行时首先会在本地查找指定的镜像&#xff0c;如果找到了&#xff0c;则直接启动&#xff0c;否则会到镜像中心查找。如果镜像中心存在该镜像&#xff0c;则会下载到…...

零基础入门转录组数据分析——机器学习算法之SVM-RFE(筛选特征基因)

零基础入门转录组数据分析——机器学习算法之SVM-RFE&#xff08;筛选特征基因&#xff09; 目录 零基础入门转录组数据分析——机器学习算法之SVM-RFE&#xff08;筛选特征基因&#xff09;1. SVM-RFE基础知识2. SVM-RFE&#xff08;Rstudio&#xff09;——代码实操2. 1 数据…...

Python酷库之旅-第三方库Pandas(067)

目录 一、用法精讲 266、pandas.Series.dt.second属性 266-1、语法 266-2、参数 266-3、功能 266-4、返回值 266-5、说明 266-6、用法 266-6-1、数据准备 266-6-2、代码示例 266-6-3、结果输出 267、pandas.Series.dt.microsecond属性 267-1、语法 267-2、参数 …...

Spring快速学习

目录 IOC控制反转 引言 IOC案例 Bean的作用范围 Bean的实例化 bean生命周期 DI 依赖注入 setter注入 构造器注入 自动装配 自动装配的方式 注意事项; 集合注入 核心容器 容器的创建方式 Bean的三种获取方式 Bean和依赖注入相关总结 IOC/DI注解开发 注解开发…...

【Web开发手礼】探索Web开发的秘密(十五)-Vue2(2)AJAX、前后端分离、前端工程化

主要介绍了AJAX、前后端分离所需的YApi、前端工程化所需要的环境安装&#xff01;&#xff01;&#xff01; 目录 前言 AJAX ​原生Ajax Axios Axios入门 案例 前后端分离开发 YApi ​前端工程化 环境准备 总结 前言 主要介绍了AJAX、前后端分离所需的YApi、前端工…...

Phalco安装过程以及踩的一些坑(mac环境)

一 背景 公司用Phalcon框架好长时间了,中途发现了一些Phalcon使用的上的问题,于是想在本地搭建一套Phalcon的环境,方便排查问题使用。 二 Mac系统下的安装 看了很多说法,最终发现还是官网给力,安装Phalcon使用下列命令即可(前提条件是PHP已安装好,工具pecl也安装好了):…...

Ubuntu修改双系统默认启动顺序

1.打开grub的默认启动配置文件 sudo gedit /etc/default/grub# If you change this file, run update-grub afterwards to update # /boot/grub/grub.cfg. # For full documentation of the options in this file, see: # info -f grub -n Simple configurationGRUB_DEFAULT…...

高仲富:49岁搞AI,白天种菜卖菜,晚上学数学搞程序

这是《开发者说》的第13期&#xff0c;本期我们邀请的开发者是高仲富&#xff0c;曾是一位数学老师&#xff0c;自学成为一名程序员&#xff0c;在北京漂过&#xff0c;后逃回了成都&#xff0c;一边与病魔抗争&#xff0c;一边写代码&#xff0c;一写就是15年&#xff0c;制作…...

光线追踪(纹理映射)

最近在跟着ray trace in one week来学习光线追踪&#xff08;很多概念茅塞顿开&#xff09;做到一半想着记录一下&#xff08;比较随心&#xff09;上面是之前的效果。ray trace in one week Texture Coordinates for Spheres&#xff08;球体纹理坐标&#xff09; u, v 纹理…...

传统产品经理VS现在AI产品经理,你要学习的太多了,超详细收藏我这一篇就够了

传统产品经理想要转行成为AI产品经理&#xff0c;需要经历一系列的学习和实践过程。下面是一份详细的学习路线图&#xff0c;旨在帮助你顺利转型。 学习路线图 了解AI基础知识 AI概览&#xff1a;阅读《人工智能&#xff1a;一种现代的方法》这样的书籍&#xff0c;以获得对AI…...

C#使用Socket实现TCP服务器端

1、TCP服务器实现代码 using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks;namespace PtLib.TcpServer {public delegate void Tcp…...

MTK联发科MT8766/MT8166安卓核心板性能参数对

MT8766核心板 采用联发科四核2G主频芯片方案&#xff0c;国内4G全网通。12nm先进工艺&#xff0c;支持 Android9.0系统。 GPU 采用超强 IMG GE8300 ,主频600MHz。支持高速LPDDR4/X&#xff0c;主频高达1600MHz。支持EMMC5.1。标配 WIFI 802.11 ac/abgn&#xff0c;BT 5.0。 支持…...