C++linux高并发服务器项目实践 day3
- 文件IO
- 标准C库IO函数与LinuxIO函数
- 虚拟地址空间
- 文件描述符
- Linux系统IO函数
- open与close
- mode:八进制的数,表示用户对创建出的新的文件的操作权限 最终的权限是:mode & ~umask 0777 r(读) w(写) x(可执行)都有这样的权限即都是1,换算成8进制就是7 三个7分别代表当前用户,当前用户所在组的权限,其他组的权限 可以通过umask来查询本机的umask的值是多少 我这里查到的是0002取反后为0775 0777 -> 11111111 & 0775 -> 11111101
- read和write
- lseek函数
- stat与lstat
文件IO
内存===文件
从文件到内存为I
从内存到文件为O
标准C库IO函数与LinuxIO函数
标准C库IO函数
man 3 fopen
可以打开linux的操作手册关于fopen的相关资料
同样的
man 3 fwrite
linux自带的读写,使用一次就会读一次或写一次磁盘,效率其实不如c标准库的高
在vscode中,写入FILE,ctrl加左键可以进入他的定义,发现他的原型是_IO_FILE,再次ctrl加左键,发现该结构体中,有char*类型的各个变量用于读写和缓冲
虚拟地址空间
只有实际内存空间会出现的问题:
- 当剩余内存不足以跑下一个程序时,运行会失败
- 当前面的程序释放后,虽然剩余空间能跑下一个程序,但是内存是分割开的
利用MMU(内存管理单元)来进行虚拟内存和物理内存之间的管理和转换
文件描述符
在Linux的内核区,有一个PCB(进程控制块),可以控制文件描述符
文件描述符存储在文件描述符表内,文件描述符表在数据结构上是数组形式,表的默认大小为1024
文件描述符表的前三位是固定的
就是标准输入、标准输出、标准错误,默认是打开状态
/dev/tty 通过上述3位可以找到当前绑定的设备终端
他们对应的终端是同一个
一个文件能被打开多次,但是多次打开的文件描述符是不一样的
在fopen打开一个文件时,一个文件描述符被占用,只有将其fclose或以linux内部的close关闭后,这个文件描述符才能再次启用
内核会自己寻找空闲的最小的文件描述符
Linux系统IO函数
int open(const char *pathname,int flags);
int open(const char *pathname,int flags,mode_t mode);
int close(int fd);
ssize_t read(int fd,void *buf,size_t count);
ssize_t write(int fd,const void *buf,size_t count);
off_t lseek(int fd,off_t offset,int whence);
int stat(const char *pathname.struct stat *statbuf);
int lstat(const char *pathname,struct stat *statbuf);
open与close
虽然函数重载是c++的内容,但是上述函数是C语言的,所以两个open其实是采用可变参数的方式实现的
open函数的声明在fcntl.h这个头文件内,open内部的flags定义了一些宏,放到另外两个头文件中
第一个open函数用于打开一个已经存在的文件
int open(const char *pathname,int flags);
参数:
- pathname:要打开的文件路径
- flags:对文件的操作权限及其他设置
O_RDONLY,O_WRONLY,O_RDWR 这三个设置是互斥的
返回值:
- 返回一个新的文件描述符,如果调用失败,返回-1
errno:属于Liux系统函数库,库里面的一个全局变量,记录的是最近的错误号
/*#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>//打开一个已经存在的文件int open(const char *pathname, int flags);参数:- pathname:要打开的文件路径- flags:对文件的操作权限及其他设置O_RDONLY,O_WRONLY,O_RDWR 这三个设置是互斥的返回值:返回一个新的文件描述符,如果调用失败,返回-1errno:属于Liux系统函数库,库里面的一个全局变量,记录的是最近的错误号#include <stdio.h>void perror(const char *s);s参数:用户描述,比如hello,最终输出的内容是 hello:xxx(实际错误描述)作用:打印errno对应的错误描述//创建一个新的文件int open(const char *pathname, int flags, mode_t mode);#include <unistd.h>int close(int fd);*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>int main(){int fd = open("a.txt",O_RDONLY);if(fd == -1){perror("open");}//关闭close(fd);return 0;
}
第二个open函数用于创建一个新的文件
int open(const char *pathname,int flags,mode_t mode);
参数:
-
pathname:要打开的文件路径
-
flags:对文件的操作权限及其他设置
- 必选项:O_RDONLY,O_WRONLY,O_RDWR
- 可选项:O_CREAT 文件不存在创建文件 -
mode:八进制的数,表示用户对创建出的新的文件的操作权限
最终的权限是:mode & ~umask
0777 r(读) w(写) x(可执行)都有这样的权限即都是1,换算成8进制就是7
三个7分别代表当前用户,当前用户所在组的权限,其他组的权限
可以通过umask来查询本机的umask的值是多少
我这里查到的是0002取反后为0775
0777 -> 11111111
& 0775 -> 1111110111111101
按位与:0和任何数都为0
umask的作用就是抹去某些权限可以用umask 后面跟上数值来修改例如 umask 022
但是这个修改只能作用在本窗口,新开一个窗口就会失效
read和write
read:
头文件: #include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
参数:
- fd:文件描述符,open得到的,通过这个文件描述符操作某个文件
- buf:需要读取数据存放的缓冲区,数组的地址(传出参数)
- count:指定的数组的大小
返回值:
- 成功:
》0:返回实际的读取到的字节数
=0:文件已经读取完了 - 失败: -1,并且设置errno
write:
头文件: #include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
参数:
- fd:文件描述符,open得到的,通过这个文件描述符操作某个文件
- buf:要往磁盘写入的数据,数组
- count:要写的数据的实际的大小
返回值:
- 成功:实际写入的字节数
- 失败:返回-1,并且设置errno
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main()
{//1.通过open打开english.txt文件int srcfd = open("english.txt",O_RDONLY);if(srcfd == -1){perror("open");return -1;}//2.创建一个新的文件(拷贝文件)int destfd = open("cpy.txt",O_WRONLY|O_CREAT,0664);if(destfd == -1){perror("open");return -1;}//3.频繁的读写操作char buf[1024] = {0};int len = 0;while(( len = read(srcfd,buf,sizeof(buf))) > 0){write(destfd,buf,len);}//4.关闭文件close(destfd);close(srcfd);return 0;
}
lseek函数
标准C库的函数
#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);
Linux系统函数
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
第一个头文件放了一些宏定义,第二个头文件是unix的标准头文件
参数:
- fd:文件描述符,通过open得到的,通过这个fd操作某个文件
- offset:偏移量
- whence:
SEEK_SET:设置文件指针的偏移量
SEEK_CUR:设置偏移量:当前位置+第二个参数offset的值
SEEK_END:设置偏移量:文件大小+第二个参数offset的值
作用:
-
移动文件指针到头文件
lseek(fd,0,SEEK_SET); -
获取当前文件指针的位置
lseek(fd,0,SEEK_CUR); -
获取文件长度
lseek(fd,0,SEEK_END); -
拓展文件的长度,当前文件10b,110b,增加了100个字节
lseek(fd,100,SEEK_END);
注意:需要写一次数据
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main(){int fd = open("hello.txt",O_RDWR);if(fd == -1){perror("open");return -1;}//扩展文件的长度int ret = lseek(fd,100,SEEK_END);if(ret == -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<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<stdio.h>int main(){struct stat statbuf;int ret = stat("a.txt",&statbuf);if(ret == -1){perror("stat");return -1;}printf("size:%ld\n",statbuf.st_size);return 0;
}
stat a.txt
可以查看a的一些信息
ln -s a.txt b.txt
创建一个软链接文件b.txt指向a.txt
若这里使用stat访问b.txt,则实际访问的是a.txt的信息
若想要访问b.txt本身,则需要lstat