linux系统基础知识-基础IO

IO

  • 概念引入
  • 位图的概念
  • IO的系统调用函数
    • open
    • write
    • read()
    • close
    • 简单使用样例:
  • 文件描述符fd
    • 默认文件流stdin/stdout/stderr
    • 文件描述符的分配规则
  • 重定向的概念
    • 输出重定向
    • 输入重定向
    • 追加重定向
    • dup2()系统调用
    • 总结
  • 文件缓冲区
    • 深入理解缓冲区的概念
    • 输出缓冲区部分代码解释
    • 总结
  • stdout和stderr的区别
    • 总结
    • 自己实现一个perror
  • 文件系统
    • inode
    • 发现系统还有空间,为什么不能创建文件?
  • 软硬链接

概念引入

在学习C语言的时候,我们学过一些文件操作的函数,比如fopen,fgets,fputs,fclose等等,其实这些函数的功能就是在操作文件,进行IO读取工作。这些是C语言封装的函数。
然而C语言封装的函数里面,一定有OS层面的动作,因为IO设计到硬件操作,而对于硬件的操作一定要通过系统调用,让OS帮助我们完成IO操作。
所以,为了更好的让大家理解IO原理,先介绍一下IO的系统调用接口。

位图的概念

在介绍系统调用函数之前,需要了解一下位图的概念。顾名思义就是每个比特位都有不通过的含义,代表着不同的功能。
下面使用一个例子介绍位图的应用:

  1 #include<stdio.h>2 3 4 // 用int中的不重复的一个bit,就可以标识一种状态5 // 第1个比特位表示功能一,第二个比特位表示功能二,第三个比特位表示功能三6 #define ONE 0x1   //0000 00017 #define TWO 0x2   //0000 00108 #define THREE 0x4 //0000 01009 10 11 void show(int flags)12 {13   //哪个标志的比特位是1,就完成对应的功能14   if(flags & ONE) printf("功能ONE\n");15   if(flags & TWO) printf("功能TWO\n");16   if(flags & THREE) printf("功能THREE\n");17 }18 19 int main()20 {21   show(ONE);   //选择功能122   show(TWO);    //选择功能223   show(ONE | TWO);   //选择功能1和224   show(ONE | TWO | THREE);  //选择功能123                                                                                                                           25   return 0;26 }

在这里插入图片描述

从上面的运行结果可以来看,我们使用位图的概念,实现了的方法可以选择任意功能。

IO的系统调用函数

open

类比fopen的使用,使用man 2 open查询open函数的用法。我们使用时,经常使用带mode的函数。
在这里插入图片描述

第一个参数:文件路径
第二个参数flags:功能位(位图的概念)
第三个参数:权限:可以使用16进制来表示,一般使用0x666表示rw权限。
返回值:成功时返回新打开的文件描述符。失败时:返回-1.

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

  • O_RDONLY:只读打开
  • O_WRONLY:只写打开
  • O_RDWR:读、写打开 (上面这三个常量,必须写一个且只能写一个)
  • O_CREAT:若文件不存在,则创建它。需要使用mode选项,来指明文件的访问权限。
  • O_APPEND:追加写。

下面我们简单举个例子来打开文件:

  1 #include<stdio.h>2 #include<sys/types.h>3 #include<sys/stat.h>4 #include<fcntl.h>5 6 int main()7 {8   //只读并且没有文件创建。9   int fd = open("log.txt", O_WRONLY|O_CREAT, 0666);// rw-rw-rw                                                                                                      10   if(fd < 0)11   {12     perror("open:");13     return 1;14   }15 16   printf("打开成功, fd %d \n", fd);17 18   return 0;19 }

在这里插入图片描述

write

类比fwrite的使用,使用man 2 write查询open函数的用法。在这里插入图片描述

fd:文件描述符
buf:要写的字符串。
count:要写的大小(单位是字节)
返回值:ssize_t :实际写的有效字节数。

read()

类比fread。
在这里插入图片描述

将fd的功能读到字符串buf中,读取count个字节。

close

关闭指定文件,通过文件描述符关闭。
在这里插入图片描述

返回值:0代表成功。-1代表失败。

简单使用样例:

创建一个log.txt文件,并且追加输入111222333

  1 #include<stdio.h>2 #include<string.h>3 #include<stdlib.h>4 #include<unistd.h>5 #include<sys/types.h>6 #include<sys/stat.h>7 #include<fcntl.h>8 9 int main()10 {11   //只读并且没有文件创建,追加写入12   int fd = open("log.txt", O_WRONLY | O_CREAT | O_APPEND, 0666);// rw-rw-rw13   if(fd < 0)14   {15     perror("open:");16     return 1;17   }18 19   printf("打开成功, fd %d \n", fd);20   const char* s1 = "111111111111";21   const char* s2 = "222222222222";22   const char* s3 = "3333333333333";23   write(fd, s1, strlen(s1));24   write(fd, s2, strlen(s1));25   write(fd, s3, strlen(s1));26 27   close(fd);                                                                                                                                                        28 29   return 0;30 }

在这里插入图片描述

文件描述符fd

通过open系统调用函数的理解,发现文件描述符其实就是一个小整数,并且一直是3,为什么呢?
这就要引入stdin,stdout,stderr的概念。

默认文件流stdin/stdout/stderr

在我们操作系统启动的时候,默认会打开三个流,他们就是stdin,stdout,stderr,即标准输入流、标准输出流、标准错误流。对应的硬件分别是:键盘(让我们能输入)、显示器(让我们能看见)、显示器(出错了也能看见)。
在这里插入图片描述

看下面一段程序:

  1 #include<stdio.h>4 int main ()5 {6 7   printf("stdin: %d\n",stdin->_fileno);8   printf("stdout: %d\n",stdout->_fileno);9   printf("stderr: %d\n",stderr->_fileno);10   return 0;                                                                                                                                                         11 }12 

在这里插入图片描述
我们可以猜测,0,1,2是被这默认打开的三个流占用了,所以我们每次再打开文件的时候,被分配的就是从3开始了。


并且在man手册查看一下他们的介绍,发现都是FILE* 类型的。
在这里插入图片描述
因此我们猜测FILE是一个结构体,并且结构体的内部一定有fd。当然这个结构体一定是由C语言提供的。
而我们上面学习了系统调用函数,使用的是fd操纵的文件,我们可以推测C语言的文件操作,一定封装了系统调用的接口,并且通过struct FILE里面的fd来和系统沟通,因为系统只认识fd,不认识FILE*。

下面我将fd与文件的关系可视化出来帮助大家理解:
在这里插入图片描述

解释:通过进程的pcb里面可以找到一个数组,其中fd就是数组的下标,数组内容存着文件的属性信息。因此当我们打开一个文件的时候,OS会在数组中申请一个最小的下标(通常情况下最小为3),然后内容存上结构体的地址,这样我们就能通过fd操作文件啦。

在这里插入图片描述

现在我们知道,文件描述符就是从0开始的小整数。当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述文件。于是就有了file结构体。表示一个已经打开的文件对象。而进程执行open系统调用,所以必须让进程和文件关联起来。每个进程都有一个指针*file,指向一张表files_struct,该表最重要的部分就是包含一个指针数组,每个元素都是一个指向打开文件的指针!所以,本质上,文件描述符fd就是该数组的下标。所以只要拿着文件描述符,就可以找到对应的文件。

文件描述符的分配规则

现在我们已经理清楚,文件描述符是什么,有什么用。那么他的分配规则是什么呢?fd分配最小下标分配原则。
关闭0号文件,那么我们如果打开文件的话描述符就为0,如下:

  1 #include<stdio.h>2 #include<string.h>3 #include<stdlib.h>4 #include<unistd.h>5 #include<sys/types.h>6 #include<sys/stat.h>7 #include<fcntl.h>8 9 int main()10 {12   close(0);   //关闭0号文件描述符                                                                                                                                                         13   int fd = open("log.txt", O_WRONLY | O_CREAT | O_APPEND, 0666);// rw-rw-rw14   if(fd < 0)15   {16     perror("open:");17     return 1;18   }19 20   printf("打开成功, fd %d \n", fd);28   close(fd);30   return 0;31 }

在这里插入图片描述

可以验证OS总是优先分配最小的数组下标,即便是0、1、2号下标也不例外!!!

重定向的概念

输出重定向

看一下下面的代码:

  1 #include<stdio.h>2 #include<string.h>3 #include<unistd.h>4 #include<sys/types.h>5 #include<sys/stat.h>6 #include<fcntl.h>7 8 int main ()9 {10 11   close(1);12   int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);  //fd的文件描述符为113   //下面的内容本应该打印到屏幕上。但实际上却没有打印出来。14   printf("打开文件分配的fd为%d\n", fd);  15   printf("打印到屏幕上的内容\n");16   fprintf(stdout, "hello fprintf\n");17   const char* s1 = "hello fwrite\n";18   fwrite(s1, 1, strlen(s1), stdout);19                                                                                                              22   fflush(stdout);23   close(fd);24   return 0;25 }

运行结果:本应该显示在屏幕上的内容却被输出到了文件里!这个现象叫做输出重定向,但是为什么呢?
在这里插入图片描述

下面我将用图例解释一下产生上面情况的原因:
在这里插入图片描述
注意stdin,stdout,stderr在C语言是宏定义,分别被定义成为了0、1、2。在代码中,我们首先关闭了1号文件描述符,然后又打开了文件,显然log.txt文件的文件描述符就是1也就是说1号下标的内容由标准输出(屏幕)变成了指向log.txt的指针。所以,我们在代码中对stdout文件输出内容,在语言层面其实就是对1号文件描述符输出内容,然而语言层面不知道这个改变(因为文件描述符是OS分配的),所以就造成了输出重定向

输入重定向

理解输出重定向例子后,我们很容易就能写出输入重定向。

  8 int main ()9 {10 11   // 关闭stdin12   close(0);13   int fd = open("log.txt", O_RDONLY);14   if(fd<0)15   {16     perror("open:");17     return 1;18   }                                                                                                         19   printf("fd = %d \n", fd);20   char buffer[128];21   fgets(buffer, sizeof buffer, stdin);22   printf("buffer:%s\n", buffer);23   close(fd);}

运行结果:
在这里插入图片描述

可知我们本该需要使用键盘输入的时候,但是却不需要我们输入,而是从我们打开的文件中读取了一行。这就完成了输入重定向工作。

追加重定向

我们实现一直往log.txt追加输出内容的代码:

  1 #include<stdio.h>2 #include<string.h>3 #include<unistd.h>4 #include<sys/types.h>5 #include<sys/stat.h>6 #include<fcntl.h>7 8 int main ()9 {10 11   close(1);//将文件打开方式变为追加打开即可12   int fd = open("log.txt", O_WRONLY | O_CREAT | O_APPEND, 0666);  //fd的文件描述符为113   //下面的内容本应该打印到屏幕上。但实际上却没有打印出来。14   printf("打开文件分配的fd为%d\n", fd);  15   printf("打印到屏幕上的内容\n");16   fprintf(stdout, "hello fprintf\n");17   const char* s1 = "hello fwrite\n";18   fwrite(s1, 1, strlen(s1), stdout);19                                                                                                              22   fflush(stdout);23   close(fd);24   return 0;25 }

在这里插入图片描述
运行程序前后,log.txt内容确实被追加了!

dup2()系统调用

上面的例子只是让我们见识一下重定向是什么样子。而实际工程中,我们多使用dup2()系统调用,来完成此功能。

在这里插入图片描述
在这里插入图片描述

把oldfd文件描述符的内容给nwefd(相当于修改的是数组内容)。

看下面代码示例:

  8 int main (int argc, char* argv[])9 {10   if(argc!=2)11   {12     return 1;13   }14 15   int fd = open("log.txt", O_CREAT| O_WRONLY|O_TRUNC, 0666);16   printf("fd:%d\n", fd);17   dup2(fd,1); //将stdout的内容修改为fd18   printf("打印屏幕上的内容:%s\n",argv[1]);19 20   close(fd);21   return 0;   }   

运行结果:
在这里插入图片描述
通过上面的例子,我们使用dup2完成了输出重定向!

总结

重定向的本质,其实是在OS内部,更改fd对应的内容指向!!!
重定向很容易解决Linux系统一切皆文件的设计理念。它把显示器、键盘、磁盘当成文件,全部封装成struct file,在用户看来,只需要操作对应的结构体,即可完成重定向的工作。用户根本不需要知道硬件的细节。也恰好能体现封装的思想。

文件缓冲区

  1. 什么是缓冲区?
    缓冲区就是一段内存区域,介于CPU和硬盘之间,因为要执行IO的话,CPU肯定会访问硬盘。CPU的速度是ns级别,硬盘的速度是ms级别。如果让CPU直接访问的话,肯定会拖慢系统的速度。因此我们可以在内存中开辟一小段区域,这段区域就叫做缓冲区。

  2. 缓冲区有什么用?
    缓冲区可以提高效率。
    写透模式(wrrite through):CPU直接向硬盘写入(耽误CPU时间,慢)。
    写回模式:(write back):CPU将数据写到缓冲区后,就干别的事情去了,让缓冲区自己往硬盘写入,写完报告给CPU即可。(不耽误CPU时间,快,,也可以提高用户的相应速度)。

  3. 缓冲区的刷新策略
    立即刷新(写完就立马刷新)、行刷新/行缓冲(遇到\n就立马刷新)、满刷新/全缓冲(写满缓冲区才刷新)。
    一般而言,行缓冲的设备文件:显示器;全缓冲的设备:磁盘文件

深入理解缓冲区的概念

下面我们先看一段代码:

  8 int main ()9 {10   11   //C语言提供的12   printf("hello world\n");13   const char* s1 = "hello frite\n";14   fwrite(s1, 1, strlen(s1), stdout);15   fprintf(stdout,"hello fprintf\n");16 17   //OS系统提供的接口18   const char *s2 = "hello write\n";19   write(1, s2, strlen(s2));20 21 22   fork();                                                                                                   23   return 0;24 }

程序运行结果如下:
在这里插入图片描述
**观察发现,程序运行时是正常的,但是当我们重定向到文件里面后,发现C语言提供的函数打印了两次,而系统调用函数只打印一次。**如果我们把fork去掉之后,程序正常运行。我们猜想,这种情况一定和fork有某种关系。

解释:

  1. 如果向显示器打印,刷新策略是行刷新,那么最后执行fork的时候,一定是函数执行完了同时数据已经被刷新了,此时再fork就没有意义了。
  2. 如果对应的程序进行了重定向,要向磁盘文件打印,隐形的刷新策略变成了全缓冲(\n便不管用了),fork的时候,一定是函数已经执行完了,但是数据还没有刷新!因为刷新会清空缓冲区,这涉及到写时拷贝,当父进程结束运行,会刷新缓冲区,同时触发写时拷贝,赋值给子进程,子进程退出时再刷新一份,这样就相当于打印了两次
  3. 为什么系统调用函数只打印一次呢?
    原因是,我们上面提到的缓冲区其实是C语言层面上的,调用C函数时,会将数据写到C提供的缓冲区上。而fork后,出发写时拷贝时,父进程刷新数据到OS缓冲区(1份),子进程刷新到OS缓冲区(2份),因此C语言层面上会打印两次。而write系统调用会直接将数据写到OS缓冲区,因为他是系统提供的函数,不属于C语言函数,因此不涉及C语言层面上的写时拷贝动作,因此只会刷新一次。
    下图解释了这种现象:

在这里插入图片描述

输出缓冲区部分代码解释

在这里插入图片描述

我们在输出缓冲区,当时红色框框的代码没有讲解,现在我们可以解释一下:

  1. 当我们关闭了1号文件,打开了log.txt,此时log.txt的文件描述符为1.
  2. 刷新策略由行刷新,变成了普通刷新,因为是往文件里打印,因此我们输出的数据,其实是在stdout的缓冲区里,并没有刷新呢!
  3. 如果我们提前关闭close(1),因为数据还在缓冲区里,还没有来得及刷新到文件里。所以log.txt文件里什么都没有。
  4. 当我们提前fflush文件的时候,已经把数据刷新到文件里,因此再关闭文件就没事了。

总结

所有的设备按道理来说,都倾向于全缓冲,缓冲区满了,才刷新,这样能进行更少的IO操作,提高效率。因为和外设IO时,数据量的大小不是主要矛盾,和外设预IO的过程(准备过程)是最耗费时间的。
为了照顾到用户体验,显示器一般都是行刷新。极端情况下,我们也可以自定义刷新规则。

**C语言的缓冲区在哪里呢?**和fd一样,存在FILE结构体里面,包含了该文件fd对应的语言层的缓冲区结构。

stdout和stderr的区别

stdout和stderr所对应的硬件都是显示器,但是他们具体有什么区别呢?
名字上:stdout叫标准输出流,stderr叫标准错误流。
让我们看下面一段代码:
该程序输出的1表示使用stdout打印的。输出2表示使用stderr打印的。

  1 #include<iostream>2 #include<stdio.h>3 4 int main()5 {6   //stdout -> 17   printf("hello printf 1\n");8   fprintf(stdout, "hello fprintf1\n");9 10   //stderr11   perror("hello perror 2");12 13   // cout -> 114   std::cout<< "hello cout 1"<<std::endl;15   //cerr -> 2                                                                                                        16   std::cerr<<"hello cerr 2"<<std::endl;17 18   return 0;19 }

在这里插入图片描述

程序首先都被打印到了屏幕上,证明他们对应的硬件都是显示器。

我们在此程序的基础上,运行下面的命令:
重定向后,发现stderr的输出仍然打印出来了,但是stdout的输出被重定向到log.txt。
在这里插入图片描述
结论:因为屏幕只有一个,stdin和stdout都打开了显示器文件,即一个显示器文件被打开了两次,有两个文件描述符,输出重定向只是重定向了fd=1描述符,并没有影响到stderr。因此stderr仍然打印到屏幕上。

应用:

//运行下面的命令即可把两种输出,分别重定向到两个文件里。./myfile > log.txt 2>err.txt

在这里插入图片描述

//运行下面的命令即可把两种输出,重定向到一个文件里。./myfile > log.txt 2>&1

在这里插入图片描述

总结

一般而言程序如果可能出问题的话,使用stderr或者cerr来打印。
如果是常规文本的打印,建议使用cout或者stdout打印。

自己实现一个perror

在我们的文件操作函数出错之后,出错信息一般都会存在一个全局变量,errno里面,我们一般都使用perror函数来打印错误信息,但是该接口不需要errno。说明该接口一定封装了errno。代码如下:

  5 void myperror(const char* s)6 {7   //使用errno全局变量打印错误8   fprintf(stderr, "%s %s\n",s, strerror(errno));9 }

文件系统

文件系统顾名思义是管理文件的系统。如下图是Linux ext2文件系统的磁盘文件系统图。
磁盘是典型的块设备,硬盘分区被划分为一个个的block。一个block的大小是格式化的时候确定的,并且不能更改。其中启动块(Boot Block)的大小是确定的。
在这里插入图片描述

  • Block Group:块组,文件系统会根据分区的大小划分为好多个Block Group。而每个Block Group有着相同的结构。
  • 超级块(Super Block):存放(整个)文件系统本身的结构信息。记录的主要信息有:block和inode的总量,未使用的block和inode的数量,一个block和inode的大小,最近一次挂载的时间,最近一次写入数据的时间,最近一次检查磁盘的时间等其他文件系统的相关信息。Super Block如果被损坏,整个文件系统就被破坏了。因此,有好多个Block Group都有Super Block。
  • GDT(Group Descriptor Table):块组描述符,描述块组的属性信息,这个块组多大,已经使用了多少,有多少inode,已经占用了多少,还剩多少,一共有多少个block,使用了多少。
  • 块位图(Block Bitmap):位图中记录着哪个数据块已经被占用,哪个数据块没有被占用。
  • inode位图(inode Bitmap):每个bit表示一个inode是否空闲可用。
  • inode Table:i结点表,存放文件属性和文件大小,所有者,最近修改时间等。
  • Data blocks:数据区存放文件内容。

众所周知:文件 = 属性 + 数据因此flie = inode Table + Data blocks

inode

inode是一个结构体,存放着文件的各种属性。

struct inode
{//文件大小//文件的inode编号//其他属性int blocks[15] //(0-11是直接索引,12-15是间接索引)
}
  • inode与文件名的关系:我们使用文件名去寻找文件,但是linux其实并不认识文件名,它只认识inode号,因此,在每个目录下面<文件名,inode>都有着对映的键值对,可以帮助转化成inode。
  • 目录也是文件,也有ionde号,也有data Block。
    因此对于一个目录,进入目录需要X(执行)权限,创建文件需要W(写)权限(要写数据块),显示文件名与属性r(读)权限(要读数据块)。

创建文件过程:首先os申请一个inode号,并且为之分配一个数据块,然后更新inode表。完成之后,更新目录的data block。

发现系统还有空间,为什么不能创建文件?

因为inode和data block是固定的。可能是没有ionde号了。

软硬链接

  • 硬链接是通过inode引用另外一个文件,引用计数+1,inode号相同,相当于共享一个inode。
  • 软链接是通过名字引用另外一个文件,引用计数不变,新inode号,相当于一个新的inode号指向老的inode号,相当于快捷方式。

在这里插入图片描述

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

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

相关文章

WEB 3D技术 three.js 线框几何体

本文 我们说一下 线框几何体 想将一个几何体 以线框形式展现 threeJS中 有两种类可以实现 第一种 WireframeGeometry 这种几何体 其实就类似于 将材质中的 wireframe 开启 这种方法 之前我们也用过 还有一种 就是 EdgesGeometry 边缘几何体 我们先将代码写成这样 import .…

【深度学习每日小知识】Data Augmentation 数据增强

数据增强是通过对原始数据进行各种转换和修改来人工生成附加数据的过程&#xff0c;旨在增加机器学习模型中训练数据的大小和多样性。这对于计算机视觉领域尤为重要&#xff0c;因为图像经常被用作输入数据。 计算机视觉中的数据增强 数据增强的主要目标是解决过拟合问题&…

使用迭代优化递归程

王有志&#xff0c;一个分享硬核Java技术的互金摸鱼侠加入Java人的提桶跑路群&#xff1a;共同富裕的Java人 今天我们将会分析上篇文章中递归算法存在的问题&#xff0c;并通过迭代去优化。 递归存在的问题 上一篇中&#xff0c;我们计算了序号10以内的斐波那契数。今天为了清…

【Leetcode】236.二叉树的最近公共祖先

一、题目 1、题目描述 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。” 示例1…

关于“Python”的核心知识点整理大全63

目录 20.2.11 使用 Git 跟踪项目文件 1. 安装Git 2. 配置Git 3. 忽略文件 .gitignore 注意 4. 提交项目 20.2.12 推送到 Heroku 注意 20.2.13 在 Heroku 上建立数据库 20.2.14 改进 Heroku 部署 1. 在Heroku上创建超级用户 注意 注意 20.2.11 使用 Git 跟踪项目文件…

Vue3-37-路由-组件内的路由守卫 onBeforeRouteLeave 和 onBeforeRouteUpdate

简介 组件内的路由守卫&#xff0c;实际上就是两个 API 方法。 他们与普通的守卫不同的是 &#xff1a; 他们是写在组件内的&#xff0c;在组件中监听路由的变化&#xff0c;不是全局的&#xff0c;比较灵活。 以下是两个 API 的功能说明&#xff1a;onBeforeRouteLeave() : 守…

Java中的序列化方法探索

.为什么要序列化 对象不序列化&#xff0c;能直接存储吗&#xff1f; 在 Java 中&#xff0c;序列化是将对象的状态信息转换为可以存储或传输的形式&#xff08;例如&#xff0c;转换为字节流&#xff09;的过程。在对象数据需要在网络上传输或需要在磁盘上持久化存储时&#…

指针的含义、表示、规范、存储、运用

指针的含义、表示、规范、存储、运用 指针的含义指针的表示指针的规范先声明再定义声明和定义一起表示错误表示 指针的存储理解一个变量的存储过程和原理理解一个指针的存储过程和原理理解多个指针的存储过程和原理 指针的运用 指针的含义 表示某个变量或数据所在的内存地址 注…

使用tailscale访问对端局域网上的其他设备

当tailscale客户端应用程序直接安装在组织中的每个客户端、服务器和虚拟机上时&#xff0c;Tailscale 效果最佳。这样&#xff0c;流量就会被端到端加密&#xff0c;并且无需配置即可在物理位置之间移动机器。 但是&#xff0c;在某些情况下&#xff0c;你不能或不想在每台设备…

Linux第18步_安装“Ubuntu系统下的C语言编GCC译器”

Ubuntu系统没有提供C/C的编译环境&#xff0c;因此还需要手动安装build-essential软件包&#xff0c;它包含了 GNU 编辑器&#xff0c;GNU 调试器&#xff0c;和其他编译软件所必需的开发库和工具。本节用于重点介绍安装“Ubuntu系统下的C语言编译器GCC”和使用。 1、在安装前…

图片纹理贴图

/* * 当需要给图形赋予真实颜色的时候&#xff0c;不太可能为没一个顶点指定一个颜色&#xff0c;通常会采用纹理贴图 * 每个顶点关联一个纹理坐标 (Texture Coordinate) 其它片段上进行片段插值 * */#include <iostream> #define STBI_NO_SIMD #define STB_IMAGE_IMPLE…

【嵌入式移植】1、Ubuntu系统准备

Ubuntu系统准备 虚拟机与Ubuntu安装下载Ubuntu创建虚拟机系统配置 虚拟机与Ubuntu安装 嵌入式移植通常使用Linux操作系统的环境&#xff0c;使用Linux下的交叉编译工具链对BootLoader、kernel以及应用程序进行编译&#xff0c;然后下载运行。当然也可以通过各类IDE或者Windows…

从文本(.txt)文件中读取数据时出现中文乱码

前言 当需要从记事本中读取数据时&#xff0c;发现读取的数据会出现中文乱码&#xff0c;我尝试了C和C读取文件&#xff0c;发现都是这样。 乱码原因 文本文件的保存默认使用UTF-8编码方式&#xff0c;而VS编译器的编码方式是GBK&#xff0c;所以不同的编码方式导致了乱码。…

【leetcode】力扣算法之删除链表中倒数第n个节点【中等难度】

删除链表中倒数第n个节点 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 用例 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5] 输入&#xff1a;head [1], n 1 输出&#xff1a;[] 输入&#xff1a;head …

各种锁的概述

乐观锁与悲观锁 悲观锁指对数据被外界修改持保守态度&#xff0c;认为数据很容易就会被其他线程修改&#xff0c;所以在数据被处理前先对数据进行加锁&#xff0c;并在整个数据处理过程中&#xff0c;使数据处于锁定状态。 悲观锁的实现往往依靠数据库提供的锁机制&#xff0…

计算机体系结构期末复习流程大纲

1.存储器和cache 存储器的容量、速度与价格之间的要求是相互矛盾的&#xff0c;速度越快&#xff0c;没bit位价格越高&#xff0c;容量越大&#xff0c;速度越慢&#xff0c;目前主存一般有DRAM构成。 处理器CPU访问存储器的指标&#xff1a; 延迟时间&#xff08;Latency&am…

【C++】—— 工厂模式详解

目录 &#xff08;一&#xff09;工厂模式的特点 &#xff08;二&#xff09;工厂模式分类 1、简单工厂模式 2、工厂方法模式 3、抽象工厂模式 &#xff08;三&#xff09;总结与回顾 &#xff08;一&#xff09;工厂模式的特点 1、优势 ⼯⼚模式是⼀种创建型设计模式&a…

快速入门Visual Studio 2022开发.Net Framework研发环境指南

IDE工具 Visual Studio 2022 Vs2022企业版 - VisualStudioSetup.exe Visual Studio Code VSCodeUserSetup-x64-1.66.2.exeVSCodeUserSetup-x64-1.67.0-insider.exe IDE环境 编程字体YaHei.Consolas YaHei.Consolas.1.12.ttf IDE插件 Visual Studio Code常用插件 Chinese…

django项目基础后端功能使用

参考材料 Django新手项目实例-CSDN博客 一、django安装 pip3 install django 二、django项目新建 在目标目下执行 django-admin startproject testdjgo 执行完成后生成对应项目路径 三、django路由功能编写 /xxx/urls.py中编写路由信息&#xff0c;并且把路由转发到对应…

说出来你别不信,盲订问界M9的原因 你们想错了,他们只图这个

文|AUTO芯球 作者|李瑞 怎么还有人说华为是骗子&#xff1f; 华为一张海报说问界M9上市6天&#xff0c;大定超过3万台。有些人就说这是假的&#xff0c;反正没第三方数据&#xff0c;华为可以随便写。 我去&#xff0c;我作为一名大定问界M9的车主&#xff0c;就奉劝哪些黑子…