C++linux高并发服务器项目实践 day3

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函数
C++linux高并发服务器项目实践 day3_第1张图片

man 3 fopen
可以打开linux的操作手册关于fopen的相关资料

同样的

man 3 fwrite

linux自带的读写,使用一次就会读一次或写一次磁盘,效率其实不如c标准库的高

C++linux高并发服务器项目实践 day3_第2张图片

在vscode中,写入FILE,ctrl加左键可以进入他的定义,发现他的原型是_IO_FILE,再次ctrl加左键,发现该结构体中,有char*类型的各个变量用于读写和缓冲

虚拟地址空间

C++linux高并发服务器项目实践 day3_第3张图片
只有实际内存空间会出现的问题:

  1. 当剩余内存不足以跑下一个程序时,运行会失败
  2. 当前面的程序释放后,虽然剩余空间能跑下一个程序,但是内存是分割开的

利用MMU(内存管理单元)来进行虚拟内存和物理内存之间的管理和转换

文件描述符

在Linux的内核区,有一个PCB(进程控制块),可以控制文件描述符
文件描述符存储在文件描述符表内,文件描述符表在数据结构上是数组形式,表的默认大小为1024
C++linux高并发服务器项目实践 day3_第4张图片
文件描述符表的前三位是固定的
就是标准输入、标准输出、标准错误,默认是打开状态
/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 
    #include 
    #include 

    //打开一个已经存在的文件
    int open(const char *pathname, int flags);
        参数:
            - pathname:要打开的文件路径
            - flags:对文件的操作权限及其他设置
                O_RDONLY,O_WRONLY,O_RDWR  这三个设置是互斥的
        返回值:返回一个新的文件描述符,如果调用失败,返回-1
    errno:属于Liux系统函数库,库里面的一个全局变量,记录的是最近的错误号

    #include 
    void perror(const char *s);
        s参数:用户描述,比如hello,最终输出的内容是 hello:xxx(实际错误描述)

    作用:打印errno对应的错误描述

    //创建一个新的文件
    int open(const char *pathname, int flags, mode_t mode);

    #include 

    int close(int fd);


*/
#include 
#include 
#include 
#include 
#include 

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 -> 11111101

            11111101
    

    按位与:0和任何数都为0
    umask的作用就是抹去某些权限

    可以用umask 后面跟上数值来修改例如 umask 022
    但是这个修改只能作用在本窗口,新开一个窗口就会失效

read和write

read:
头文件: #include

ssize_t read(int fd, void *buf, size_t count);
参数:

  • fd:文件描述符,open得到的,通过这个文件描述符操作某个文件
  • buf:需要读取数据存放的缓冲区,数组的地址(传出参数)
  • count:指定的数组的大小

返回值:

  • 成功:
    》0:返回实际的读取到的字节数
    =0:文件已经读取完了
  • 失败: -1,并且设置errno

write:
头文件: #include

ssize_t write(int fd, const void *buf, size_t count);
参数:

  • fd:文件描述符,open得到的,通过这个文件描述符操作某个文件
  • buf:要往磁盘写入的数据,数组
  • count:要写的数据的实际的大小

返回值:

  • 成功:实际写入的字节数
  • 失败:返回-1,并且设置errno
#include 
#include 
#include 
#include 
#include 

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
int fseek(FILE *stream, long offset, int whence);

Linux系统函数
#include
#include
off_t lseek(int fd, off_t offset, int whence);
第一个头文件放了一些宏定义,第二个头文件是unix的标准头文件

参数:

  • fd:文件描述符,通过open得到的,通过这个fd操作某个文件
  • offset:偏移量
  • whence:
    SEEK_SET:设置文件指针的偏移量
    SEEK_CUR:设置偏移量:当前位置+第二个参数offset的值
    SEEK_END:设置偏移量:文件大小+第二个参数offset的值

作用:

  1. 移动文件指针到头文件
    lseek(fd,0,SEEK_SET);

  2. 获取当前文件指针的位置
    lseek(fd,0,SEEK_CUR);

  3. 获取文件长度
    lseek(fd,0,SEEK_END);

  4. 拓展文件的长度,当前文件10b,110b,增加了100个字节
    lseek(fd,100,SEEK_END);
    注意:需要写一次数据

#include 
#include 
#include 
#include 
#include 

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
#include
#include

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
#include
#include
#include

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;
}

C++linux高并发服务器项目实践 day3_第5张图片
C++linux高并发服务器项目实践 day3_第6张图片

stat a.txt

可以查看a的一些信息

ln -s a.txt b.txt

创建一个软链接文件b.txt指向a.txt
若这里使用stat访问b.txt,则实际访问的是a.txt的信息
若想要访问b.txt本身,则需要lstat

你可能感兴趣的:(C++Liunx服务器项目,linux,服务器,c++)