UC成长之路3

回顾:
UC成长之路1
UC成长之路2

UC成长之路3

一、动态库的制作和使用

动态库的命名:lib库名.so

  1. 将要加入库文件的源码文件编译成与位置无关的目标文件,gcc -c -fPIC *.c
pmath$ls
add.c  add.o  mul.c  mul.o  pmath.h
pmath$rm *.o
pmath$ls
add.c  mul.c  pmath.h
pmath$gcc -c -fPIC *.c
pmath$ls
add.c  add.o  mul.c  mul.o  pmath.h
  1. 将这些目标文件打包成动态库文件,gcc -shared -o libpmath.so *.o
pmath$gcc -shared -o libpmath.so *.o
pmath$ls
add.c  add.o  libpmath.so  mul.c  mul.o  pmath.h

  1. 使用动态库链接生成可执行文件
dongtai$gcc main.o -Lpmath -lpmath
dongtai$ls
a.out  main.c  main.o  pmath

error:

dongtai$./a.out 
./a.out: error while loading shared libraries: libpmath.so: cannot open shared object file: No such file or directory

动态链接:代码加载到内存执行的时候才发生的链接
链接器:就是执行gcc main.o -Lpmath -lpmath命令
加载器:就是执行./a.out可执行文件时夹在程序
发生错误的原因: -L -l 告诉链接器动态库的位置,但加载器不知道动态库的位置
使用命令:ldd a.out查看加载动态库情况,就是查看程序需要的动态库

dongtai$ldd a.out
	linux-vdso.so.1 =>  (0x00007ffcdffb4000)
	libpmath.so => not found
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f4fc4ea1000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f4fc526b000)

解决方案:告诉加载器动态库的位置,两种方法:

  • 使用这个环境变量:LD_LIBRARY_PATH告诉加载器搜索动态库的路径
dongtai$LD_LIBRARY_PATH=$LD_LIBRARY_PATH:pmath
dongtai$export LD_LIBRARY_PATH
dongtai$./a.out 
6+2=8
hello beijing
  • 使用默认的路径,就是将动态库文件移动到加载器的默认路径下
    加载器的默认路径是: /usr/lib 或 /lib
    在这里插入图片描述
    连接器的默认路径也是:/usr/lib 或 /lib

小结: 动态链接和静态链接

  • 静态链接静态库文件的时候,将目标文件的代码合并拷贝,可执行文件比较大,可执行文件不依赖静态库文件
  • 动态链接动态库文件的时候,生成的可执行文件中,使用到的动态库文件的函数是未定义引用的,函数的地址不确定,在执行的时候才确定函数的地址,可执行文件依赖与动态库文件,可执行文件的大小比静态库链接生成的可执行文件小。

二、动态加载

代码执行的时候,自动加载
在程序执行的过程中,根据需要加载相应的动态库函数。
系统提供了函数接口:

  • dlopen(3) man 3 dlopen
DLOPEN(3)                  Linux Programmer's Manual                 DLOPEN(3)

NAME
       dlclose, dlopen, dlmopen - open and close a shared object

SYNOPSIS
       #include <dlfcn.h>

       void *dlopen(const char *filename, int flags);
       //功能:加载指定的动态库文件
       //参数:
       //filename:指定了要加载的动态库文件的名字
       //flag:1)RTLD_LAZY:懒加载 or 2)RTLD_NOW:立即加载
       //返回值:NULL错误 or 非NULL成功

       int dlclose(void *handle);
       //功能:动态库的应用计数减1
       //参数:
       //handle:dlopen(3)的返回值
       //返回值:成功0, 错误非0

       char *dlerror(void);
       //功能:获取最近的dlopen dlsym dlclose函数调用的错误信息
       //参数:void
       //返回值:一个字符串,描述了函数调用的错误信息
       
       void *dlsym(void *handle, const char *symbol);
       //功能:在动态库里找symbol指定的函数,将函数的地址返回
       //参数:
       //handle:dlopen(3)的返回值,指定了动态库
       //symbol:指定符号名字,可认为是函数名
       //返回值:NULL在动态库里没用这个函数,否则返回函数在内存的地址
       
       Link with -ldl.
       //gcc -ldl:dl是一个动态库名字

引用计数:使用共享库(动态库)进程的计数

举例说明:动态加载动态库文件libpmath.so,这个动态库的位置是:/lib, 代码参见:dynamic.c

  • :dynamic.c
#include <stdio.h>
#include <dlfcn.h>

int main(void)
{
    //加載動態庫文件libpmath.so /lib
    void *p = dlopen("libpmath.so", RTLD_NOW);
    if(p==NULL){
        printf("dlopen fails...\n");
        return -1; 
    }   
    printf("dlopen success...\n");
    //關閉動態庫的加載
    dlclose(p);
    return 0;
}

在这里插入图片描述

#include <stdio.h>
#include <dlfcn.h>

int main(void)
{
    //f_t是變量,指針類型的
    int (*f_t)(int, int);
    //加載動態庫文件libpmath.so /lib
    void *p = dlopen("libpmath.so", RTLD_NOW);
    if(p==NULL){
        printf("%s\n", dlerror());
        return -1; 
    }   
    printf("dlopen success...\n");
    //在動態庫找函數,並且返回函數的地址
    void *f=dlsym(p,"p_add");
    if(f==NULL){
        printf("%s\n",dlerror());
        return -1; 
    }   
    f_t = f;
    //f變量空間裏是一個地址,是p_add函數的入口地址
    printf("3+2=%d\n", f_t(3,2));
    //關閉動態庫的加載
    dlclose(p);
    return 0;
}

在这里插入图片描述

上面的程序也可写成下面的形式

#include <stdio.h>
#include <dlfcn.h>

int main(void)
{
	//使用typedef
    typedef int (*f_t)(int, int);
    void* handle = dlopen("libpmath.so", RTLD_NOW);
    if(NULL == handle){
        printf("%s\n", dlerror());
        //printf("dlopen fail\n");
        return -1; 
    }   
    printf("dlopen success...\n");
    void* f = dlsym(handle, "add");
    if(NULL == f){ 
        printf("%s\n", dlerror());
        dlclose(handle);
        return -1; 
    }   
    
    printf("2+3=%d\n", ((f_t)f)(2, 3));
    dlclose(handle);
    return 0;
}

三、程序中的错误处理

举例说明:代码参见file.c

#include <stdio.h>

//從命令行中傳入參數
int main(int argc, char *argv[])
{
    FILE *fp;
    //打開文件,文件的名字從命令行的第一個參數傳入
    fp=fopen(argv[1], "r");
    if(fp==NULL){
        printf("fopen fails...\n");
        return -1; 
    }   
    printf("open file success...\n");
    //關閉文件
    fclose(fp);
    return 0;
}

在这里插入图片描述
系统中有一个全局变量errno。记录了最近函数调用产生的错误编号
extern int errno

errnodescription
numberstring
要使用errno需要包含头文件#include <errno.h>
#include <stdio.h>
#include <errno.h>

//從命令行中傳入參數
int main(int argc, char *argv[])
{
    FILE *fp;
    //打開文件,文件的名字從命令行的第一個參數傳入
    fp=fopen(argv[1], "r");
    if(fp==NULL){
        printf("fopen fails...D=%d\n", errno);
        return -1; 
    }   
    printf("open file success...\n");
    //關閉文件
    fclose(fp);
    return 0;
}

在这里插入图片描述

只是获得errno的值,想要获取相应的描述信息。如何获取?

  • perror(3)
#include <stdio.h>
void perror(const char *s);
//功能:输出一条系统错误的消息
//参数:
//s:字符串的首地址
//返回值:

eg:

#include <stdio.h>
#include <errno.h>

//從命令行中傳入參數
int main(int argc, char *argv[])
{
    FILE *fp;
    //打開文件,文件的名字從命令行的第一個參數傳入
    fp=fopen(argv[1], "r");
    if(fp==NULL){
        //printf("fopen fails...D=%d\n", errno);
        perror("fopen");
        return -1; 
    }   
    printf("open file success...\n");
    //關閉文件
    fclose(fp);
    return 0;
}

在这里插入图片描述

  • strerror(3)
#include <string.h>
char *strerror(int errnum);
//功能:获取错误编号的错误描述信息
//参数:
//errnum:指定了错误的编号
//返回值:如果找到这个编号对应的错误描述信息,获取信息;如果错误编号没有对应的错误描述信息返回“Unknow error nnn”

eg:

#include <stdio.h>
#include <errno.h>
#include <string.h>
//從命令行中傳入參數
int main(int argc, char *argv[])
{
    FILE *fp;
    //打開文件,文件的名字從命令行的第一個參數傳入
    fp=fopen(argv[1], "r");
    if(fp==NULL){
        //printf("fopen fails...D=%d\n", errno);
        //perror("fopen");
        printf("%s\n", strerror(errno));
        return -1; 
    }   
    printf("open file success...\n");
    //關閉文件
    fclose(fp);
    return 0;
}

在这里插入图片描述

附件:

我的目录结构

  • dongtai
    • main.c
    • pmath
      • add.c
      • mul.c
      • pmath.h

main.c

#include <pmath.h>
#include <stdio.h>
//框架
int main(void)
{
    int a=6, b=2;
    printf("%d+%d=%d\n", a, b, p_add(a,b));
    //組件
    printf("hello beijing\n");
    return 0;
}

add.c

#include "pmath.h"

int p_add(int a, int b)
{
    return a+b;
}

int p_sub(int a, int b)
{
    return a-b;
}

mul.c

#include "pmath.h"

int p_mul(int a, int b)
{
    return a*b;
}

int p_div(int a, int b)
{
    return a/b;
}

pmath.h

#ifndef PMATH_H_
#define PMATH_H_
/*函數的定義*/
int p_add(int, int);
int p_sub(int, int);
int p_mul(int, int);
int p_div(int, int);

#endif

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值