【Linux】基础IO,详解系统文件IO

news2024/12/29 16:53:20

目录

C语言文件操作简单回顾

C语言相关文件接口汇总

默认打开的三个流

系统文件I/O

open

open的第一个参数

open的第二个参数

open的第三个参数

open的返回值

close

write

read

文件描述符

什么是文件描述符

文件描述符分配规则

重定向

重定向的本质 

输出重定向 '>'

追加重定向'>>'

输入重定向'<'

dup2函数

添加重定向到自己做的shell中

FILE

用户级缓冲区

结语


C语言文件操作简单回顾

C语言相关文件接口汇总

文件的打开和关闭
fopen打开文件
fclose关闭文件
文件的顺序读写
fgetc字符输入函数
fputc字符输出函数
fgets文本行输入函数
fputs文本行输出函数
fscanf格式化输入函数
fprintf格式化输出函数
fread二进制输入
fwrite二进制输出
文件的随机读写
fseek根据文件指针的位置和偏移量来定位文件指针
ftell返回文件指针相对于起始位置的偏移量
rewind让文件指针的位置回到文件的起始位置

对于相关操作博主就不详细进行演示了,想回顾的可以看下以前写的有关c语言文件操作的博客。

C语言文件操作

对文件进行写入操作

#include <stdio.h>
//写入操作
int main()
{
    FILE* fp = fopen("markdown.txt","w");
    fprintf(fp,"我是写入测试文件\n");
    fclose(fp);
    return 0;
}

 对上面的文件进行读取操作:

#include <stdio.h>

int main()
{
    FILE* fp = fopen("markdown.txt","r");
    char buff[256];
    fgets(buff,sizeof(buff)-1,fp);
    printf("%s\n",buff);
    fclose(fp);
    return 0;
}

我们成功将之前写入文件的数据打印到了屏幕上。

文件打开方式总结:

文件使用方式含义如果指定文件不存在
“r”(只读)为了输入数据,打开一个已经存在的文本文件出错
“w”(只写)为了输出数据,打开一个文本文件建立一个新的文件
“a”(追加)向文本文件尾添加数据建立一个新的文件
“rb”(只读)为了输入数据,打开一个二进制文件出错
“wb”(只写)为了输出数据,打开一个二进制文件建立一个新的文件
“ab”(追加)向一个二进制文件尾添加数据出错
“r+”(读写)为了读和写,打开一个文本文件出错
“w+”(读写)为了读和写,建议一个新的文件建立一个新的文件
“a+”(读写)打开一个文件,在文件尾进行读写建立一个新的文件
“rb+”(读写)为了读和写打开一个二进制文件出错
“wb+”(读写)为了读和写,新建一个新的二进制文件建立一个新的文件
“ab+”(读写)打开一个二进制文件,在文件尾进行读和写建立一个新的文件

默认打开的三个流

        Linux下一切皆文件,在任意进程运行时,都会默认打开三个流,分别为标准输出流(显示屏),标准输入流(键盘),标准错误流(显示器)C语言中用stdout、stdin、stderror来表示。之所以我们能使用printf函数打印结果到屏幕上,其底层实际上就是将我们要打印的内容写入到了标准输出流之中。

        C语言的文件操作都是对底层系统调用的封装,在回顾了C语言文件操作后,我们来学习和使用系统文件操作的内容。

系统文件I/O

        该部分我们先学会使用相关的系统调用接口,然后通过理解文件描述符,来弄明白重定向的操作。

open

//需要的头文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
//函数原型
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);

open的第一个参数

第一个参数pathname要打开或创建的目标文件

我们可以以两种方式:

  1. 以路径的方式给出,会在对应路径下创建和打开该文件。

  2. 以文件名的方式给出,会默认在当前路径下创建和打开文件。

open的第二个参数

第二个参数flags: 打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行“或”运算,构成flags。

或运算的奇妙用法:我们可以以对应的比特位是否为0来判断是否要进行对应的操作。

flags参数选项(只列举了常用的六个选项,前三个必须指定一个且只能选择一个):

O_RDONLY只读打开
O_WRONLY只写打开
O_RDWR读,写打开
O_CREAT若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限
O_APPEND追加写
O_TRUNC有写的权限时,会先清空已存在的文件,之后再写入

传入flags的每一个选项在系统当中都是以宏的方式进行定义的:

#define O_RDONLY         00
#define O_WRONLY         01
#define O_RDWR           02
#define O_CREAT        0100

        这些选项的二进制序列都只有一个比特位为1(O_RDONLY为0,代表其为默认选项),且各选项比特位为1的位置不同,此时,我们就可以通过或运算将不同的选项组合起来,open内部通过判断对应比特位是否为1就可以知道是否选择了对应的选项了。

//模拟open判断机制
int open(pathname,flags,mode)
{
    if(flags & O_WRONLY) //按位与运算结果为1 ,证明选择了该选项,否则为0
    {
        //TODO
    }
    if(flags & O_RDWR)
    {
        //TODO
    }
    //......  
}

open的第三个参数

mode参数表示我们要创建的文件的权限

文件是有权限的

如果我们通过open函数创建文件时没给mode会导致创建的文件的权限是乱码,无法打开该文件。

我们一般以8进制的形式作为mode:

 例如,如果将mode设置为0666,文件创建出时的权限应该为:

-rw-rw-rw-

但实际上创建的权限为:

-rw-rw-r--

这时因为umask(文件默认掩码)的原因,假设默认权限是mask,则实际创建的出来的文件权限是: mask & ~umask

umask(0).  //umask默认为2,可以通过指令更改,程序中可以通过umask函数更改

如果不需要创建新文件,mode参数可以忽略。

open的返回值

  • 成功:新打开的文件描述符(文件描述符的概念后面有讲解,可以先直接跳过去看)

  • 失败:-1

close

使用close关闭文件

 #include <unistd.h>
 int close(int fd);

参数讲解:

  • fd:要关闭文件的文件描述符

返回值:

  • 关闭成功返回0,失败返回-1。

write

我们使用write向文件中写入信息

//头文件
#include <unistd.h>
//函数原型
ssize_t write(int fd, const void *buf, size_t count);

参数讲解:

  • fd: 要写入文件的文件描述符

  • buf: 存放要写入信息的缓冲区

  • count:要写入信息的大小

返回值:

  • 如果数据写入成功,返回实际写入数据的字节个数。

  • 如果数据写入失败,返回-1。

操作示例:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main()
{
    int fd = open("log.txt",O_WRONLY|O_CREAT,0666);
    char buff[256];
    int size = snprintf(buff,sizeof(buff)-1,"新建文件log.txt\n");
    buff[size] = 0;
    write(fd,&buff,strlen(buff));
    close(fd);
    return 0;
}

我们创建了一个新文件log.txt,并写入了信息。

read

我们使用read从文件中读信息

//头文件
#include <unistd.h>
//函数原型
ssize_t read(int fd, void *buf, size_t count);

参数讲解:

  • fd:要读取文件的文件描述符

  • buf:读取的数据存放的地方

  • count:读取数据的大小

返回值:

  • 如果数据读取成功,返回实际读取数据的字节个数。

  • 如果数据读取失败,返回-1。

读取刚才创建的文件:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main()
{
    int fd = open("log.txt",O_RDONLY);
    char buff[256];
    ssize_t size = read(fd,buff,sizeof(buff)-1);
    buff[size] = 0;
    printf("%s\n",buff);
    close(fd);
    return 0;
} 

因为读取的文件里本身就有换行符,所以打印了两次换行。

文件描述符

什么是文件描述符

        我们都知道,一个程序要想运行,得先从磁盘加载到内存中,此时操作系统会创建该进程对应的PCB(Linux下是task_struct),进程地址空间(mm_struct),页表等数据结构,之后再通过页表建立虚拟内存和物理内存直接的映射关系。

        我们可以通过进程来打开文件,当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件。于是就有了file结构体,表示一个已经打开的文件对象。内存中有很多打开的文件,那么我们如何对他们进行管理呢?

        所以必须让进程和文件关联起来。每个进程PCB中都有一个指针*files, 指向一张表files_struct,该表最重要的部分就是包涵一个指针数组,每个元素都是一个指向打开文件的指针!所以,本质上,文件描述符就是该数组的下标。所以,只要拿着文件描述符,就可以找到对应的文件。

文件描述符分配规则

#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h> 
#include <sys/stat.h>
#include <unistd.h>

int main()
{
    for(int i=0;i<6;++i)
    {
        int fd = open("new file",O_WRONLY|O_CREAT,0666);
        printf("%d ",fd);
    }
    printf("\n");

    return 0;
}

我们从上面可以看到,文件描述符以此从3开始递增分配,为什么是这样呢?

  • 首先通过之前的讲解,我们知道了每个进程都会默认打开三个流,0就是标准输入流,对应键盘;1就是标准输出流,对应显示器;2就是标准错误流,也是对应显示器

        很明显,文件描述符的分配是从小到大来分配的,前面哪里有空位就分配到哪个

我们可以试着关闭默认打开的三个流来进行测试。

#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h> 
#include <sys/stat.h>
#include <unistd.h>

int main()
{
    close(0); //关闭标准输入
    int fd = open("new file",O_WRONLY|O_CREAT,0666);
    printf("%d\n",fd);
    return 0;
}

 关闭标准输出流:

我们可以看到什么都没打印,为什么?

  • 我们的printf函数底层就是把数据写入到标准输出流(即显示器),关闭后所以才什么都没有打印。

那么数据写到了文件里吗?是的。

1被写到了创建的文件中,而这也就是我们所说的重定向!

重定向

重定向的本质 

改变文件描述符0,1所指向的打开的文件

输出重定向 '>'

命令行操作:

 代码演示:

#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h> 
#include <sys/stat.h>
#include <unistd.h>

int main()
{
    close(1); //关闭标准输入
    int fd = open("newfile",O_WRONLY|O_CREAT|O_TRUNC,0666);
    printf("i am new file%d\n",fd);
    fflush(stdout);
    close(fd);
    return 0;
}

追加重定向'>>'

命令行操作:

在该文件原有内容的基础上追加了内容。

代码演示:

#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h> 
#include <sys/stat.h>
#include <unistd.h>
int main()
{
    close(1); //关闭标准输入
    int fd = open("newfile",O_WRONLY|O_CREAT|O_APPEND,0666);
    printf("i am append data %d\n",fd);
    fflush(stdout);
    close(fd);
    return 0;

}

追加重定向和输出重定向的区别:

  • 输出重定向是覆盖式输出数据

  • 而追加重定向是追加式输出数据。

输入重定向'<'

将从标准输入流中读取改为从文件中读取。

#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h> 
#include <sys/stat.h>
#include <unistd.h>

int main()
{
    close(0);
    int fd = open("newlog.txt",O_RDONLY);  
    char buff[64];
    while(scanf("%s",buff)!=EOF)      //从newlog.txt文件中获取输入
    {
        printf("%s\n",buff);
    }
    close(fd);
    return 0;
}

  • scanf函数是默认从stdin读取数据的,而stdin指向的FILE结构体中存储的文件描述符是0,因此scanf实际上就是从文件描述符为0的文件(标准输入流)中读取数据。

dup2函数

        像上面那样先关闭对应的文件描述符来实现重定向是很low的,因此还提供了名为dup2的系统调用。

原理:

把新打开文件的fd拷贝覆盖到指定fd下

 //头文件
 #include <unistd.h>
 //函数原型
 int dup2(int oldfd, int newfd);
  • dup2 会把 arry[oldfd]的内容拷贝到arry[newfd]中

参数讲解:

  • oldfd: 要进行拷贝的fd

  • newfd: 将被覆盖的fd

返回值:

  • 如果调用成功,返回newfd,否则返回-1。

改造下前面的代码:

#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h> 
#include <sys/stat.h>
#include <unistd.h>

int main()
{
    printf("\n");
    int fd = open("newfile",O_WRONLY|O_CREAT,0666);
    dup2(fd,1);
    printf("i am new file%d\n",fd);
    fflush(stdout);
    close(fd);
    return 0;
}

一样实现了之前的效果。

添加重定向到自己做的shell中

在进程控制部分时,我们写了一个自己的shell,这次我们将重定向功能也添加到其中。

Linux\进程控制精讲,简单实现一个shell_Sola一轩的博客-CSDN博客

如何添加重定向功能?

  1. 检测是否需要重定向,确定是哪种重定向,记录下对应的状态

  2. 获取重定向符号后的文件名

  3. 使用对应文件打开方式打开文件后,使用dup2函数

#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>



#define SIZE 1024
char CommandLine[SIZE];  //存放输入的指令

#define OPT_NUM 64
char* Myargv[OPT_NUM];  //存放分割后的程序指令

//保存上次运行时的退出码和退出信号
int lastCode;
int lastSignal;
#define NOREDIR 0
#define INREDIR 1   //输入重定向
#define OUTREDIR 2  //输出重定向
#define APPREDIR 3  //追加重定向

int RedirMode = NOREDIR;         //重定向的模式
char* Redirfile = NULL;  //存储要重定向的文件名

#define RmSpace(start) do{ \
 while(isspace(*start)) ++start;\
} while(0)                 //定义宏函数


void CommandCheck(char* cl)  //检测是否需要重定向
{
    assert(cl!=NULL);
    char* start = cl;
    char* end = cl+strlen(cl);

    while(start < end)
    {
        if(*start == '>')  //输出重定向
        {
          *start = '\0';
          ++start;
          if(*start == '>')  //追加重定向
          {
            RedirMode = APPREDIR;
            start++;
          }
          else
          RedirMode = OUTREDIR;
          //去掉空格
          RmSpace(start);

          Redirfile = start;       //获取文件名
          break;
        }

        if(*start == '<')   //输入重定向
        {
          *start = '\0';
          RedirMode = INREDIR;
          ++start;
          RmSpace(start);
          Redirfile = start;
          break;
        }

        ++start;
    }
}

int main( )
{
  while(true)
  {
    RedirMode = NOREDIR;
    Redirfile = NULL;

    //1.打印提示符
    printf("[用户名@主机名 当前路径]#");
    fflush(stdout);        //刷新缓冲区

    //获取用户输入
    char* s = fgets(CommandLine,sizeof(CommandLine)-1,stdin);
    assert(s != NULL);  //检查释放获取成功
    (void)s;      
    CommandLine[strlen(CommandLine)-1] = 0;  //消除掉输入时带的换行符

    CommandCheck(CommandLine);

    //字符串分割,拿出指令
    Myargv[0] = strtok(CommandLine," ");
    int i = 1;
    //给ls命令增加配色方案
    if(Myargv[0]!=NULL && strcmp(Myargv[0],"ls")==0)
    {
      Myargv[i++] = (char*)"--color=auto";
    }
    while( Myargv[i++] = strtok(NULL," "));  //无法分割时返回空指针。 命令行参数最后刚好需要以NULL结尾

    //内建命令,内置命令不需要创建子进程来执行
    //cd 命令需要改变当前进程的工作目录
    if(Myargv[0]!=NULL && strcmp(Myargv[0],"cd")==0)
    {
        if(Myargv[1]!=NULL)
        chdir(Myargv[1]);
        continue;
    }

    //echo命令获取上次程序的退出码
    if(Myargv[0]!=NULL && Myargv[1]!=NULL && strcmp(Myargv[0],"echo")==0)
    {
        if(strcmp(Myargv[1],"$?")==0)
        {
          printf("lastcode:%d , lastSignal:%d\n",lastCode,lastSignal);
        }
        else
        {
          printf("%s\n",Myargv[1]);
        }
        continue;
    }

//条件编译来测试  编译时带上 -DDEBUG即可运行测试
#ifdef DEBUG
for(int i=0; Myargv[i] ;++i)
printf("%s\n",Myargv[i]);
#endif

 //创建子进程执行相关指令
pid_t id = fork();
assert(id != -1); //检测子进程是否创建失败

if(id == 0) //子进程进程切换 执行对应的指令
{
  switch(RedirMode) 
  {
    case NOREDIR:      //什么都不做
    break;
    case INREDIR:    //输入重定向
    {
      int fd = open(Redirfile,O_RDONLY);
      dup2(fd,0);
    }
    break;
    case OUTREDIR:
    case APPREDIR:
    {
       int flags = O_CREAT | O_WRONLY ;
      if(RedirMode == OUTREDIR)
      {
        flags |= O_TRUNC;
      }
      else
      {
        flags |= O_APPEND;
      }
    int fd = open(Redirfile,flags,0666);
    dup2(fd,1);
    }
    break;

    default:
    printf("未知错误\n");
    break;
  }

  execvp(Myargv[0],Myargv);

  exit(1); //异常时才从这退出
}
int status;  //拿到子程序的退出码
waitpid(id,&status,0);

lastCode = ((status>>8) & 0xFF);
lastSignal = (status & 0x7F);
    
 }
  return 0;
}

FILE

  • 因为IO相关函数与系统调用接口对应,并且库函数封装系统调用,所以本质上,访问文件都是通过fd访问的。

  • 所以C库当中的FILE结构体内部,必定封装了fd。

在/usr/include/stdio.h中,我们可以看到这句代码:

typedef struct _IO_FILE FILE;

        很明显FILE是struct _IO_FILE的别名。在/usr/include/libio.h中我们能找到它.

struct _IO_FILE {
 int _flags; /* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags
 //缓冲区相关
 /* The following pointers correspond to the C++ streambuf protocol. */
 /* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
 char* _IO_read_ptr; /* Current read pointer */
 char* _IO_read_end; /* End of get area. */
 char* _IO_read_base; /* Start of putback+get area. */
 char* _IO_write_base; /* Start of put area. */
  char* _IO_write_ptr; /* Current put pointer. */
 char* _IO_write_end; /* End of put area. */
 char* _IO_buf_base; /* Start of reserve area. */
 char* _IO_buf_end; /* End of reserve area. */
 /* The following fields are used to support backing up and undo. */
 char *_IO_save_base; /* Pointer to start of non-current get area. */
 char *_IO_backup_base; /* Pointer to first valid character of backup area */
 char *_IO_save_end; /* Pointer to end of non-current get area. */
 struct _IO_marker *_markers;
 struct _IO_FILE *_chain;
 int _fileno; //封装的文件描述符
#if 0
 int _blksize;
#else
 int _flags2;
#endif
 _IO_off_t _old_offset; /* This used to be _offset but it's too small. */
#define __HAVE_COLUMN /* temporary */
 /* 1+column number of pbase(); 0 is unknown. */
 unsigned short _cur_column;
 signed char _vtable_offset;
 char _shortbuf[1];
 /* char* _save_gptr; char* _save_egptr; */
 _IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};

从上我们可以看到其内部封装了文件描述符,并且FILE还有自己的缓冲区。

用户级缓冲区

来段代码感受一下:

#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main()
{
    const char* st1 = "hello printf\n";
    const char* st2 = "hello fwrite\n";
    const char* st3 = "hello write\n";

    printf("%s",st1);
    fwrite(st2,strlen(st2),1,stdout);
    write(1,st3,strlen(st3)); 
    fork();
    return 0;
}

 接着我们进行重定向:

        我们发现 printf 和 fwrite(库函数)都输出了2次,而 write 只输出了一次(系统调用)。为什么呢?肯定和fork有关!

  • 一般C库函数写入文件时是全缓冲的,而写入显示器是行缓冲

  • printf fwrite 库函数会自带缓冲区,当发生重定向到普通文件时,数据的缓冲方式由行缓冲变成了全缓冲

  • 而我们放在缓冲区中的数据,就不会被立即刷新,甚至在fork之后

  • 但是进程退出之后,会统一刷新,写入文件当中

  • 但是fork的时候,父子数据会发生写时拷贝,所以当你父进程准备刷新的时候,子进程也就有了同样的一份数据,随即产生两份数据

  • write 没有变化,说明没有所谓的缓冲

printf 、fwrite 库函数会自带缓冲区,而 write 系统调用没有带缓冲区。

  • 另外,我们这里所说的缓冲区,都是用户级缓冲区。其实为了提升整机性能,OS也会提供相关内核级缓冲区,不过不再我们讨论范围之内。

  • 那这个缓冲区谁提供呢? printf fwrite 是库函数, write 是系统调用,库函数在系统调用的“上层”, 是对系统调用的“封装”,但是 write 没有缓冲区,而 printf、fwrite 有,足以说明,该缓冲区是二次加上的,是由C标准库提供的

结语

        通过这篇博客,我们聊了很多内存中文件相关的知识,不知大家收获如何,那么硬盘中的文件是如何管理的呢下篇博客文件系统就揭开其神秘的面纱。希望大家给个三连支持一波。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/441096.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ETCD(一)简介

1. ETCD是什么 etcd 是一个分布式键值对存储&#xff0c;设计用来可靠而快速的保存关键数据并提供访问。和数据库一样都是用来存数据的&#xff0c;但是etcd有自己的特点&#xff0c;因此有自己的使用场景。 2. etcd 特点 完全复制&#xff0c;集群中的每个节点均拥有全量数…

跌倒检测和识别2:YOLOv5实现跌倒检测(含跌倒检测数据集和训练代码)

跌倒检测和识别2&#xff1a;YOLOv5实现跌倒检测(含跌倒检测数据集和训练代码) 目录 跌倒检测和识别2&#xff1a;YOLOv5实现跌倒检测(含跌倒检测数据集和训练代码) 1. 前言 2. 跌倒检测数据集说明 &#xff08;1&#xff09;跌倒检测数据集 &#xff08;2&#xff09;自定…

《离散数学导学》精炼——第10章(序列)

Learning never exhausts the mind. 文章目录 引言正文元包序列的定义序列与函数的关系空序列长度连接头尾运算符限制运算符逆置运算符单射序列 引言 笔者一直觉得在计算机这一学科的学习中&#xff0c;离散数学是极为重要的知识基础。离散化的思想体现在计算机学科的方方面面…

SpringDataJPA

介绍&#xff1a; spirng data jpa是spring提供的一套简化JPA开发的框架&#xff0c;按照约定好的规则进行【方法命名】去写dao层接口&#xff0c;就可以在不写接口实现的情况下&#xff0c;实现对数据库的访问和操作。同时提供了很多除了CRUD之外的功能&#xff0c;如分页、排…

剖析Linux内核—fork背后隐藏的技术细节

1、前言 全文分为两部分讲解&#xff1a;fork的内存管理部分和进程管理部分&#xff0c;内存管理主要讲解子进程如何构建自己的内存管理相关基础设施&#xff0c;父子进程如何共享地址空间的&#xff0c;写时复制如何发生&#xff0c;页表层面为我们做了哪些事情等等。而进程管…

Redis缓存雪崩、穿透、击穿

Redis缓存雪崩、穿透、击穿 解决方案正常的缓存流程Redis缓存雪崩Redis缓存雪崩解决方案 Redis缓存穿透Redis缓存穿透解决方案 Redis缓存击穿Redis缓存击穿解决方案 解决方案 布隆过滤器&#xff0c;分布式锁 正常的缓存流程 Redis缓存雪崩 Redis中的key大面积失效&#xff0…

BUUCTF-PWN-pwn1_sctf_2016

下载 放入 ubuntu里查信息 现在这些保护我都没有遇到 以后慢慢做应该是会遇到的 然后进行发现是32 所以我们记住 如果栈溢出漏洞 我们需要4个字节填满基地址 放入ida32 查看字符串 发现 cat flag 敏感字符串 然后我们就看引用 先记住地址 为 0x8048F0D 然后开始进去 发…

windows下网络聊天室项目(C++、SQLite、计算机网络)——思路+源码+解析

这里只是在windows系统下用C++做的简易聊天室,其界面比较low,不过该有的功能已基本实现。至于带界面的,QT里有封装好的QTcpSocket套接字,会在下一篇博客里予以介绍。主要的功能主要包含的功能有:查看当前在线人数、进入群聊界面、进入私聊界面、查看聊天记录、文件传输、更…

【Python小程序】纪念日礼物|恋爱一周年,送媳妇儿什么礼物浪漫又有惊喜?一键收藏,过节再也不用绞尽脑汁啦~(特别的礼物赠送)

前言 有些日子不可懒&#xff0c;指的就是纪念日。不同阶段过纪念日&#xff0c;也是不同的心境。 ——偷偷告诉你 所有文章完整的素材源码都在&#x1f447;&#x1f447; 粉丝白嫖源码福利&#xff0c;请移步至CSDN社区或文末公众hao即可免费。 作为一个编程博主&#xff0c…

爬虫学习 例子

以新能源网为例 【10-实战课】从源码获取豆瓣电影TOP250_哔哩哔哩_bilibili 1.查看网站结构 可见结构比较简单&#xff0c;直接循环爬取即可 2.代码&#xff08;无数据存储&#xff09; import requests from bs4 import BeautifulSoup headers {"User-Agent":&…

追梦之旅【数据结构篇】——看看小白试如何利用C语言“痛”撕堆排序

追梦之旅【数据结构篇】——看看小白试如何利用C语言“痛”撕堆排序 ~&#x1f60e; 前言&#x1f64c;堆的应用 —— 堆排序算法&#xff1a;堆排序算法源代码分享运行结果测试截图&#xff1a; 总结撒花&#x1f49e; &#x1f60e;博客昵称&#xff1a;博客小梦 &#x1f60…

【菜鸡读论文】Cross-domain Named Entity Recognition via Graph Matching

【菜鸡读论文】Cross-domain Named Entity Recognition via Graph Matching 最近到了研一下学期&#xff0c;很多身边的同学也开始有了成果&#xff0c;但本菜鸡一点成果都没有【大哭】所以也没什么好写的。虽然菜鸡口头上不说&#xff0c;但内心也感受到非常之焦虑。最近读论…

Linux 触摸屏 (IIC驱动详解)

前言&#xff1a; 本文使用触摸屏iic驱动&#xff0c;驱动编写中要使用总线&#xff0c;设备&#xff0c;驱动分层的思想&#xff0c;IIC总线&#xff0c;触摸屏驱动&#xff0c;触摸屏设备&#xff0c; IIC总线驱动 一般 SOC 的 I2C 总线驱动都是由半导体厂商编写的&…

使用SeaFile搭建私有云盘并公网访问【cpolar内网穿透】

文章目录 1. 前言2. SeaFile云盘设置2.1 Owncould的安装环境设置2.2 SeaFile下载安装2.3 SeaFile的配置 3. cpolar内网穿透3.1 Cpolar下载安装3.2 Cpolar的注册3.3 Cpolar云端设置3.4 Cpolar本地设置 4. 公网访问测试5. 结语 1. 前言 现在我们身边的只能设备越来越多&#xff…

20230420使用逻辑分析仪测量摄像头的PAG7920的时钟信号

20230420使用逻辑分析仪测量摄像头的PAG7920的时钟信号 2023/4/20 19:14 在CV1826平台&#xff1a; 1、vsync信号&#xff1a;刷新率120HZ PAG7920LT: Ultra-Low Power Global Shutter Image Sensor Max. Frame Rate 180 FPS 20KSa/20KHZ 2、href行同步信号&#xff1a;KHZ级别…

栈的定义、存储结构、基本操作、案例

&#x1f4dd;个人主页&#xff1a;爱吃炫迈 &#x1f48c;系列专栏&#xff1a;数据结构与算法 &#x1f9d1;‍&#x1f4bb;座右铭&#xff1a;道阻且长&#xff0c;行则将至&#x1f497; 文章目录 栈栈的存储栈的基本操作进栈出栈读栈顶元素记录栈内元素个数清除栈内所有元…

详版Git命令播报

文章目录 下载安装Git 配置config文件配置用户信息查看配置信息 工作协作流程git四个区协作流程 初始化新仓库检出仓库git clonegit remote本地存有代码进行clone没有git仓库已存在git仓库 提交与修改git add 及 git statusgit diffgit commitgit resetgit rmgit mv 查看提交历…

DHCP故障定位

1.请分析可能的原因,定位并排除故障。 (1)存在仿冒DHCP服务器攻击 导致部分有线终端获取到错误的IP地址、网关等信息,进而导致无法访问网关。 解决办法:为了防止DHCP Server仿冒者攻击,将与合法DHCP服务器直接或间接连接的接口设置为信任接口,其他接口设置为非信信任接…

Flutter开发日常练习-pageview滑动视图动画

养小猫咪的伙伴来我的店铺逛逛吧!抖音商城搜索#早睡早起的猫咪小铺子 学着写一下滑动页面 pageview实现左右滑动视图 class SlidingContainer extends StatefulWidget {const SlidingContainer({super.key});overrideState<SlidingContainer> createState() > _Slidi…

【C语言学习3——基本的C语言语法知识2】

C语言学习3——基本的C语言语法知识 标识符关键词什么是字面常量&#xff1f;printf函数printf函数更多用法 #include命令 标识符 在前面的代码中&#xff0c;由我们自己命名&#xff0c;用于指代某一个实体的名称&#xff0c;例如:add&#xff0c;result&#xff0c;函数的参…