FILE
因为IO相关函数与系统调用接口对应,并且库函数封装系统调用,所以本质上,访问文件都是通过fd访问的。所以C库当中的FILE结构体内部,必定封装了fd
我们看下面的代码
#include <stdio.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 printf hello fwrite hello write
但如果对进程实现输出重定向呢? ./hello > file
, 我们发现结果变成了:
hello write hello printf hello fwrite hello printf hello fwrite
我们发现 printf
和 fwrite
(库函数)都输出了2次,而 write
只输出了一次(系统调用)。为什么呢?肯定和fork
有关!
一般C库函数写入文件时是全缓冲的,而写入显示器是行缓冲。
printf fwrite
库函数会自带缓冲区(进度条例子就可以说明),当发生重定向到普通文件时,数据的缓冲方式由行缓冲变成了全缓冲。而我们放在缓冲区中的数据,就不会被立即刷新,甚至
fork
之后但是进程退出之后,会统一刷新,写入文件当中。
但是
fork
的时候,父子数据会发生写时拷贝,所以当你父进程准备刷新的时候,子进程也就有了同样的一份数据,随即产生两份数据。
write
没有变化,说明没有所谓的缓冲。
综上: printf fwrite
库函数会自带缓冲区,而 write
系统调用没有带缓冲区。另外,我们这里所说的缓冲区,都是用户级缓冲区。其实为了提升整机性能,OS
也会提供相关内核级缓冲区,不过不再我们讨论范围之内。那这个缓冲区谁提供呢? printf
fwrite
是库函数, write
是系统调用,库函数在系统调用的“上层”, 是对系统调用的“封装”,但是 write
没有缓冲区,而 printf
fwrite
有,足以说明,该缓冲区是二次加上的,又因为是C,所以由C标准库提供。
如果有兴趣,可以看看FILE结构体:
typedef struct _IO_FILE FILE; 在/usr/include/stdio.h
在/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 };
文件系统
我们输入ls -l
显示下面的文件信息
-rw-rw-r--. 1 kingxzq kingxzq 172 Aug 9 08:30 Makefile -rwxrwxr-x. 1 kingxzq kingxzq 8560 Aug 24 10:00 mycmd -rw-rw-r--. 1 kingxzq kingxzq 472 Aug 8 20:21 mycmd.c -rwxrwxr-x. 1 kingxzq kingxzq 13416 Aug 24 10:00 myshell -rw-rw-r--. 1 kingxzq kingxzq 3110 Aug 9 09:41 myshell.c -rw-r--r--. 1 kingxzq kingxzq 35 Aug 24 10:32 output.txt -rwxrwxr-x. 1 kingxzq kingxzq 8560 Aug 24 10:00 test -rw-rw-r--. 1 kingxzq kingxzq 498 Aug 24 10:00 test.c
我们看第一行文件信息
-rw-rw-r--. 1 kingxzq kingxzq 172 Aug 9 08:30 Makefile
共有 7 列信息,每列代表了不同的属性。以下是每列信息所代表的含义:
-rw-rw-r--.
: 文件权限和类型。这列显示了文件的权限和类型。在这个例子中,-
表示这是一个普通文件。后面的rw-rw-r--.
表示文件的权限,分为三组(所有者、群组、其他用户),每组的权限有读取(r
)、写入(w
)和执行(x
)。1
: 硬链接计数。这列显示了文件的硬链接计数,即有多少个目录项指向这个文件。在这里,值为1
表示只有一个目录项指向这个文件。kingxzq
: 所有者。这列显示了文件的所有者用户名。kingxzq
: 所属群组。这列显示了文件所属的用户组。172
: 文件大小。这列显示了文件的大小,以字节为单位。Aug 9 08:30
: 最后修改时间。这列显示了文件的最后修改时间,格式为月份(Aug
)、日期(9
)和时间(08:30
)。Makefile
: 文件名。这列显示了文件的名称。
ls -l
读取存储在磁盘上的文件信息,然后显示出来
除了ls -l
命令,stat
命令也能看到更多信息
[kingxzq@localhost Documents]$ stat Makefile File: ‘Makefile’ Size: 172 Blocks: 8 IO Block: 4096 regular file Device: 803h/2051d Inode: 34241206 Links: 1 Access: (0664/-rw-rw-r--) Uid: ( 1000/ kingxzq) Gid: ( 1000/ kingxzq) Context: unconfined_u:object_r:user_home_t:s0 Access: 2023-08-24 09:56:39.445610701 +0800 Modify: 2023-08-09 08:30:42.044878543 +0800 Change: 2023-08-09 08:30:42.044878543 +0800 Birth: -
inode
为了能解释清楚inode我们先简单了解一下文件系统
Linux ext2
文件系统,上图为磁盘文件系统图(内核内存映像肯定有所不同),磁盘是典型的块设备,硬盘分区被划分为一个个的block
。一个block
的大小是由格式化的时候确定的,并且不可以更改。例如mke2fs
的-b
选项可以设定block
大小为1024
、2048
或4096
字节。而上图中启动块(Boot Block
)的大小是确定的,
Block Group:
ext2
文件系统会根据分区的大小划分为数个Block Group
。而每个Block Group
都有着相同的结构组成。政府管理各区的例子超级块(Super Block):存放文件系统本身的结构信息。记录的信息主要有:
bolck
和inode
的总量,未使用的block
和inode
的数量,一个block
和inode
的大小,最近一次挂载的时间,最近一次写入数据的时间,最近一次检验磁盘的时间等其他文件系统的相关信息。Super Block
的信息被破坏,可以说整个文件系统结构就被破坏了GDT,Group Descriptor Table:块组描述符,描述块组属性信息
块位图(Block Bitmap):
Block Bitmap
中记录着Data Block
中哪个数据块已经被占用,哪个数据块没有被占用inode位图(inode Bitmap):每个
bit
表示一个inode
是否空闲可用。i节点表:存放文件属性 如 文件大小,所有者,最近修改时间等
数据区:存放文件内容
将属性和数据分开存放的想法看起来很简单,但实际上是如何工作的呢?我们通过touch一个新文件来看看如何工作?
[kingxzq@localhost Documents]$ touch abc [kingxzq@localhost Documents]$ ls -i abc 263466 abc
创建一个新文件主要有以下4个操作
- 存储属性
内核先找到一个空闲的i节点(这里是263466
)。内核把文件信息记录到其中。
- 存储数据
该文件需要存储在三个磁盘块,内核找到了三个空闲块:300
,500
,800
。将内核缓冲区的第一块数据复制到300
,下一块复制到500
,以此类推。
- 记录分配情况
文件内容按顺序300
,500
,800
存放。内核在inode
上的磁盘分布区记录了上述块列表。
- 添加文件名到目录
新的文件名abc
,linux
如何在当前的目录中记录这个文件?内核将入口(263466
,abc
)添加到目录文件。文件名和inode
之间的对应关系将文件名和文件的内容及属性连接起来。
软硬链接
在 Linux
中,硬链接(Hard Link
)和软连接(Symbolic Link
,也称为软链接或符号链接)都是用于创建文件链接的概念,但它们有一些重要的区别。
硬链接(Hard Link):
硬链接是指在文件系统中创建一个文件的副本,这个副本与原始文件共享相同的 inode
(索引节点)。因此,硬链接与原始文件在文件系统中的位置和属性是一样的,它们实际上指向同一个数据块。删除一个硬链接并不会影响其他硬链接或原始文件,只有所有的链接都被删除后,文件的内容才会真正被释放。
硬链接的特点:
- 硬链接没有独立的文件大小,因为它们共享相同的数据块。
- 硬链接不跨越文件系统边界,即不能链接到不同文件系统的文件。
- 硬链接不能链接到目录。
软连接(Symbolic Link,Symbolic Link,Symlink):
软连接是一个指向目标文件或目录的特殊文件,其中包含了目标文件的路径。它实际上是一个指向另一个文件的快捷方式,就像 Windows 系统中的快捷方式一样。软连接与硬链接不同,它有自己的 inode,并且可以跨越文件系统边界。
软连接的特点:
- 软连接有独立的文件大小,因为它包含了目标文件的路径信息。
- 软连接可以链接到不同文件系统的文件。
- 删除原始文件或目录不会影响软连接,但删除软连接不会影响原始文件。
假设你在 Linux 系统中有一个文件 original.txt
包含一些文本内容。我们将在同一目录下创建硬链接和软连接来演示它们的区别。
创建硬链接:
[kingxzq@localhost Documents]$ ln original.txt hardlink.txt
这将在同一目录下创建了一个名为 hardlink.txt
的硬链接。现在,original.txt
和 hardlink.txt
是硬链接,它们共享相同的 inode 和数据块,如下输出。
[kingxzq@localhost Documents]$ ll -i total 0 37444362 -rw-rw-r--. 2 kingxzq kingxzq 0 Aug 24 15:22 hardlink.txt 37444362 -rw-rw-r--. 2 kingxzq kingxzq 0 Aug 24 15:22 original.txt
.
和..
在我们的每个目录下,都隐藏着两个文件,.
和..
,.
代表当前路径,..
是上一级路径,它们的本质实际就是硬链接
我们可以看在Document
目录下
[kingxzq@localhost Documents]$ ll -a -i 37444361 drwxrwxr-x. 2 kingxzq kingxzq 65 Aug 24 15:28 . 67145634 drwx------. 21 kingxzq kingxzq 4096 Aug 24 15:14 ..
回到上一级路径,我们不难发现他和上面的.
的文件信息一模一样
37444361 drwxrwxr-x. 2 kingxzq kingxzq 65 Aug 24 15:28 Documents
再回到上一级路径,也不难发现他和上面的..
的文件信息一模一样
67145634 drwx------. 21 kingxzq kingxzq 4096 Aug 24 15:14 kingxzq
创建软链接:
[kingxzq@localhost Documents]$ ln -s original.txt symlink.txt
软链接symlink.txt
则拥有独立空间,所以inode
与源文件并不相同,可以理解为快捷方式
[kingxzq@localhost Documents]$ ll -i total 0 37444362 -rw-rw-r--. 2 kingxzq kingxzq 0 Aug 24 15:22 hardlink.txt 37444362 -rw-rw-r--. 2 kingxzq kingxzq 0 Aug 24 15:22 original.txt 37444368 lrwxrwxrwx. 1 kingxzq kingxzq 12 Aug 24 15:28 symlink.txt -> original.txt
这将在同一目录下创建了一个名为 symlink.txt
的软链接。现在,symlink.txt
是一个指向 original.txt
的符号链接。
现在,假设你编辑了 original.txt
中的内容。然后你可以观察到:
- 硬链接:
original.txt
和hardlink.txt
都会反映出内容的更改,因为它们实际上是同一个文件的两个名称。 - 软链接:
symlink.txt
也会反映出内容的更改,因为它指向了original.txt
的路径,而不是实际的数据块。
删除original.txt
文件,然后观察:
- 硬链接:即使删除了
original.txt
,hardlink.txt
仍然存在,因为硬链接与原始文件共享相同的数据块。 - 软链接:删除了
original.txt
后,symlink.txt
将变为无效,因为它指向的目标不存在。
这个例子说明了硬链接和软链接的不同行为和特性。
总结:
- 硬链接是在文件系统中创建的原始文件的副本,它们共享相同的
inode
和数据块。 - 软链接是一个指向目标文件的特殊文件,它包含了目标文件的路径信息。
在选择使用硬链接还是软链接时,需要根据具体情况考虑不同的需求和限制。
ACM时间
在 Linux 系统中,通常有三种主要的时间戳,如下所示:
- Access Time (atime): 这是文件的最后访问时间。当文件被读取时,系统会更新此时间戳。默认情况下,在读取文件时会更新此时间,但这可能会导致频繁的磁盘写入,因此在某些情况下可以通过挂载文件系统时的选项来禁用更新。在某些应用中,禁用
atime
更新可以提高性能。 - Modify Time (mtime): 这是文件内容的最后修改时间。当文件内容被修改时,系统会更新此时间戳。这是指文件的实际数据内容上发生了变化的时间。
- Change Time (ctime): 这是文件属性的最后修改时间。当文件的元数据(例如文件权限、所有者、链接数等)发生变化时,系统会更新此时间戳。与
mtime
不同,ctime
不仅在文件内容修改时更新,还在文件元数据修改时更新。
需要注意的是,在 Linux
中,ctime
表示文件的元数据更改时间,而不仅仅是属性的修改。这可能包括文件的内容变化、文件所有者的变更、权限的更改、硬链接数的更改等。
这些时间戳对于文件的管理和跟踪非常有用,例如检测文件是否被修改过、备份文件时判断是否需要更新等。通过命令 ls -l
可以查看文件的这些时间戳。
比如下面的时间就是文件内容最后修改时间mtime
[kingxzq@localhost Documents]$ ls -l total 0 -rw-rw-r--. 2 kingxzq kingxzq 0 Aug 24 15:22 hardlink.txt -rw-rw-r--. 2 kingxzq kingxzq 0 Aug 24 15:22 original.txt lrwxrwxrwx. 1 kingxzq kingxzq 12 Aug 24 15:28 symlink.txt -> original.txt
可以使用stat
命令来查看完整的文件时间戳信息
[kingxzq@localhost Documents]$ stat hardlink.txt File: ‘hardlink.txt’ Size: 0 Blocks: 0 IO Block: 4096 regular empty file Device: 803h/2051d Inode: 37444362 Links: 2 Access: (0664/-rw-rw-r--) Uid: ( 1000/ kingxzq) Gid: ( 1000/ kingxzq) Context: unconfined_u:object_r:user_home_t:s0 Access: 2023-08-24 15:22:19.891225541 +0800 Modify: 2023-08-24 15:22:00.971069512 +0800 Change: 2023-08-24 15:26:41.513394899 +0800 Birth: -
静态库和动态库
在 Linux 中,静态库(.a)(Static Library)和动态库(.so)(Dynamic Library)是两种不同的库文件形式,用于在编程中共享和重用代码。它们有不同的特点和用途。
静态库(Static Library):
静态库是编译时链接到程序中的一组函数和数据的集合。当你使用静态库时,编译器将库中的代码复制到你的程序中,使你的程序可以独立运行,不需要依赖外部的库文件。每个使用了静态库的可执行文件都会包含库的一份拷贝。
主要特点:
- 静态库会被完整地嵌入到最终可执行文件中,使得可执行文件变得比较大。
- 每个可执行文件都包含了库的副本,因此可执行文件更为独立,无需在运行时依赖外部库。
- 静态库的更新需要重新编译和链接可执行文件。
动态库(Dynamic Library):
动态库是在程序运行时加载的库,它不会被复制到可执行文件中,而是在系统中以共享的形式存在。多个程序可以共享同一个动态库,从而节省内存和磁盘空间。动态库在系统中只有一份实例,被多个程序共享使用。
主要特点:
- 动态库在运行时被加载,程序只需要引用动态库的接口,而不会包含库的实际代码。
- 可执行文件比较小,因为不包含库的代码。
- 动态库的更新只需要替换库文件,不需要重新编译可执行文件。
使用静态库和动态库的选择取决于不同的因素,如代码的重用性、可执行文件大小、内存占用和依赖关系。静态库适合于简单的应用,而动态库适用于需要共享的功能和模块。
总之,静态库将代码嵌入到可执行文件中,动态库在运行时加载并共享,两者在性能、依赖和文件大小等方面有所不同。
测试程序
mymath.h
#pragma once #include<stdio.h> extern int addToTarget(int from, int to);
myprint,h
#pragma once #include <stdio.h> #include <time.h> extern void Print(const char *str);
mypath.c
#include "mymath.h" int addToTarget(int from, int to) { int sum = 0; for(int i = from; i <= to; i++) { sum += i; } return sum; }
myprint.c
#include "myprint.h" void Print(const char *str) { printf("%s[%d]\n", str, (int)time(NULL)); }
main.c
#include "myprint.h" #include "mymath.h" int main() { Print("hello world"); int res = addToTarget(1,100); printf("res: %d\n", res); return 0; }
生成静态库
我们先编写Makefile
文件
libhello.a: mymath.o myprint.o ar -rc libhello.a mymath.o myprint.o #生成静态库 ar是gnu归档工具,rc表示(replace and create) mymath.o:mymath.c gcc -c mymath.c -o mymath.o myprint.o:myprint.c gcc -c myprint.c -o myprint.o .PHONY:hello hello: mkdir -p hello/lib mkdir -p hello/include cp -rf *.h hello/include cp -rf *.a hello/lib .PHONY:clean clean: rm -rf *.o libhello.a hello
我们直接执行make libhello.a
命令
[kingxzq@localhost Documents]$ make libhello.a gcc -c mymath.c -o mymath.o gcc -c myprint.c -o myprint.o ar -rc libhello.a mymath.o myprint.o
生成libhello.a
链接先执行生成.o
文件,查看静态库中的目录列表
[kingxzq@localhost Documents]$ ar -tv libhello.a rw-rw-r-- 1000/1000 1280 Aug 24 16:47 2023 mymath.o rw-rw-r-- 1000/1000 1576 Aug 24 16:47 2023 myprint.o
t
:列出静态库中的文件
v
:verbose 详细信息
通过静态库编译程序
[kingxzq@localhost Documents]$ gcc main.c -I . -L . -lhello [kingxzq@localhost Documents]$ ./a.out hello world[1692867452] res: 5050
-I
头文件搜索路径
-L
指定库路径
-l
指定库名
测试目标文件生成后,静态库删掉,程序照样可以运行
库搜索路径
从左到右搜索
-L
指定的目录。由环境变量指定的目录 (
LIBRARY_PATH
)由系统指定的目录
/usr/lib
/usr/local/lib
生成动态库
首先同样编写Makefile
文件
.PHONY:all all:libhello.so libhello.a libhello.so:mymath_d.o myprint_d.o gcc -shared mymath_d.o myprint_d.o -o libhello.so #shared: 表示生成共享库格式 mymath_d.o:mymath.c gcc -c -fPIC mymath.c -o mymath_d.o #fPIC:产生位置无关码(position independent code) myprint_d.o:myprint.c gcc -c -fPIC myprint.c -o myprint_d.o libhello.a: mymath.o myprint.o ar -rc libhello.a mymath.o myprint.o mymath.o:mymath.c gcc -c mymath.c -o mymath.o myprint.o:myprint.c gcc -c myprint.c -o myprint.o .PHONY:hello hello: mkdir -p hello/lib mkdir -p hello/include cp -rf *.h hello/include cp -rf *.a hello/lib cp -rf *.so hello/lib .PHONY:clean clean: rm -rf *.o *.a *.so hello
我们直接执行make libhello.so
命令
[kingxzq@localhost Documents]$ make libhello.so gcc -c -fPIC mymath.c -o mymath_d.o #fPIC:产生位置无关码(position independent code) gcc -c -fPIC myprint.c -o myprint_d.o gcc -shared mymath_d.o myprint_d.o -o libhello.so #shared: 表示生成共享库格式
再执行make hello
[kingxzq@localhost Documents]$ make hello mkdir -p hello/lib mkdir -p hello/include cp -rf *.h hello/include cp -rf *.a hello/lib cp -rf *.so hello/lib
我们看看hello
所包含文件
[kingxzq@localhost Documents]$ tree hello hello ├── include │ ├── mymath.h │ └── myprint.h └── lib ├── libhello.a └── libhello.so 2 directories, 4 files
使用动态库
如果这里的文件只有静态库文件我们可以直接输入命令
[kingxzq@localhost Documents]$ gcc main.c -I hello/include -L hello/lib -lhello
但是这里有动态库文件则不能这样,因为默认编译链接时,链接的是动态库(这种情况若要使用静态库,可在最后加上-static
),而这里我们自建的动态库如果直接链接后运行,则会失败,如下(我们要把之前的.c和.h文件都删除,否则当然能成功)
./a.out: error while loading shared libraries: libhello.so: cannot open shared object file: No such file or directory
查看链接
[kingxzq@localhost Documents]$ ldd a.out linux-vdso.so.1 => (0x00007ffe61123000) libhello.so => not found libc.so.6 => /lib64/libc.so.6 (0x00007ff0ea658000) /lib64/ld-linux-x86-64.so.2 (0x00007ff0eaa26000)
那么我们该如何重新生成呢?
- 第一种方法
将你的.so
动态库文件拷贝到/lib64
路径下(不推荐)
- 第二种方法
添加环境变量路径LD_LIBRARY_PATH
输入命令
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:动态库绝对路径
比如我这里
kingxzq@localhost Documents]$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/kingxzq/hello/lib
查看环境变量
[kingxzq@localhost Documents]$ echo $LD_LIBRARY_PATH :/home/kingxzq/.VimForCpp/vim/bundle/YCM.so/el7.x86_64:/home/kingxzq/hello/lib
这时就可以正常链接到动态库了
[kingxzq@localhost Documents]$ ./a.out hello world[1692880468] res: 5050 [kingxzq@localhost Documents]$ ldd a.out linux-vdso.so.1 => (0x00007ffd04e9f000) libhello.so => /home/kingxzq/hello/lib/libhello.so (0x00007fa8cce23000) libc.so.6 => /lib64/libc.so.6 (0x00007fa8cca55000) /lib64/ld-linux-x86-64.so.2 (0x00007fa8cd025000)
但是这种方法同样存在缺陷,在你下一次重新连接用户时,又会链接失败了,因为此环境变量属于内存级的环境变量
- 第三种方法
在/etc/ld.so.conf.d/
路径添加配置文件,ldconfig
更新
我们在该路径下随便建立一个.conf
文件,将动态库路径拷贝到这个.conf
文件中,保存关闭后,输入ldconfig
更新配置文件(注意权限问题)
[kingxzq@localhost Documents]$ cd /etc/ld.so.conf.d/ [kingxzq@localhost ld.so.conf.d]$ sudo vim hello.conf [sudo] password for kingxzq: [kingxzq@localhost ld.so.conf.d]$ cd /home/kingxzq/Documents [kingxzq@localhost Documents]$ sudo ldconfig [kingxzq@localhost Documents]$ ./a.out hello world[1692882239] res: 5050 [kingxzq@localhost Documents]$ ldd a.out linux-vdso.so.1 => (0x00007ffe1f7f9000) libhello.so => /home/kingxzq/hello/lib/libhello.so (0x00007f88ce116000) libc.so.6 => /lib64/libc.so.6 (0x00007f88cdd48000) /lib64/ld-linux-x86-64.so.2 (0x00007f88ce318000)
这种方法是一劳永逸的
- 第四种方法
建立软链接
[kingxzq@localhost Documents]$ sudo ln -s ~/Documents/hello/lib/libhello.so /lib64/libhello.so
查看软链接建立成功执行程序
[kingxzq@localhost Documents]$ ll /lib64/libhello.so lrwxrwxrwx. 1 root root 45 Aug 24 21:39 /lib64/libhello.so -> /home/kingxzq/Documents/hello/lib/libhello.so [kingxzq@localhost Documents]$ ./a.out hello world[1692884449] res: 5050
使用外部库
在 Linux 中,要使用外部数学库(例如数学函数库),你需要通过编译器的链接选项来指定链接到这些库。常见的数学库是 libm
,它包含了数学函数如 sin、cos、sqrt 等。
当你在编译源代码时需要使用数学库时,可以使用 -lm
选项来告诉编译器链接到数学库。以下是使用外部数学库的示例:
gcc -o my_program my_program.c -lm
其中:
-o my_program
:指定输出的可执行文件名为my_program
。my_program.c
:源代码文件名。-lm
:告诉编译器链接到数学库(libm
)。
通过这个编译命令,编译器会自动查找并链接到数学库,使得你的程序可以使用数学函数。
配置文件,ldconfig
更新
我们在该路径下随便建立一个.conf
文件,将动态库路径拷贝到这个.conf
文件中,保存关闭后,输入ldconfig
更新配置文件(注意权限问题)
[kingxzq@localhost Documents]$ cd /etc/ld.so.conf.d/ [kingxzq@localhost ld.so.conf.d]$ sudo vim hello.conf [sudo] password for kingxzq: [kingxzq@localhost ld.so.conf.d]$ cd /home/kingxzq/Documents [kingxzq@localhost Documents]$ sudo ldconfig [kingxzq@localhost Documents]$ ./a.out hello world[1692882239] res: 5050 [kingxzq@localhost Documents]$ ldd a.out linux-vdso.so.1 => (0x00007ffe1f7f9000) libhello.so => /home/kingxzq/hello/lib/libhello.so (0x00007f88ce116000) libc.so.6 => /lib64/libc.so.6 (0x00007f88cdd48000) /lib64/ld-linux-x86-64.so.2 (0x00007f88ce318000)
这种方法是一劳永逸的
- 第四种方法
建立软链接
[kingxzq@localhost Documents]$ sudo ln -s ~/Documents/hello/lib/libhello.so /lib64/libhello.so
查看软链接建立成功执行程序
[kingxzq@localhost Documents]$ ll /lib64/libhello.so lrwxrwxrwx. 1 root root 45 Aug 24 21:39 /lib64/libhello.so -> /home/kingxzq/Documents/hello/lib/libhello.so [kingxzq@localhost Documents]$ ./a.out hello world[1692884449] res: 5050
使用外部库
在 Linux 中,要使用外部数学库(例如数学函数库),你需要通过编译器的链接选项来指定链接到这些库。常见的数学库是 libm
,它包含了数学函数如 sin、cos、sqrt 等。
当你在编译源代码时需要使用数学库时,可以使用 -lm
选项来告诉编译器链接到数学库。以下是使用外部数学库的示例:
gcc -o my_program my_program.c -lm
其中:
-o my_program
:指定输出的可执行文件名为my_program
。my_program.c
:源代码文件名。-lm
:告诉编译器链接到数学库(libm
)。
通过这个编译命令,编译器会自动查找并链接到数学库,使得你的程序可以使用数学函数。
需要注意的是,-lm
应该放在源代码文件名的后面,以便编译器在链接时正确地解析数学库的符号。如果你还需要链接到其他库,可以在同一命令中使用多个 -l
选项,如 -lm -l其他库名
。