【Linux】文件描述符——有这篇就够了

目录

前言

预备知识 

复习C语言的文件接口

写方式打开文件

追加方式打开文件

读方式打开文件

系统的文件接口

open

close 

write

read

文件描述符

0 & 1 & 2 

理解文件描述符

文件描述符的分配规则

重定向的本质

dup2

理解Linux下一切皆文件

缓冲区

认识缓冲区 

缓冲区的体现


前言

本篇文章内容有点多,但是非常的重要,,希望你可以坚持看下去,看会你将会对Linux下一切皆文件有一个全新的理解。

如果内容有错或者不足,还望你能指出,让我们共同进步

预备知识 

知识点1 

当我们创建了一个空文件时,这个空文件是否会占空间呢?

答案是:当然啦,因为文件 = 内容 + 属性,文件的属性也是数据当然会占空间啦

因此我们对文件的操作无外乎是对文件的内容或者文件的属性做操作。

知识点2

文件会被加载到磁盘中,我们在C语言或者其它语言中通过编写代码并运行来访问文件,那么这本质是谁在访问文件呢?

答:是进程通过C语言或者其它语言给我们提供的文件接口来访问的。

知识点3 

我们的磁盘是属于硬件,我们普通用户是没有权利向硬件中写入的,只有操作系统才有这个权利,而我们也想要向硬件中写入的话必须通过操作系统提供给我们的接口才能向磁盘中写入。

知识点4

显示器也是属于硬件,我们在C语言中使用printf向显示器中打印时,其实和向磁盘中写入是一样的,没有本质的区别。 

知识点5

所有的语言给我们都提供了有文件访问接口,其实在底层实现都封装的是系统调用的接口,那么这些语言为什么要封装其接口呢?

封装的原因是系统调用的接口比较难,为了能让这些接口更好的给用户使用,所以在语言层面上对这些接口进行了封装。并且这些语言为了实现跨平台性,在底层把所用平台提供的文件接口都实现了一遍,通过条件编译的方式将这些代码进行动态裁剪(就跟多态才不多),这样就能实现在不同平台调用的文件访问接口都是一样的。

知识点6

Linux下一切皆文件,在这里先有个感性的认识,站在你写代码的角度,你会认为加载到内存中的就是文件,但站在系统的角度,能够被读取或者能够被写入的设备就叫做文件。

狭义上理解的文件:普通的磁盘文件

广义上理解的文件:几乎所有外设都可被称为文件。

复习C语言的文件接口

文件打开的方式

写方式打开文件

 先来看看下面的代码

#include <stdio.h>int main()
{FILE *fp = fopen("log.txt", "w");if(fp == NULL){perror("fopen");return 1;}fclose(fp);return 0;
}

我们都知道当我们执行这份代码时,如果当前路径下没有log.txt文件,w方式会在当前路径下创建log.txt文件,那么什么叫做当前路径呢?

所以当前路径是指当你的进程运行起来时所处的工作路径

在来看看下面的代码

#include <stdio.h>
#include <string.h>int main()
{FILE *fp = fopen("log.txt", "w");if(fp == NULL){perror("fopen");return 1;}const char *s1 = "hello man\n";fwrite(s1, strlen(s1), 1, fp);fclose(fp);return 0;
}

我们知道字符串的末尾是会加上一个'\0',那么这里的strlen(s1)是否要加1呢?

答案肯定是不要的,因为'\0'结尾是C语言的规定,文件不需要遵守,并且文件中要保存的是有效数据,'\0'不是有效数据。如果你加上1的话就会出现以下乱码的情况。

当我们以w方式写入时,是先清空文件中的内容再写入。

把上个图片中的hello man和乱码清除再写入hello Linux

这个就和输出重定向很像

追加方式打开文件

#include <stdio.h>
#include <string.h>int main()
{FILE *fp = fopen("log.txt", "a");if(fp == NULL){perror("fopen");return 1;}const char *s1 = "hello Linux\n";fwrite(s1, strlen(s1), 1, fp);fclose(fp);return 0;
}

 

读方式打开文件

#include <stdio.h>
#include <string.h>int main(int argc, char *argv[])//命令行参数
{if(argc != 2){printf("argv error");return 1;}FILE *fp = fopen(argv[1], "r");if(fp == NULL){perror("fopen");return 2;}char line[64];while(fgets(line, sizeof(line), fp) != NULL){fprintf(stdout, "%s", line);//向显示器输出}fclose(fp);return 0;
}

这样就实现了和cat一样的功能

在上面的代码中用到了一个stdout,这个stdout为标准输出

在C语言中会默认打开三个标准输入输出流:stdin、stdout、stderr

并且这三个流的类型都是FILE*的(解释在后面)

系统的文件接口

在上面已经说到了C语言标准库中给我们提供的文件操作的函数在底层其实是调用的系统提供的文件接口,那么系统给我们提供了哪些文件接口呢?下面让我们来认识一下这些文件接口

open

打开成功返回的是一个文件描述符,失败则返回-1

参数介绍

pathname:要打开的目标文件

flags:打开文件的方式。提供了多个选项,可传入多个选项共同构成这个参数

下面三个选项必须指定一个且只能指定一个

  • O_RDONLY:只读
  • O_WRONLY:只写
  • O_RDWR:可读可写

上面选项配合其它选项一起使用,如

  • O_APPEND 表示追加方式的写入文件
  • O_CREAT 表示如果指定文件不存在,则创建这个文件
  • O_TRUNC 表示截断,如果文件存在,并且以只写、读写方式打开,则将其长度截断为0。
  • ……

这些选项还有好多这里就不一一列举了,大家可以通过man进行查看

可以观察到这些选项都是大写,一般这种大写的基本上是宏定义,这里也不例外。

我们通过或(|)运算就可以将这些选项结合在一起使用了,例如:O_WRONLY|O_CREAT

为什么使用或运算就可以将这些选项结合在一起使用了呢?

其实这里是用到了一个位图的思想,看了下面代码想必你应该就会明白了

#include <stdio.h>
#include <string.h>//用不重复的位就可以标识一个状态
#define ONE 0x1 //0000 0001
#define TWO 0x2 //0000 0010
#define THREE 0x4 //0000 0100void print(int flags)
{//&运算:有0则为0,全1才为1if(flags & ONE) printf("I am ONE\n");if(flags & TWO) printf("I am TWO\n");if(flags & THREE) printf("I am THREE\n");
}int main()
{print(ONE);printf("--------------------------------\n");print(TWO);printf("--------------------------------\n");print(THREE);printf("--------------------------------\n");print(ONE | TWO | THREE);//0000 0001 | 0000 0010 | 0000 0100 = 0000 0111printf("--------------------------------\n");print(ONE | TWO);//0000 0001 | 0000 0010 = 0000 0011printf("--------------------------------\n");print(ONE | THREE);//0000 0001 | 0000 0100 = 0000 0101printf("--------------------------------\n");return 0;
}

库中也确实是这样干的

mode:为设置文件访问的权限

红色区域即为文件的权限,新建文件夹的默认权限是0666,但实际看到的权限不是这个值,因为还会受到umask的影响,umask默认是0002

使用

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main()
{int fd = open("log.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666);if(fd < 0){perror("open");return 1;}printf("open success, fd:%d\n", fd);return 0;
}

close 

关闭一个文件描述符,成功返回0,失败返回-1并且设置对应的错误码

和fclose的用法就差不多

write

返回值为实际写入的有效数据,ssize_t为有符号整形

参数介绍

fd:为文件描述符

buf:为要写入的数据

count:为写入数据的大小

使用演示

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main()
{int fd = open("log.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666);if(fd < 0){perror("open");return 1;}printf("open success, fd:%d\n", fd);const char *s1 = "hello write\n";write(fd, s1, strlen(s1));close(fd);return 0;
}

 

read

 

返回值的读取到的有效数据 

参数和write一样就不过多介绍了

使用演示

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main()
{int fd = open("log.txt", O_RDONLY);if(fd < 0){perror("open");return 1;}printf("open success, fd:%d\n", fd);char buffer[64];memset(buffer, '\0', sizeof(buffer));read(fd, buffer, sizeof(buffer));printf("%s\n", buffer);close(fd);return 0;
}

这里系统接口可不会给我们在末尾加上\0,所以我们需要手动在末尾加上'\0'。 

文件描述符

0 & 1 & 2 

先来看一下下面的代码 

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main()
{int fd1 = open("log1.txt", O_WRONLY|O_CREAT);int fd2 = open("log2.txt", O_WRONLY|O_CREAT);int fd3 = open("log3.txt", O_WRONLY|O_CREAT);int fd4 = open("log4.txt", O_WRONLY|O_CREAT);printf("open success, fd1:%d\n", fd1);printf("open success, fd2:%d\n", fd2);printf("open success, fd3:%d\n", fd3);printf("open success, fd4:%d\n", fd4);close(fd1);close(fd2);close(fd3);close(fd4);return 0;
}

从上面的的代码可以看出fd都是从3开始的,那么前面的0,1,2去哪里了呢

在上面复习C语言文件接口时,提到过C语言中会默认打开三个标准输入输出流:stdin、stdout、stderr,这三个标准的输入输出流对应的就是0,1,2。

不信的话我们可以验证一下

stdout 

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main()
{fprintf(stdout, "hello stdout\n");const char *s1 = "hello 1\n";write(1, s1, strlen(s1));return 0;
}

stdin 

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main()
{char buf[64];ssize_t s = read(0, buf, sizeof(buf));if(s > 0){buf[s] = '\0';//在读取到的有效数据的末尾添加\0printf("%s\n",buf);}return 0;
}

我们再回到C语言中,我们在使用C语言的文件函数时都见过FILE*,这个FILE*是一个指针,那么这个FILE是什么呢?

FILE其实是一个结构体,是C语言对底层做出的一个封装,里面包含了很多成员其中就有这个fd(文件描述符)。

#include <stdio.h>int main()
{printf("stdin:%d\n", stdin->_fileno);printf("stdout:%d\n", stdout->_fileno);printf("stderr:%d\n", stderr->_fileno);return 0;
}

理解文件描述符

进程要访问文件,必须先要打开文件,并且一个进程可以打开多个文件,那么如果多个进程都打开自己的文件,系统中就会存在大量被打开的文件,面对如此之多的文件,操作系统肯定要管理起来,而管理的本质就是先描述,再组织。所以在操作系统的内部为了管理每一个被打开的文件,会构建一个结构体,这个结构体中包含了一个被打开的文件的所有内容,然后再将这一个个的结构体用双链表组织起来。

当我们调用open时,必须让进程和文件关联起来,才能打开对应文件,那么我们的进程又是如何和文件关联起来的呢?

在每个进程的PCB中都有一个指针*files,指向一张表files_struct,该表中会包含一个指针数组,数组中的每个元素都是一个指向打开文件的指针,这就实现了和文件进行关联。

那么既然是数组就会有下标,并且是从0开始的,所以文件描述符的本质就是该指针数组的下标。所以只要拿着文件描述符,就可找到对应的文件。

文件描述符的分配规则

从上面的演示中我们就知道了0,1,2已经被三个标准输入输出流给占了,所以我们调用open打开文件时文件描述符(后面就简称fd了)只能从3以后开始,那么我们尝试close关闭0或者2看一下会发生什么现象?

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main()
{//close(0);close(2);int fd = open("log.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666);if(fd < 0){perror("open");return 1;}printf("fd: %d\n", fd); printf("fd: %d\n", fd); printf("fd: %d\n", fd); printf("fd: %d\n", fd); printf("fd: %d\n", fd); printf("fd: %d\n", fd); close(fd);return 0;
}

所以fd的分配规则是:当前没有被使用的最小的一个下标

重定向的本质

当我们close关闭1又会发生什么现象呢?

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main()
{close(1);int fd = open("log.txt", O_WRONLY|O_CREAT, 0666);if(fd < 0){perror("open");return 1;}printf("fd: %d\n", fd); printf("fd: %d\n", fd); printf("fd: %d\n", fd); printf("fd: %d\n", fd); printf("fd: %d\n", fd); printf("fd: %d\n", fd);fflush(stdout);close(fd);return 0;
}

本该往显示器打印的内容结果打印到文件中去了。

这种现象是不是就和输出重定向是一样的了

本质是在操作系统内部更改了fd对应的内容指向

输入重定向

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main()
{close(0);int fd = open("log.txt", O_RDONLY, 0666);if(fd < 0){perror("open");return 1;}printf("fd: %d\n", fd); char buffer[64];fgets(buffer, sizeof buffer, stdin);printf("%s\n", buffer);fflush(stdout);close(fd);return 0;
}

追加重定向

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main()
{close(1);int fd = open("log.txt", O_WRONLY|O_CREAT|O_APPEND);if(fd < 0){perror("open");return 1;}fprintf(stdout, "hello hjx\n");fflush(stdout);//刷新缓冲区close(fd);return 0;
}

但事实上重定向并不是这样实现的,我们上面的实现只是利用了fd的分配规则,而在操作系统中早就给我们准备了实现重定向的接口。

dup2

 

dup2的使用描述

这里的意思就是将旧的文件描述符所对应的内容拷贝的新的文件描述符中,最后两个文件描述符是和旧的文件描述符保持一致,如果必要时把新的文件描述符关掉。

dup2的使用

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main()
{int fd = open("log.txt", O_WRONLY|O_CREAT|O_APPEND, 0666);if(fd < 0){perror("open");return 1;}dup2(fd, 1);fprintf(stdout, "hello dup2\n");close(fd);return 0;
}

理解Linux下一切皆文件

 Linux下一切皆文件时Linux的一个设计哲学,它是体现在操作系统的软件设计层面的。

我们都知道Linux是用C语言写的,那么你知道怎么用C语言来实现面向对象,甚至是运行时多态吗?

在C++或者其它语言中实现面向对象我们都知道是要用类,类中包含了成员属性和成员方法,但是C语言中只有结构体的概念,而结构体中只能包含成员属性,包含不了成员方法,那该怎么办呢?

没事,我们可以用到一个函数指针来实现其效果。

我们知道操作系统再往下就是硬件(比如磁盘、网卡、键盘、显示器等等)了,这些不同的硬件对应的一定是不同的驱动方法(你访问磁盘和访问键盘是不一样的),但是这些硬件都是外设,所以这每一个设备的核心驱动程序都可以是read/write——>I/O,根据冯诺依曼所有的外设无非都是I/O,所以所有的外设都可以有自己的read和write,但是代码实现一定是不一样的。

这种设计方案就叫做虚拟文件系统(VFS)。 

在Linux内核中也确实是这么干的。 

内核源码

struct file_operations {struct module *owner;loff_t (*llseek) (struct file *, loff_t, int);ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);ssize_t (*aio_read) (struct kiocb *, char __user *, size_t, loff_t);ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);ssize_t (*aio_write) (struct kiocb *, const char __user *, size_t, loff_t);int (*readdir) (struct file *, void *, filldir_t);unsigned int (*poll) (struct file *, struct poll_table_struct *);int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);int (*mmap) (struct file *, struct vm_area_struct *);int (*open) (struct inode *, struct file *);int (*flush) (struct file *);int (*release) (struct inode *, struct file *);int (*fsync) (struct file *, struct dentry *, int datasync);int (*aio_fsync) (struct kiocb *, int datasync);int (*fasync) (int, struct file *, int);int (*lock) (struct file *, int, struct file_lock *);ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *);ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);int (*check_flags)(int);int (*dir_notify)(struct file *filp, unsigned long arg);int (*flock) (struct file *, int, struct file_lock *);
};

缓冲区

认识缓冲区 

缓冲区其实是一段内存空间,就跟你寄快递一样,你把快递交给菜鸟,而菜鸟收到你要寄的快递并不是马上就帮你寄过去,而是先在菜鸟店里暂存等要寄的快递达到一定量时,才一起将这些快递寄出去。而这个例子中的菜鸟就起到了一个缓冲区的作用。

缓冲区的刷新策略分为:1、立即刷新    2、行刷新(行缓冲)   3、满刷新(全缓冲)

但也有特殊情况:1、用户强置刷新(fflush)   2、进程退出

为什么要有缓冲区呢?

缓冲区的出现是为了提高整机的效率,提高用户的响应速度。就比如说你寄快递是你自己亲自送到对方手中好,还是交给菜鸟由菜鸟帮你送好呢?

缓冲区的体现

 再来看一下下面的代码

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main()
{//c语言接口printf("I am printf\n");fprintf(stdout, "I am fprintf\n");const char *s = "I am s\n";fputs(s, stdout);//操作系统接口const char *s1 = "I am s1\n";write(1, s1, strlen(s1));fork();return 0;
}

 注意:fork()之前,fork上面的代码已经是执行完了

我们运行一下往显示器上打印

结果是正常的,没有任何问题,那么我们用输出重定向往文件中打印看看会发生什么呢

我们会发现凡是用了C语言接口的都打印了两次这是为什么呢?

解释:一般而言,我们的显示器采用的都是行刷新策略,而我们的磁盘文件采用的是满刷新策略,并且所有的设备都倾向于满刷新策略,因为缓冲区满了才刷新,可以减少I/O操作以及对外设的访问,从而提高效率。而显示器是给用户看的,为了照顾用户又考虑到效率问题所以采用行刷新策略。所以在上面的代码中,我们向显示器中打印,代码中的 \n 就会起作用——直接刷新到显示器上,而当我们向磁盘文件中打印时,磁盘文件采用的是满刷新,所以 \n 就不会起作用,我们的数据是被放在了缓冲区中,而且从上面的结果来看,缓冲区是语言层面给我们提供的不是系统给我们提供的。不要忘了我们执行完fork后是立即退出了(进程退出了),所以缓冲区中的数据要给我们刷新出来,并且fork创建子进程时,会有写时拷贝的发生。

因此综上所述,写时拷贝+缓冲区是我们的C语言数据打印了两次。

在上面解释中所说的缓冲区是用户级缓冲区,其实操作系统也会提供内核级别的缓冲区(不在写时拷贝的范围内),但是这里就不在讨论了,有兴趣的话可以去内核源码中找一找。

C语言提供的缓冲区

在/usr/include/stdio.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
};

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

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

相关文章

09_FreeRTOS任务通知

任务通知 任务通知常用任务通知API函数 任务通知 FreeRTOS 从 V8.2.0 版本开始提供任务通知这个功能&#xff0c;每个任务都有一个 32 位的通知值&#xff0c;在大多数情况下&#xff0c;任务通知可以替代二值信号量、计数信号量、事件组&#xff0c;也可以替代长度为 1 的队列…

自制Apache-Doris 2.0.4镜像Docker部署一Fe和一Be集群及遇到的问题解决

自制Apache-Doris 2.0.4镜像Docker部署一Fe和一Be集群及遇到的问题解决 文章目录 1.前言2.doris是什么&#xff1f;2.1简介2.2介绍2.3使用场景2.4架构 3.官网4.构建部署4.1 构建环境4.2 doris2.0.4的fe和be镜像构建4.2.1 fe2.0.4镜像构建脚本4.2.2 be2.0.4镜像构建4.2.3 启动脚…

Java -- (part16)

一.多线程基础知识 1.进程:在内存中执行的应用程序 2.线程:进程中的一个最小的执行单元 3.并行:在同一时刻,有多个指令在多个CPU上同时执行 4.并发:在同一时刻,有多个指令在单个CPU上交替执行 5.CPU调度 a.分时调度 b.抢占式调度:Java程序 6.主线程:CPU和内存之间开辟的…

C++ CRUD programming for DB

1、ODBC 开放数据库互连&#xff0c;微软主导的关系型数据库接口标准&#xff0c;允许同一代码访问不同DBMS中的数据。小案例&#xff1a;C连接Access数据库----增删改查_c access数据库-CSDN博客 ODBC(Open Database Connectivity&#xff0c;开放数据库连接) ODBC是Microsof…

PCB的通孔、盲孔、埋孔

通孔&#xff1a;是从顶层到底层 盲孔&#xff1a;看不到头&#xff0c;跟井一样&#xff0c;起点永远是第一层 埋孔&#xff1a;是正反都看不到的 总结&#xff1a; 这些孔都是用来切换层的

超详细的Maven安装与使用还有内容讲解

文章目录 作用简介模型仓库 安装配置IDEA配置Maven坐标概念主要组成 IDEA创建Maven项目基本使用常用命令生命周期使用坐标导入jar包 注意事项清理maven仓库更新索引依赖 作用 Maven是专门用于管理和构建Java项目的工具&#xff0c;它的主要功能有&#xff1a; 提供了一套标准化…

力扣HOT100 - 101. 对称二叉树

解题思路&#xff1a; class Solution {public boolean isSymmetric(TreeNode root) {if(root null) return true;return recur(root.left, root.right);}boolean recur(TreeNode L, TreeNode R) {if (L null && R null) return true;if (L null || R null || L.…

基于深度学习网络的十二生肖图像分类matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 ............................................................... for i 1:16subplot(4,4,…

第七章 信息系统维护与管理

文章目录 一&#xff0c;概述二&#xff0c;信息系统的使用&#xff08;一&#xff09;用户培训&#xff08;二&#xff09;系统转换&#xff08;三&#xff09;系统运行 三&#xff0c;信息系统的维护&#xff08;一&#xff09;信息系统维护过程1&#xff0c;维护组织2&#…

Meta Llama 3本地部署

感谢阅读 环境安装收尾 环境安装 项目文件 下载完后在根目录进入命令终端&#xff08;windows下cmd、linux下终端、conda的话activate&#xff09; 运行 pip install -e .不要控制台&#xff0c;因为还要下载模型。这里挂着是节省时间 模型申请链接 复制如图所示的链接 然后…

mongodb 安装问题

1. mongodb启动时显示 Illegal instruction (core dumped) mongodb 5.0之后(包括5.0) 开始使用需要使用 AVX 指令集 2.启动时报错 ERROR: child process failed, exited with 1 通过指令 bin/mongod --repair 查看报错信息 根据报错信息进行修改 3. 配置服务器添加节点时…

【北京迅为】《iTOP-3588开发板系统编程手册》-第19章 V4L2摄像头应用编程

RK3588是一款低功耗、高性能的处理器&#xff0c;适用于基于arm的PC和Edge计算设备、个人移动互联网设备等数字多媒体应用&#xff0c;RK3588支持8K视频编解码&#xff0c;内置GPU可以完全兼容OpenGLES 1.1、2.0和3.2。RK3588引入了新一代完全基于硬件的最大4800万像素ISP&…

企商在线亮相2024中国生成式AI大会,展出多元异构算力服务

4月18—19日&#xff0c;由知名媒体机构智东西与智猩猩共同主办的2024中国生成式AI大会在北京举行&#xff0c;55位重量级产学研投界代表同台分享。企商在线作为算力行业代表企业&#xff0c;参展生成式AI展区&#xff0c;现场展出企商在线AI算力平台及异构算力服务。 大会以“…

三分钟快速理解Flink 作业提交流程(包工头的工程之路)

核心组件 我们先来简单了解一下 flink 作业提交涉及到的组件 同时&#xff0c;如果不了解 Yarn 的同学欢迎跳转到这篇文章&#xff0c;了解一下健鑫集团的工程承包流程(doge): 三分钟快速理解Yarn的工作流程 JobManager JobManager 是整个flink作业的管理者 包含 Dispatch…

“PowerInfer:消费级GPU上的高效大型语言模型推理引擎“

PowerInfer是由上海交通大学IPADS实验室开发的一个高效大型语言模型&#xff08;LLM&#xff09;推理引擎&#xff0c;专为个人电脑&#xff08;PC&#xff09;上的消费者级GPU设计。它通过利用LLM推理中的高局部性&#xff0c;实现了快速且资源消耗低的模型推理&#xff0c;这…

深入探究图像增强(C语言实现)

我们将从基础出发使用C语言进行图像处理与分析&#xff0c;重点讨论图像增强和平滑技术。图像增强技术旨在通过增加对比度、亮度和整体清晰度来改善图像的视觉质量。另一方面&#xff0c;图像平滑方法则用于减少噪声并减少图像中的突变&#xff0c;使图像更加均匀和视觉上吸引人…

Github Copilot正版的激活成功,终于可以chat了

Github Copilot 代码补全等功能&#xff0c;提高写代码的效率 https://web.52shizhan.cn/activity/copilot 登录授权后&#xff0c;已经可以使用&#xff0c;完美。如图

OpenFE:开启数据特征工程新时代

OpenFE&#xff1a;开启数据特征工程新时代 数据特征工程是机器学习和数据分析领域中至关重要的一环&#xff0c;它涉及对原始数据进行处理和转换&#xff0c;以提取出有用的特征&#xff0c;为模型构建和预测提供更好的输入。在这个领域中&#xff0c;Python库OpenFE为数据科学…

查找两个字符串的最长公共子串

暴力解法 #include <iostream> #include <vector> #include <cstring> using namespace std; string a, b, minn ""; // a和b是我们输入的 // minn存储的是我们最小的那个字符串string cut(int l, int r) {string tmp "";for (int i …

大小端解释以及如何使用程序判断IDE的存储模式

今天让我们来了解一下大小端的概念吧 什么是大小端&#xff1f; 大端&#xff08;存储&#xff09;模式&#xff1a;指的是数据的低位保存在内存的高地址处&#xff0c;而数据的高位则保存在内存的低地址处。 小端&#xff08;存储&#xff09;模式&#xff1a;指的是数据的低位…