【Linux】基础IO,软硬链接,动静态库

1. 认识IO

什么是IO

I/O简单来说对应的就是两个单词Input和Output,指的是计算机系统与外部环境(通常是硬件设备或其他计算机系统)之间的数据交换过程

I/O 可以分为两种主要类型:

  1. 输入(Input): 输入是指计算机系统接收来自外部环境的数据或信息的过程。例如,当用户在键盘上输入文本时,这些输入字符被视为输入操作。输入可以来自各种外部源,包括键盘、鼠标、传感器、网络连接、磁盘驱动器等。计算机系统需要能够接收和处理这些输入数据,以便执行相应的操作。
  2. 输出(Output): 输出是指计算机系统将处理后的数据或信息发送到外部环境的过程。例如,当计算机系统将文本显示在屏幕上、打印文档到打印机或将数据写入磁盘驱动器时,这些都是输出操作。输出使计算机系统能够与用户或其他设备进行有效的通信。

在计算机程序中,I/O 操作通常是相对较慢的操作,因为涉及到与外部设备通信或存储介质的读写,而这些操作比内存中的数据处理更为耗时。因此,在编写高效的程序时,需要注意如何最小化 I/O 操作的次数,以提高性能。此外,对于某些应用程序,如数据库管理系统和网络通信应用程序,有效的 I/O 管理对于系统的响应时间和吞吐量至关重要。

2.C文件接口的IO

写文件

c 复制代码
#include <stdio.h>
#include <string.h>
int main()
{
    FILE* fp=fopen("myfile","w");
    if(fp==NULL){
        perror("open");
        return 1;
    }

    const char* str="hello world\n";
    int cnt=5;
    while(cnt--)
        fwrite(str,strlen(str),1,fp);
    
    fclose(fp);
    return 0;
}

读文件

c 复制代码
#include <stdio.h>
#include <string.h>
int main()
{
    FILE* fp=fopen("myfile","r");
    if(fp==NULL){
        perror("open");
        return 1;
    }

    const char* msg="hello world\n";
    char* str[1024];
    while (1)
    {
        size_t s=fread(str,1,strlen(msg),fp);
        if(s>0)
        {
            str[s]=0;
            printf("%s",str);
        }
        if(feof(fp)) break;
    }
    
    fclose(fp);
    return 0;
}

3. 系统文件接口的I/O

操作文件,除了上述C接口(当然其他语言也有),我们还可以采用系统接口来进行文件访问

接口介绍

c 复制代码
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
pathname: 要打开或创建的目标文件
flags: 打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行"或"运算,构成flags。
参数:
    O_RDONLY: 只读打开
    O_WRONLY: 只写打开
    O_RDWR : 读,写打开
    这三个常量,必须指定一个且只能指定一个
    O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限
    O_APPEND: 追加写
返回值:
    成功:新打开的文件描述符
    失败:-1

mode_t理解:直接看man 手册,比什么都清楚。

open 函数具体使用哪个,和具体应用场景相关,如目标文件不存在,需要open创建,则第三个参数表示创建文件的默认权限,否则,使用两个参数的open。

写文件

c 复制代码
#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("myfile",O_WRONLY);
    if(fd<0){
        perror("open");
    }

    const char* msg="hello world!!\n";
    int cnt=5;
    while (cnt--){
        //fd: 后面讲, msg:缓冲区首地址, len: 本次读取,期望写入多少个字节的数据。 返回值:实际写了多少字节数据
        write(fd,msg,strlen(msg));
    }
    
    close(fd);
    return 0;
}

读文件

c 复制代码
#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("myfile",O_RDONLY);
    if(fd<0){
        perror("open");
    }

    const char* msg="hello world!!\n";
    char* str[1024];
    while (read(fd,str,strlen(msg)))
    {
        printf("%s",str);
    }
    
    close(fd);
    return 0;
}

open函数返回值

在认识返回值之前,先来认识一下两个概念: 系统调用和 库函数

上面的 fopen fclose fread fwrite 都是C标准库当中的函数,我们称之为库函数(libc)。

而, open close read write lseek 都属于系统提供的接口,称之为系统调用接口

所以,可以认为,f#系列的函数,都是对系统调用的封装,方便二次开发。

文件描述符fd

这个文件描述符fd究竟是个什么数字呢?

我们可以试着把它打印出来看看

c 复制代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
    int fd=open("myfile",O_RDONLY);
    if(fd<0){
        perror("open");
    }
    printf("%d\n",fd);
    close(fd);
    return 0;
}
//输出结果
//3

文件描述符就是一个小整数,但它为什么是3呢?学过C语言的应该都知道

C默认会打开三个输入输出流,分别是stdin, stdout, stderr仔细观察发现,这三个流的类型都是FILE*, fopen返回值类型,文件指针,其实这三个流就对应数字0,1,2,进而可以得出,fd本质上就是数组的下标,只不过这个数组是系统帮我们维护的

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

Linux进程默认情况下会有3个缺省打开的文件描述符,分别是标准输入0, 标准输出1, 标准错误2

0,1,2对应的物理设备一般是:键盘,显示器,显示器、

所以输入输出还可以采用如下方式:

c 复制代码
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
    char buffer[1024];
    ssize_t s=read(0,buffer,sizeof buffer);
    if(s>0){
        buffer[s]=0;
        write(1,buffer,strlen(buffer));
        write(2,buffer,strlen(buffer));
    }
    return 0;
}
//运行结果
//输入:hello world!!!
//输出:
//hello world!!!
//hello world!!!

文件描述符的分配规则

上面的例子中,我们新打开一个文件输出后发现是 fd: 3

现在关闭文件描述符0或者2再来看看

c 复制代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
    close(0);
    //close(2);
    int fd = open("myfile", O_RDONLY);
    if(fd < 0){
        perror("open");
        return 1;
    }
    printf("fd: %d\n", fd);
    close(fd);
    return 0;
}

发现是结果是: fd: 0 或者 fd 2 可见,文件描述符的分配规则:在files_struct数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。

那如果我们关闭标准输出1呢?

c 复制代码
#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);
    close(fd);
    return 0;
}

此时,我们发现,本来应该输出到显示器上的内容,输出到了文件 myfile 当中,其中,fd=1。这种现象叫做输出重定向。常见的重定向有:>, >>, <

shell 复制代码
//比如我想让myfile文件写入ls -l 打印的内容
[hdm@centos7 BasicIO]$ ls -l > myfile 
[hdm@centos7 BasicIO]$ cat myfile 
total 28
-rw-r--r-- 1 hdm wheel    65 Sep  9 15:56 makefile
-rw-r--r-- 1 hdm wheel     0 Sep 10 12:40 myfile
-rwxr-xr-x 1 hdm wheel 16576 Sep 10 12:33 test
-rw-r--r-- 1 hdm wheel   652 Sep 10 12:33 test.c

重定向

重定向的本质是什么呢?

不过我们要实现重定向,并不会像上面的列子一样,先把一个文件描述符关闭再打开,系统给了我们一套接口

dup2 系统调用

函数原型如下:

c 复制代码
#include <unistd.h>
int dup2(int oldfd, int newfd);
c 复制代码
#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("myfile",O_WRONLY | O_CREAT,0644);
    if(fd<0){
        perror("open");
    }
    dup2(fd,1);
    printf("fd: %d\n", fd);
    fflush(stdout);
    const char* str="hello\n";
    const char* str1="nihao\n";

    fprintf(stdout,"%s",str1);
    write(1,str,strlen(str));
    write(fd,str,strlen(str));

    return 0;
}

printf是C库当中的IO函数,一般往 stdout 中输出,但是stdout底层访问文件的时候,找的还是fd:1, 但此时,fd:1下标所表示内容,已经变成了myfile的地址,不再是显示器文件的地址,所以,输出的任何消息都会往文件中写入,进而完成输出重定向。

FILE

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

c 复制代码
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
    const char* str1="hello printf\n";
    const char* str2="hello fprintf\n";
    const char* str3="hello write\n";

    printf("%s",str1);
    fprintf(stdout,"%s",str2);
    write(1,str3,strlen(str3));

    fork();
    return 0;
}
//运行出结果:
//hello printf
//hello fprintf
//hello write

但如果对进程实现输出重定向呢? ./test > myfile , 我们发现结果变成了:

shell 复制代码
[hdm@centos7 BasicIO]$ ./test  >myfile 
[hdm@centos7 BasicIO]$ cat myfile 
hello write
hello printf
hello fprintf
hello printf
hello fprintf

我们发现 printf 和 fwrite (库函数)都输出了2次,而 write 只输出了一次(系统调用)。

为什么呢?肯定和fork有关!

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

  • printf fwrite 库函数会自带缓冲区,当发生重定向到普通文件时,数据的缓冲方式由行缓冲变成了全缓冲。

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

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

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

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

综上: printf fwrite 库函数会自带缓冲区,而 write 系统调用没有带缓冲区。另外,我们这里所说的缓冲区,都是用户级缓冲区。其实为了提升整机性能,OS也会提供相关内核级缓冲区,不过不再我们讨论范围内。

那这个缓冲区谁提供呢? printf fwrite 是库函数, write 是系统调用,库函数在系统调用的"上层", 是对系统调用的"封装",但是 write 没有缓冲区,而 printf fwrite 有,足以说明,该缓冲区是二次加上的,又因为是C,所以由C标准库提供。

4. 理解文件系统

我们使用ls -l的时候看到的除了看到文件名,还看到了文件的其他数据。

shell 复制代码
[hdm@centos7 BasicIO]$ ls -l
total 12
-rw-r--r-- 1 hdm wheel   65 Sep 10 12:57 makefile
-rw-r--r-- 1 hdm wheel   66 Sep 10 15:50 myfile
-rw-r--r-- 1 hdm wheel 1101 Sep 10 15:48 test.c

每行包含7列:

  • 模式

  • 硬链接数

  • 文件所有者

  • 大小

  • 最后修改时间

  • 文件名

其实这个信息除了通过这种方式来读取,还有一个stat命令能够看到更多信息

shell 复制代码
[hdm@centos7 BasicIO]$ stat test.c 
 File: 'test.c'
 Size: 1101            Blocks: 8          IO Block: 4096   regular file
Device: fd01h/64769d    Inode: 1582421     Links: 1
Access: (0644/-rw-r--r--)  Uid: ( 1000/     hdm)   Gid: (   10/   wheel)
Access: 2023-09-10 15:48:26.249722588 +0800
Modify: 2023-09-10 15:48:26.086715074 +0800
Change: 2023-09-10 15:48:26.086715074 +0800
Birth: -

上面的执行结果有几个信息需要解释清楚

inode

为了能解释清楚inode我们先简单了解一下文件系统

Linux ext2文件系统,上图为磁盘文件系统图(内核内存映像肯定有所不同),磁盘是典型的块设备,硬盘分区被划分为一个个的block。一个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一个新文件来看看如何工作

shell 复制代码
[hdm@centos7 BasicIO]$ touch myfile
[hdm@centos7 BasicIO]$ ls -i myfile 
1574424 myfile

为了说明问题,我们将上图简化

创建一个新文件主要有一下4个操作:

  1. 存储属性

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

  2. 存储数据

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

  3. 记录分配情况

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

  4. 添加文件名到目录

新的文件名myfile。linux如何在当前的目录中记录这个文件?内核将入口(1574424,myfile)添加到目录文件。文件名和inode之间的对应关系将文件名和文件的内容及属性连接起来。

理解软硬链接

我们看到,真正找到磁盘上文件的并不是文件名,而是inode。 其实在linux中可以让多个文件名对应于同一个inode。

shell 复制代码
//ln 属于硬链接的文件 硬链接文件的名字
ln file file.link
shell 复制代码
[hdm@centos7 BasicIO]$ touch myfile
[hdm@centos7 BasicIO]$ ls -li 
1574424 -rw-r--r-- 1 hdm wheel   66 Sep 11 13:27 myfile
hdm@centos7 BasicIO]$ ln myfile myfile.link 
[hdm@centos7 BasicIO]$ ls -li 
1574424 -rw-r--r-- 2 hdm wheel   66 Sep 11 13:27 myfile
1574424 -rw-r--r-- 2 hdm wheel   66 Sep 11 13:27 myfile.link
[hdm@centos7 BasicIO]$ unlink myfile.link 
[hdm@centos7 BasicIO]$ ls -li
1574424 -rw-r--r-- 1 hdm wheel   66 Sep 11 13:27 myfile
[hdm@centos7 BasicIO]$ rm myfile 
[hdm@centos7 BasicIO]$ ls -li

myfile和myfile.link的链接状态完全相同,他们被称为指向文件的硬链接。内核记录了这个连接数,inode

1574424的硬连接数为2。

我们在删除文件时干了两件事情:

1.在目录中将对应的记录删除,

2.将硬连接数-1,如果为0,则将对应的磁盘释放。

为什么创建目录之后,它的当前目录硬链接数会加一?

因为在当前目录比如test下创建一个目录比如a,那么进入这个目录a之后,里面会存在两个隐藏目录, 一个. ..对应的含义分别是当前目录和上级,而这个 ..就是对上级目录test的硬链接。而且这样对目录的硬链接我们普通用户自己不能进行操作,只能由操作系统分配

为什么我们自己不能对目录进行硬链接?

  1. 循环链接问题: 允许目录硬链接可能导致循环链接的问题。当一个目录被硬连接到另一个目录时,可以创建一个链接链,其中一个目录包含硬连接到另一个目录,而另一个目录包含硬连接到第一个目录,从而形成一个无限循环的链接链。这种循环链接对文件系统的稳定性和可用性造成了严重威胁,因为它会导致文件系统陷入无限循环的操作中。
  2. 文件系统的一致性和安全性: 目录是用于组织文件和子目录的结构,包括文件和子目录的层次结构和元数据(如权限、所有者等)。如果允许硬链接目录,可能会导致混淆和权限问题。例如,不同的用户或进程可以在不同位置创建硬链接,这可能导致混淆和权限冲突。目录硬链接可能会对文件系统的一致性和安全性造成严重影响。
  3. 性能问题: 如果目录可以硬连接,那么在处理目录结构时,文件系统必须处理硬链接的情况,这可能导致性能问题和更复杂的目录操作实现。

软链接

硬链接是通过inode引用另外一个文件,软链接是通过名字引用另外一个文件,在shell中的做法

软连接其实就跟windows系统上的文件快捷方式一样,只要被链接那个文件删除了,那么软连接也将失效

shell 复制代码
//ln 属于软链接的文件 软链接文件的名字
ln -s file file.link
shell 复制代码
[hdm@centos7 BasicIO]$ touch myfile
[hdm@centos7 BasicIO]$ ls -li
1574424 -rw-r--r-- 1 hdm wheel    0 Sep 11 13:34 myfile
[hdm@centos7 BasicIO]$ ln -s myfile myfile.link.s
[hdm@centos7 BasicIO]$ ls -li
1574424 -rw-r--r-- 1 hdm wheel    0 Sep 11 13:34 myfile
1574967 lrwxrwxrwx 1 hdm wheel    6 Sep 11 13:34 myfile.link.s -> myfile

我们在用 stat命令进行查看文件属性的时候,会出现三行时间,那它们分别代表上面含义呢?

  • Access 最后访问时间

    访问时间: 访问时间是文件或目录上一次被访问的时间。每当文件或目录被打开、读取或执行时,访问时间都会被更新。访问时间的记录有助于了解文件或目录的访问频率,但在某些情况下,频繁更新访问时间可能会对性能产生影响。可以使用 statls -lu 命令查看文件的访问时间。

  • Modify 文件内容最后修改时间

    访问时间: 访问时间是文件或目录上一次被访问的时间。每当文件或目录被打开、读取或执行时,访问时间都会被更新。访问时间的记录有助于了解文件或目录的访问频率,但在某些情况下,频繁更新访问时间可能会对性能产生影响。可以使用 statls -lu 命令查看文件的访问时间。

  • Change 属性最后修改时间

    更改时间: 更改时间是文件或目录上一次元数据(metadata)发生更改的时间。元数据包括文件的权限、所有者、链接数等信息。当文件的元数据发生更改时(例如,文件的权限被修改),更改时间将被更新。更改时间还包括文件或目录的创建时间。可以使用 stat 命令查看文件的更改时间。

5. 动态库和静态库

动态库和静态库是在编程中常用的两种库文件类型,它们有不同的特点和用途。

  1. 静态库(Static Library):
    • 静态库是一组已编译的对象文件的集合,通常以.a(在Unix/Linux)或.lib(在Windows)为文件扩展名。
    • 当你将静态库链接到一个可执行文件中时,编译器会将库中的代码和数据复制到最终的可执行文件中。
    • 静态库使得可执行文件具有所有必需的代码,因此它们通常较大。
    • 静态库的优点是独立性,不需要依赖外部库文件,但可能导致可执行文件变得较大。
  2. 动态库(Dynamic Link Library, DLL,或Shared Library):
    • 动态库也是一组已编译的对象文件的集合,通常以.dll(在Windows)或.so(在Unix/Linux)为文件扩展名。
    • 与静态库不同,动态库的代码和数据不会被复制到可执行文件中。相反,可执行文件包含指向库的引用,库在运行时加载到内存中。
    • 动态库使得多个程序可以共享同一份库,节省了磁盘和内存空间。
    • 动态库的缺点是,如果库文件不存在或版本不匹配,程序可能无法运行。

选择使用动态库还是静态库取决于项目的需求和设计。通常情况下:

  • 使用动态库可以减小可执行文件的大小,因为多个程序可以共享同一个库。
  • 使用静态库可以确保可执行文件的独立性,不受外部库的影响,但可能导致可执行文件变得较大。

生成静态库

我们可以自己制定一个动静态库来测试,例如

c 复制代码
/add.h/
#ifndef __ADD_H__
#define __ADD_H__
int add(int a, int b);
#endif // __ADD_H__
/add.c/
#include "add.h"
int add(int a, int b){
    return a + b;
}
/sub.h/
#ifndef __SUB_H__
#define __SUB_H__
int sub(int a, int b);
#endif // __SUB_H__
/add.c/
#include "add.h"
int sub(int a, int b){
    return a - b;
}
///main.c
#include <stdio.h>
#include "add.h"
#include "sub.h"
int main(void)
{
    int a = 10;
    int b = 20;
    printf("add(%d,%d)=%d\n", a, b, add(a, b));
    a = 100;
    b = 200;
    printf("sub(%d,%d)=%d\n", a, b, sub(a, b));
}
shell 复制代码
[hdm@centos7 myfile]$ ls
add.c  add.h  main.c  sub.c  sub.h
[hdm@centos7 myfile]$ gcc -c add.c -o add.o 
[hdm@centos7 myfile]$ gcc -c sub.c -o sub.o

生成静态库
[hdm@centos7 myfile]$ ar -rc libmymath.a add.o sub.o
ar是gnu归档工具,rc表示(replace and create)

查看静态库中的目录列表
[hdm@centos7 myfile]$ ar -tv libmymath.a 
rw-r--r-- 1000/10   1240 Sep 11 15:49 2023 add.o
rw-r--r-- 1000/10   1232 Sep 11 15:50 2023 sub.o
t:列出静态库中的文件
v:verbose 详细信息

[hdm@centos7 myfile]$ gcc main.c -L. -lmymath
-L 指定库路径
-l 指定库名
测试目标文件生成后,静态库删掉,程序照样可以运行。

库搜索路径

  • 从左到右搜索-L指定的目录。

  • 由环境变量指定的目录 (LIBRARY_PATH)

  • 由系统指定的目录

    /usr/lib

    /usr/local/lib

生成动态库

shared: 表示生成共享库格式

fPIC:产生位置无关码(position independent code)

库名规则:libxxx.so

shell 复制代码
[hdm@centos7 myfile]$ ls
add.c  add.h  add.o  libmymath.a  main.c  sub.c  sub.h  sub.o
[hdm@centos7 myfile]$  gcc -fPIC -c sub.c add.c
[hdm@centos7 myfile]$ gcc -shared -o libmymath.so *.o
[hdm@centos7 myfile]$ ls -l
total 44
-rw-r--r-- 1 hdm wheel    57 Sep 11 15:42 add.c
-rw-r--r-- 1 hdm wheel     0 Sep 11 15:36 add.h
-rw-r--r-- 1 hdm wheel  1240 Sep 11 15:57 add.o
-rw-r--r-- 1 hdm wheel  2680 Sep 11 15:50 libmymath.a
-rwxr-xr-x 1 hdm wheel 15824 Sep 11 15:58 libmymath.so
-rw-r--r-- 1 hdm wheel   236 Sep 11 15:52 main.c
-rw-r--r-- 1 hdm wheel    57 Sep 11 15:43 sub.c
-rw-r--r-- 1 hdm wheel    66 Sep 11 15:42 sub.h
-rw-r--r-- 1 hdm wheel  1232 Sep 11 15:57 sub.o

使用动态库

编译选项

l:链接动态库,只要库名即可(去掉lib以及版本号)

L:链接库所在的路径.

示例: gcc main.c -lmymath -L.

运行动态库

1、拷贝.so文件到系统共享库路径下, 一般指/usr/lib(需要管理员权限)

2、更改 LD_LIBRARY_PATH

相关推荐
wabs6664 小时前
关于贪心算法的思考
算法·贪心算法
YXXY3134 小时前
线程的介绍(四)
linux
李白你好4 小时前
AI Agent 架构的自动化渗透测试工具
运维·人工智能·自动化
社交怪人4 小时前
【判断大小】信息学奥赛一本通C语言解法(题号1043)
算法
Snasph5 小时前
GNU Make 用户手册(中文版)
服务器·算法·gnu
江澎涌5 小时前
拆解与 AI 的一次对话
人工智能·算法·程序员
一勺菠萝丶5 小时前
Docker Desktop 启动后容器自动启动怎么办?如何关闭容器自启动
运维·docker·容器
sheeta19985 小时前
LeetCode 每日一题笔记 日期:2026.06.02 题目:3635. 最早完成陆地和水上游乐设施的时间 II
笔记·算法·leetcode
Lsk_Smion6 小时前
力扣实训 _ [102].层序遍历--前序--后续_递归与非递归的实现
数据结构·算法·leetcode
ylscode6 小时前
Comodo防火墙曝致命零日漏洞:单个IPv6数据包即可触发Windows蓝屏死机
运维·网络·windows·安全·安全威胁分析