Linux之基础IO文件系统讲解(下)

简介: 因为IO相关函数与系统调用接口对应,并且库函数封装系统调用,所以本质上,访问文件都是通过fd访问的。所以C库当中的FILE结构体内部,必定封装了fd

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

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

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

printf fwrite 库函数会自带缓冲区(进度条例子就可以说明),当发生重定向到普通文件时,数据的缓冲方式由行缓冲变成了全缓冲。

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

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

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

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

综上printf fwrite 库函数会自带缓冲区,而 write 系统调用没有带缓冲区。另外,我们这里所说的缓冲区,都是用户级缓冲区。其实为了提升整机性能,OS也会提供相关内核级缓冲区,不过不再我们讨论范围之内。那这个缓冲区谁提供呢? printffwrite 是库函数, write 是系统调用,库函数在系统调用的“上层”, 是对系统调用的“封装”,但是 write 没有缓冲区,而 printffwrite 有,足以说明,该缓冲区是二次加上的,又因为是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 列信息,每列代表了不同的属性。以下是每列信息所代表的含义:

  1. -rw-rw-r--.: 文件权限和类型。这列显示了文件的权限和类型。在这个例子中,- 表示这是一个普通文件。后面的 rw-rw-r--. 表示文件的权限,分为三组(所有者、群组、其他用户),每组的权限有读取(r)、写入(w)和执行(x)。
  2. 1: 硬链接计数。这列显示了文件的硬链接计数,即有多少个目录项指向这个文件。在这里,值为 1 表示只有一个目录项指向这个文件。
  3. kingxzq: 所有者。这列显示了文件的所有者用户名。
  4. kingxzq: 所属群组。这列显示了文件所属的用户组。
  5. 172: 文件大小。这列显示了文件的大小,以字节为单位。
  6. Aug 9 08:30: 最后修改时间。这列显示了文件的最后修改时间,格式为月份(Aug)、日期(9)和时间(08:30)。
  7. 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大小为102420484096字节。而上图中启动块(Boot Block)的大小是确定的,

Block Groupext2文件系统会根据分区的大小划分为数个Block Group。而每个Block Group都有着相同的结构组成。政府管理各区的例子

超级块Super Block):存放文件系统本身的结构信息。记录的信息主要有:bolckinode的总量,未使用的blockinode的数量,一个blockinode的大小,最近一次挂载的时间,最近一次写入数据的时间,最近一次检验磁盘的时间等其他文件系统的相关信息。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个操作

  1. 存储属性

内核先找到一个空闲的i节点(这里是263466)。内核把文件信息记录到其中。

  1. 存储数据

该文件需要存储在三个磁盘块,内核找到了三个空闲块:300,500800。将内核缓冲区的第一块数据复制到300,下一块复制到500,以此类推。

  1. 记录分配情况

文件内容按顺序300,500,800存放。内核在inode上的磁盘分布区记录了上述块列表。

  1. 添加文件名到目录

新的文件名abclinux如何在当前的目录中记录这个文件?内核将入口(263466abc)添加到目录文件。文件名和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.txthardlink.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.txthardlink.txt 都会反映出内容的更改,因为它们实际上是同一个文件的两个名称。
  • 软链接:symlink.txt 也会反映出内容的更改,因为它指向了 original.txt 的路径,而不是实际的数据块。

删除original.txt 文件,然后观察:

  • 硬链接:即使删除了 original.txthardlink.txt 仍然存在,因为硬链接与原始文件共享相同的数据块。
  • 软链接:删除了 original.txt 后,symlink.txt 将变为无效,因为它指向的目标不存在。

这个例子说明了硬链接和软链接的不同行为和特性。

总结

  • 硬链接是在文件系统中创建的原始文件的副本,它们共享相同的 inode 和数据块。
  • 软链接是一个指向目标文件的特殊文件,它包含了目标文件的路径信息。

在选择使用硬链接还是软链接时,需要根据具体情况考虑不同的需求和限制。

ACM时间

在 Linux 系统中,通常有三种主要的时间戳,如下所示:

  1. Access Time (atime): 这是文件的最后访问时间。当文件被读取时,系统会更新此时间戳。默认情况下,在读取文件时会更新此时间,但这可能会导致频繁的磁盘写入,因此在某些情况下可以通过挂载文件系统时的选项来禁用更新。在某些应用中,禁用 atime 更新可以提高性能。
  2. Modify Time (mtime): 这是文件内容的最后修改时间。当文件内容被修改时,系统会更新此时间戳。这是指文件的实际数据内容上发生了变化的时间。
  3. 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)

那么我们该如何重新生成呢?

  1. 第一种方法

将你的.so动态库文件拷贝到/lib64路径下(不推荐)

  1. 第二种方法

添加环境变量路径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)

但是这种方法同样存在缺陷,在你下一次重新连接用户时,又会链接失败了,因为此环境变量属于内存级的环境变量

  1. 第三种方法

/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)

这种方法是一劳永逸的

  1. 第四种方法

建立软链接

[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)

这种方法是一劳永逸的

  1. 第四种方法

建立软链接

[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其他库名


相关文章
|
2月前
|
网络协议 安全 Linux
Linux C/C++之IO多路复用(select)
这篇文章主要介绍了TCP的三次握手和四次挥手过程,TCP与UDP的区别,以及如何使用select函数实现IO多路复用,包括服务器监听多个客户端连接和简单聊天室场景的应用示例。
99 0
|
2月前
|
存储 Linux C语言
Linux C/C++之IO多路复用(aio)
这篇文章介绍了Linux中IO多路复用技术epoll和异步IO技术aio的区别、执行过程、编程模型以及具体的编程实现方式。
115 1
Linux C/C++之IO多路复用(aio)
|
22天前
|
安全 Linux 数据安全/隐私保护
深入Linux操作系统:文件系统和权限管理
在数字世界的海洋中,操作系统是连接用户与硬件的桥梁,而Linux作为其中的佼佼者,其文件系统和权限管理则是这座桥梁上不可或缺的结构。本文将带你探索Linux的文件系统结构,理解文件权限的重要性,并通过实际案例揭示如何有效地管理和控制这些权限。我们将一起航行在Linux的命令行海洋中,解锁文件系统的奥秘,并学习如何保护你的数据免受不必要的访问。
|
4月前
|
缓存 安全 Linux
Linux 五种IO模型
Linux 五种IO模型
|
1月前
|
存储 运维 监控
深入Linux基础:文件系统与进程管理详解
深入Linux基础:文件系统与进程管理详解
86 8
|
1月前
|
存储 Linux 文件存储
Linux文件系统
Linux文件系统 一切皆文件 在Linux中,“一切皆文件”的概念意味着系统中的所有资源,包括硬件设备、目录及进程等,均被视为文件。这种设计简化了操作和管理,具体包括: 普通文件:存储数据的常规文件。 目录文件:包含其他文件和子目录的文件。 进程文件:在/proc目录下代表系统中运行的进程。 设备文件:位于/dev目录,代表硬件设备。 网络字节流套接字文件:用于网络通信的数据流。 链接文件:指向另一个文件的符号链接或硬链接。 管道文件:用于进程间通信的文件。
54 7
|
2月前
|
Linux C++
Linux C/C++之IO多路复用(poll,epoll)
这篇文章详细介绍了Linux下C/C++编程中IO多路复用的两种机制:poll和epoll,包括它们的比较、编程模型、函数原型以及如何使用这些机制实现服务器端和客户端之间的多个连接。
39 0
Linux C/C++之IO多路复用(poll,epoll)
|
2月前
|
存储 Java API
【文件IO】文件系统操作
【文件IO】文件系统操作
53 1
|
4月前
|
编解码 Linux 程序员
深度探索Linux操作系统 —— 构建根文件系统2
深度探索Linux操作系统 —— 构建根文件系统
53 12
|
3月前
|
存储 Linux 索引
Linux 下最主流的文件系统格式——ext
【9月更文挑战第8天】硬盘被划分为若干相同大小的块(Block),默认大小为4K,便于灵活管理文件数据。文件数据分散存放于这些块中,提高了数据添加、删除和插入的便利性。

热门文章

最新文章