赞
踩
模式 | 描述 | 含义 |
---|---|---|
“r” | 读 | 文件不存在失败返回null |
“r+” | 读写 | 文件不存在打开失败返回null,文件存在则从头开始覆盖现有的数据(不会清空数据) |
“w” | 写 | 文件不存在创建文件,文件存在则清空文件 |
“w+” | 读写 | 文件不存在创建文件,文件存在清空文件 |
“a” | 写 | 文件不存在创建文件,文件存在数据被附加到文件末尾 |
“a+” | 读写 | 文件不存在创建文件,文件存在数据被附加到文件末尾 |
情景一:
在没有myfile文件的情况下一”w“方式打开文件。
#include<stdio.h> #include<string.h> int main() { FILE* fd=fopen("myfile","w"); if(!fd) { printf("fopen error!\n"); } const char *msg="hello world\n"; int cnt=6; while(cnt--) { fwrite(msg,strlen(msg),1,fd); } fclose(fd); return 0; }
以上的代码会出现的情况:在当前目录创建了一个叫myfile的文件,并且会往文件中写入数据。
情景二:
存在myfile 文件的时候使用”r“方式打开文件
#include<stdio.h> #include<string.h> #include <unistd.h> #include <sys/types.h> int main() { FILE*fd=fopen("myfile","r"); if(!fd) { printf("fopen error!\n"); } char buf[1024]; const char*msg="hello world\n"; while(1) { ssize_t s=fread(buf,1,strlen(msg),fd); if(s>0) { buf[s]=0; printf("%s",buf); } if(feof(fd)) { break; } } fclose(fd); return 0; }
以上的代码会出现的情况:会读取当前目录下的myfile文件,并且会往文件中的数据读取到显示器上。
feof是检测流上的文件结束符的函数,如果文件结束,则返回非0值,否则返回0;站在光标所在位置,向后看还有没有字符,如果有,返回0;如果没有,返回非0。它并不会读取相关信息,只是查看光标后是否还有内容。
pathname: 要打开或创建的目标文件
flags: 打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行“或”运算,构成flags。
参数:
O_RDONLY: 只读打开
O_WRONLY: 只写打开
O_RDWR : 读,写打开
**这三个常量,必须指定一个且只能指定一个 **
O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限
O_APPEND: 追加写
返回值:
成功:新打开的文件描述符
失败:-1
write(int fd, const void *buf, size_t count);
第一个参数 文件描述符fd
第二个参数 无类型的指针buf,可以存放要写的内容
第三个参数 写多少字节数
返回值:如果顺利write()会返回实际写入的字节数(len)。错误发生时则返回-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("myfile", O_RDONLY); if (fd < 0) { perror("open"); return 1; } const char *msg = "hello world\n"; char buf[1024]; while (1) { ssize_t s = read(fd, buf, strlen(msg)); // 类比write if (s > 0) { printf("%s", buf); } else { break; } } close(fd); return 0; }
在Linux操作系统中,文件描述符是一种用于访问文件或输入/输出资源的抽象概念,它是为了更有效地管理和操作文件、设备、套接字等资源而引入的。文件描述符的作用和重要性在操作系统和编程中具有深远意义。
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <string.h> int main() { char buf[1024]; ssize_t s = read(0, buf, sizeof(buf)); if (s > 0) { buf[s] = 0; write(1, buf, strlen(buf)); write(2, buf, strlen(buf)); } return 0; }
文件描述符是一个下标,是用来标识和操作文件或者输入输出设备的整数。每个打开的文件(包括标准输入、标准输出和标准错误输出)都会被分配一个唯一的文件描述符。
文件描述符的分配规则:在files_struct数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。
我们平常的输入是从标准输入(键盘)读取,输出往标准输出(显示器)打印!输入和输出重定向就是不再从标准输入读取,向标准输出打印了,就是从指定的文件读取,或只输入到指定文件。
输出通常包含两种类型:
什么是输入:
默认情况下,标准输入连接到键盘。I/O重定向功能可以改变输出内容发送的目的地,也可以改变输入内容的来源地。
前提知识:
操作系统内部只认识只认识文件标识符fd,并不认识所谓的stdin、stdout。
文件描述符的分配规则是从小到大没有被分配的文件描述符分配给新文件。
总结:
如果我们关闭标准输出,然后打开指定的文件,这时新打开的文件fd的值,就是我们刚刚关闭的标准输出文件的fd的值,所以我们就可以把输出定向到这个文件。这就是我们说的输出重定向,输入重定向也是一样的。
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> int main() { close(1); int fd = open("myfile", O_WRONLY | O_CREAT, 00644); if (fd < 0) { perror("open"); return 1; } printf("fd: %d\n", fd); fflush(stdout); close(fd); exit(0); }
问题:
就是修改特定的文件描述符。
#include <unistd.h>
int dup2(int oldfd, int newfd);
dup2函数是Unix/Linux系统提供的一个系统调用函数其作用是
参数:
返回值:
该函数返回新的文件描述符,若出错则返回-1。
工作原理:
#include <stdio.h> #include <unistd.h> #include <fcntl.h> int main() { int fd = open("./log", O_CREAT | O_RDWR,0666); if (fd < 0) { perror("open"); return 1; } close(1); dup2(fd, 1); for (;;) { char buf[1024] = {0}; ssize_t read_size = read(0, buf, sizeof(buf) - 1); if (read_size < 0) { perror("read"); break; } printf("%s", buf); fflush(stdout); } return 0; }
过程:
通过open函数打开文件,如果不存在就创建,存在就打开文件;然后关闭close描述符;在使用dup2函数复制描述符;最后把输入的东西输出到文件中。
本质就是C语言提供的结构体
struct _iobuf {
char *_ptr; // 文件输入的下一个位置
int _cnt; // 当前缓冲区位置
char *_base; // 文件起始位置
int _flag; // 文件标志
int _file; // 文件有效性验证
int _charbuf; // 当前缓冲区状况
int _bufsiz; // 缓冲区大小
char *_tmpfname; // 临时文件名
};
typedef struct _iobuf FILE; //别名FILE,可以直接用
缓冲区(Buffer)是内存空间的一部分。也就是说,在内存中预留了一定的存储空间,用来暂时保存输入或输出的数据,这部分预留的空间就叫做缓冲区。
缓存区根据对应的设备分为:
输入缓存区和输出缓存区
缓存区的分类
完全缓存:就是等整个缓存区被填满,才会被刷新缓存。
无缓存:不对输入输出操作进行缓存,对流的读写可以立即操作实际文件。
行缓存:当一行结束的时候便开始刷新缓存,想要刷新行缓存,只要使用换行符’\n’便成功。
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <string.h> int main() { const char *msg0 = "hello printf\n"; const char *msg1 = "hello fwrite\n"; const char *msg2 = "hello write\n"; printf("%s", msg0); fwrite(msg1, strlen(msg0), 1, stdout); write(1, msg2, strlen(msg2)); fork(); return 0; }
运行结果:
但如果对进程实现输出重定向呢? ./hello > file , 我们发现结果变成了:
现 printf 和 fwrite (库函数)都输出了2次,而 write 只输出了一次(系统调用)。为什么呢?肯定和 fork有关!
问题:
缓存主要是为了提高数据的读取速度。因为服务器和应用客户端之间存在着流量的瓶颈,所以读取大容量数据时,使用缓存来直接为客户端服务,可以减少客户端与服务器端的数据交互,从而大大提高程序的性能。
Linux文件系统中的文件是数据的集合,文件系统不仅包含着文件中的数据而且还有文件系统的结构,所有Linux 用户和程序看到的文件、目录、软连接及文件保护信息等都存储在其中。
底层原理图:
每行包括7列:(模式、硬链接数、文件所有者、组、大小、最后修改时间、文件名)
产生inode的前提?
文件是存储在硬盘上的。硬盘的最小单位是扇区,每个扇区的大小为512字节。如果系统在读取硬盘数据的时候按扇区一个一个来读取,那效率就太低了,而是一次连续性读取多个扇区,所以设计者又将多个扇区整合成一个块(block),所以,块就是文件存取的最小单位。一个块的大小为4k。我们现在已经有了块的概念,文件数据就是存放在块中。但光有数据还是不行啊?为了方便管理文件,我们还需要文件的元信息,比如文件的属性,创建时间,权限,所占的块大小,数量等等。
什么是inode?
inode 是一个关键的元数据结构,每个文件或目录都有一个唯一的 inode 号码来标识它。inode 存储了文件的类型、所有者、权限、时间戳、数据块分配情况等信息,而文件的实际内容则存储在数据块中。通过 inode,文件系统可以快速地查找和访问文件的相关信息,而不必遍历整个文件系统。所以硬盘在分区的时候会分为两个区域,一个区域存放数据,一个区域存放inode信息。
还可以通过stat命令看到更多的信息
文件名
inode编号
文件拥有者uid
文件的所属用户组 gid
文件的可读,可写,可执行权限 :Access: (0755/drwxr-xr-x)
文件的时间戳:
硬链接数:links
文件数据的所占用的块:blocks
文件所占用的字节数 size
超级块(Super Block):存放文件系统本身的结构信息。记录的信息主要有:bolck 和 inode的总量, 未使用的block和inode的数量,一个block和inode的大小,最近一次挂载的时间,最近一次写入数据的 时间,最近一次检验磁盘的时间等其他文件系统的相关信息。Super Block的信息被破坏,可以说整个 文件系统结构就被破坏了
GDT,Group Descriptor Table:块组描述符,描述块组属性信息
块位图(Block Bitmap):Block Bitmap中记录着DataBlock中哪个数据块已经被占用,哪个数据块没有被占用
inode位图(inode Bitmap):每个bit表示一个inode是否空闲可用。
i节点表:存放文件属性 如 文件大小,所有者,最近修改时间等
数据块:存放文件内容
文件系统是操作系统用于明确存储设备(常见的是磁盘,也有基于NAND Flash的固态硬盘)或分区上的文件的方法和数据结构,即在存储设备上组织文件的方法。操作系统中负责管理和存储文件信息的软件机构称为文件管理系统,简称文件系统。
常见的几种文件结构:
文件系统的功能包括:管理和调度文件的存储空间,提供文件的逻辑结构、物理结构和存储方法;实现文件从标识到实际地址的映射,实现文件的控制操作和存取操作,实现文件信息的共享并提供可靠的文件保密和保护措施,提供文件的安全措施。
FAT(File Allocation Table)
是一种最早的文件系统类型,常用于Windows系统,具有简单、易于实现和兼容性好等特点,但是对于大容量硬盘的支持不太好。
NTFS(New Technology File System)
是Windows NT及其后续版本的默认文件系统类型,支持文件和目录的加密、压缩、权限控制等高级功能,适合大容量硬盘的管理。
EXT(Extended File System)
是Linux系统的默认文件系统类型,具有高效、稳定、可靠等特点,支持大容量硬盘和文件的管理。
HFS(Hierarchical File System)
是Mac OS系统的默认文件系统类型,具有对于苹果设备的兼容性好、支持高级功能等特点。
APFS(Apple File System)
是苹果公司开发的新一代文件系统类型,适用于苹果设备上的存储管理,具有高效、安全、可靠等特点。
什么是硬链接?
由于linux下的文件是通过索引节点(Inode)来识别文件,硬链接可以认为是一个指针,指向文件索引节点的指针,系统并不为它重新分配inode。每添加一个一个硬链接,文件的链接数就加1。
硬连接之间没有主次之分,删除某个硬链接,只是将其从目录的数据块中删除相关信息,并且文件链接数减一。不会从inode表中删除inode,除非只剩下一个链接数。
硬链接的作用?
怎么使用硬链接?
在Linux系统中,可以使用ln命令来创建硬链接。
ln [options] target_file link_name
如果你有一个名为original_file.txt的文件,并希望为它创建一个名为alias_file.txt的硬链接,你可以使用以下命令:
ln original_file.txt alias_file.txt
执行此命令后,alias_file.txt将成为original_file.txt的一个硬链接。它们将共享相同的inode和文件数据。
硬链接的应用?
什么是软连接?
特点和用途
软连接的作用?
怎么使用软连接?
软连接的应用?
什么是静态库?
定义
静态库是指在开发应用程序时,将一些公共的、需要反复使用的代码编译成的一个“库”文件。在链接步骤中,连接器会从静态库文件中取得所需的代码,并将这些代码直接复制到生成的可执行文件中。
特点
优缺点
使用方法
在开发过程中,可以通过配置编译器和链接器来使用静态库。通常需要将静态库文件添加到项目的链接器设置中,以便在编译时正确链接到静态库中的代码。具体步骤可能因开发环境和编程语言的不同而有所差异。
如何生成静态库?
gcc -c file1.c -o file1.o
gcc -c file2.c -o file2.o
# 以此类推,为所有源文件生成目标文件
3.创建头文件
ar rcs libmylib.a file1.o file2.o
# 其中libmylib.a是生成的静态库文件名,rcs是ar命令的选项,
# r表示替换,c表示创建,s表示创建目标文件索引
# 查看静态库中的目录列表
[root@localhost linux]# ar -tv libmymath.a
注意事项:
怎么使用静态库?
#include "library.h" // 假设这是静态库提供的头文件
编译器选项(指定头文件搜索路径)
gcc -I/path/to/library/headers your_program.c -o your_program
链接器选项(指定库文件路径和名称)
gcc your_program.c -L/path/to/library -llibrary -o your_program
在这个例子中,-llibrary告诉链接器链接到名为library的库,链接器会在-L指定的路径下查找名为liblibrary.a(在Unix系统上)或library.lib(在Windows系统上)的文件。
注意事项和归纳
什么是动态库?
定义
动态库,又称为动态链接库(Dynamic Link Library,简称DLL),在Windows系统中通常以.dll为文件扩展名,而在Linux或Unix系统中则通常以.so为文件扩展名。动态库在程序运行时被加载到内存中,与程序进行动态链接。
特点
使用方式
在使用动态库时,需要在编译时指定动态库的接口,而不是将其代码直接复制到可执行文件中。这意味着在编译时,编译器会生成一个对动态库接口的引用,而不是将动态库的代码嵌入到可执行文件中。在程序运行时,操作系统会根据这个引用加载相应的动态库到内存中,并将程序与动态库进行链接。
示例
在Linux系统中,可以使用gcc编译器和ld链接器来创建和使用动态库。首先,通过gcc编译器将源文件编译为目标文件(.o文件),并使用-fPIC选项生成位置无关的代码。然后,使用gcc的-shared选项将目标文件链接为动态库(.so文件)。最后,在编译使用动态库的程序时,需要指定动态库的接口(即头文件)和动态库文件的路径(使用-L选项指定路径,-l选项指定库名)。
注意事项
如何生成动态库?
gcc -c -fPIC source_file.c -o source_file.o
在这里,source_file.c是你的源文件,source_file.o是生成的目标文件。
3. 链接目标文件以生成动态库
使用GCC的-shared选项,你可以将目标文件链接成一个动态库。假设你有一个或多个目标文件,你可以将它们全部链接到一个动态库中。
gcc -shared -o libmy_library.so source_file1.o source_file2.o ...
在这里,libmy_library.so是你想要生成的动态库文件的名字。
归纳
生成动态库主要涉及以下几个步骤:
怎么使用动态库?
归纳
使用动态库主要涉及以下几个关键步骤:
怎么安装动态库?
既然在系统默认的搜索路径下找不到我们的库文件和头文件,我们就将它们拷贝到系统的默认搜索路径中。
拷贝头文件
sudo cp mylib/include/.h /usr/include/
拷贝库文件
sudo cp mylib/lib/.so /lib64
ln -s mylib/lib/libmylib.so libmylib.so
删除当前软链接的指令为 rm -rf libmylib.so;如果使用 rm -rf libmylib.so/ 则会将该软链接指向的目录全部清空!
系统在运行的时候会去帮我们找我们的库,去哪里找呢?除了系统默认库路径下去找,还会去LD_LIBRARY_PATH 加载库的环境变量中去找!
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/path/to/library
但是当前这种方法导环境变量是内存级的,当我们退出后重新进入它就没了
在系统中存在一个 /etc/ld.so.conf.d/ 这样的一个配置文件目录,这是系统管理所有系统动态库加载相关的配置文件。
sudo touch /etc/ld.so.conf.d/temp.conf
然后填写动态库的路径,最后在使用sudo ldconfig刷新
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。