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

1. 认识IO

什么是IO

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

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

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

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

2.C文件接口的IO

写文件

#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;
}

image-20230909160750952

读文件

#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;
}

image-20230909161531953

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

操作文件,除了上述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。

写文件

#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;
}

读文件

#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 都属于系统提供的接口,称之为系统调用接口

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

文件描述符fd

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

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

#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本质上就是数组的下标,只不过这个数组是系统帮我们维护的

image-20230910122140248

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

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

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()
{
    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再来看看

#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呢?

#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;
}

image-20230910123418750

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

//比如我想让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

重定向

重定向的本质是什么呢?

image-20230910125105172

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

dup2 系统调用

函数原型如下:

#include <unistd.h>
int dup2(int oldfd, int newfd);
#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;
}

image-20230910130802335

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

FILE

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

#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 , 我们发现结果变成了:

[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的时候看到的除了看到文件名,还看到了文件的其他数据。

[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列:

  • 模式

  • 硬链接数

  • 文件所有者

  • 大小

  • 最后修改时间

  • 文件名

image-20230910160312460

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

[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我们先简单了解一下文件系统

image-20230910165605740

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一个新文件来看看如何工作

[hdm@centos7 BasicIO]$ touch myfile
[hdm@centos7 BasicIO]$ ls -i myfile 
1574424 myfile

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

image-20230910170312651

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

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

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

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

  4. 添加文件名到目录

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

理解软硬链接

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

//ln 属于硬链接的文件 硬链接文件的名字
ln file file.link
[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,则将对应的磁盘释放。

image-20230911134946032

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

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

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

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

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

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

//ln 属于软链接的文件 软链接文件的名字
ln -s file file.link
[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

image-20230911133934619

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

image-20230911135415361

  • 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)为文件扩展名。
    • 与静态库不同,动态库的代码和数据不会被复制到可执行文件中。相反,可执行文件包含指向库的引用,库在运行时加载到内存中。
    • 动态库使得多个程序可以共享同一份库,节省了磁盘和内存空间。
    • 动态库的缺点是,如果库文件不存在或版本不匹配,程序可能无法运行。

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

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

生成静态库

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

/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));
}
[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

[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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小明的c++笔记本

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值