day02

0 回顾

brk/sbrk
维护一个位置。 brk/sbrk改变这个位置。
brk改变绝对位置
sbrk改变相对位置

昨天的补充:
永远记住:C的基本类型就那几种。
所有全新类型都是使用typedef重新定义的。
类型重定义的好处:
1. 维护方便
2. 便于移植(每个系统中都用同一个名,不用修改)
3. 容易理解

1 映射虚拟内存

没有任何额外维护数据的内存分配 mmap/munmap

1.1 函数说明

void *mmap(
void *start, //指定映射的虚拟地址,如果为0,则由系统指定开始位置
size_t length,//指定映射空间的大小。 pagesize的倍数
int prot, //映射的权限 PROT_NONE PROT_READ PROT_WRITE PROT_WRITE PROT_EXEC
int flags, //映射的方式
int fd, //文件描述符号
offset_t off //文件中的映射开始位置(必须是0或pagesezi的倍数)
);

关于映射的方式flags:
内存映射:又叫匿名映射,最后两个参数无效
文件映射:映射到某个文件
只有文件映射,最后两个参数才有效
MAP_ANONYMOUS:内存映射
MAP_SHAREDMAP_PRIVATE:二选一,文件映射

1.2 案例

#include <unistd.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <stdio.h>

int main()
{
int *p = mmap(
NULL,
getpagesize(),
PROT_READ|PROT_WRITE,
MAP_ANONYMOUS|MAP_SHARED,
0,
0);
*P = 20;
*(p+1) = 30;
*(p+2) = 40;
printf("%d\n", p[2]); //打印出40
munmap(p, 4096);
}

1.3 总结

选择什么样的内存管理方法?
STL
new

malloc小而多的数据
brk/sbrk同类型的大块数据,动态移动指针
mmap/munmap 控制内存的访问/使用文件映射/控制内存共享

2 编程工具与动态库

gcc
make
gdb
其他工具
动态库(共享库)

2.1 gcc

-o 输出文件名
-O-O1-O2-O3//编译优化
-g-g1-g2-g3//产生调试信息
-Wallerror//-Wall 显示所有警告-Werror 将警告当成错误提示
-w//关闭所有警告
-c//只编译不连接,产生 .o文件(目标文件)
-E//预编译
-S//汇编。 产生 .s文件(汇编文件)

编译4过程是 -E(产生.i) -c(产生.o) -S(产生.s) 自动调用连接器ld

-D//在命令行定义宏 (宏可以在代码中定义,也可以在命令行上定义)
-x//指定编译的语言类型 C, C++, .S(汇编), none(自动判定)
-std=c89 使用标准C89
-std=c99 使用标准C99

3 静态库的编译

3.1 编译过程 (*.a)

a是achieve的缩写

3.1.1 编译成目标文件

-static 可选
gcc -c -static 代码文件.c //生产可用于归档的目标代码:代码文件.0

3.1.2 归档成静态库

ar工具 (常用-r -t选项)
ar -r 静态库 被归档的文件名(上一步代码文件.o)
ar -r add.a add.o
nm工具(查看库中所蕴含的函数列表)
nm 静态库或动态库或目标文件或执行文件

3.1.3 使用静态库

gcc 代码文件 静态库
小例子:
使用静态库完成如下程序
输入一个菱形半径,打印菱形
输入整型封装成IOTool
菱形打印封装成Graphic
计划:
1) 实现输入
2)实现菱形
3)编译静态库
4)调用静态库

//iotool.c
#include <stdio.h>
int inputInt(const char *info)
{
int r; //返回值
printf("%s:", info);
scanf("%d", &r);
return r;
}
//graphic.c
#include <stdio.h>
void diamond(int r)
{
int x, y;
for(y=0; y<=2*r; y++)
{
for(x=0; x<=2*r; x++)
{
if(y == x+r || y == x-r ||y == -x+r || y == -x+3*r)
{
printf("*");
}
else
{
printf(" ");
}
}
printf("\n");

}
}

编译: gcc -c -static iotool.c

gcc -c -static graphic.c
ar -r demo1.a iotool.o graphic.o
ar -t demo1.a //相当于nm demo1.a

//main.c
main()
{
int r = inputInt("输入菱形半径:");
diamond(r);
}

编译: gcc main.c demo1.a -o main

执行:./main
把静态库作为代码的一部分来编译

总结:
1)什么是库?
函数等代码封装的二进制已经编译的归档文件
2)ar归档工具
3)采用库的方式管理代码优点:
容易组织代码
复用
保护代码版权
4)静态库的“静态”的含义:
编译好的程序运行的时候不依赖库
库作为程序的一部分编译连接
5) 静态库的本质
就是目标文件的集合(归档)
6)-static可选

3.2 库的规范与约定

库命名规则:
lib库名.a.主版本号.副版本号.批号
一般就写“lib库名.a”就行了。
ar -r libdemo2.a iotool.o graphic.o
库的使用规则
-l库名
-L库所在的目录
gcc main.c -o main -l demo2 -L.

4 动态库的编译

4.1 什么是动态库(共享库)

动态库是可以执行的,静态库不能执行
但动态库没有main,不能独立执行
动态库不会连接成程序的一部分
程序执行时,必须需要动态库文件

4.2 工具

ldd查看程序需要调用的动态库 ,ldd只能查看可执行文件(共享库文件或elf文件)
nm (查看库中的函数符号)

4.3 动态库的编译

4.3.1 编译

-c -f pic(可选) (-f 指定文件格式 pic 位置无关代码)

4.3.2 连接

-shared

编译:gcc -c -fpic iotool.c
gcc -c -fpic graphic.c
(非标准)gcc -shared -odemo3.so iotool.o graphic.o
(标准)gcc -shared -olibdemo4.so iotool.o graphic.o

4.4 使用动态库

gcc 代码文件名 动态库文件名
gcc 代码文件名 -l库名 -L动态库所在的路径
gcc main.c -ldemo4 -L. -o main

标准命名规则:
lib库名.so
lib库名.a

问题:
1)执行程序怎么加载动态库?
2)动态库没有作为执行程序的一部分,为什么连接需要制定动态库及目录?
因为连接器需要确认函数在动态库中的位置
动态库的加载:
①. 找到动态库
②. 加载动态库到内存(系统实现)
③. 映射到用户的内存空间(系统实现)
动态库查找规则:
/lib
/user/lib
LD_LIBRARY_PATH环境变量指定的路径中找
设置当前路径为环境变量:(自己定义的库最好设置好目录,或者放到上述公共目录)
export LD_LIBRARY_PATH=.:~:..:~Walle
缓冲机制:
系统把lib:/user/lib:LD_LIBRARY_PATH里的文件加载到缓冲
/sbin/ldconfig -v 刷新缓冲so中的搜索库的路径
小练习:
输入两个数,计算两个数的和。
要求:输入与计算两个数的和封装成动态库调用

5 使用libdl.so库

动态库加载原理
动态库中函数的查找已经封装成哭libdl.so
libdl.so里面有4个函数:
dlopen//打开一个动态库
dlsym//在打开的动态库里找一个函数
dlclose//关闭动态库
dlerror//返回错误

//dldemo.c
#include <dlfcn.h>
main()
{
void *handle = dlopen("./libdemo4.so", RTLD_LAZY);
void (*fun)(int) = dlsym(handle, "diamond");
fun(5);
dlclose(handle);
}

总结:
1) 编译连接动态库
2)使用动态库
3)怎么配置让程序调用动态库
4)掌握某些工具的使用 nm ldd lddconfig objdump strit(去掉多余的信息)

6 工具make的使用与makefile脚本

背景:
make编译脚本解释
编译脚本makefile
make -f 脚本文件 目标
脚本文件:
1) 文本文件 (例如 demo.mk)
2)基本构成语法
基本单位目标target
目标名:依赖目标
\t目标指令
\t目标指令

//demo.mk
demo:iotool.c graphic.c main.c
gcc iotool.c -c
gcc graphic.c -c
gcc iotool.o graphic.o -shared -o libdemo.so
gcc main.c -ldemo -L. -o main
make -f demo.mk demo 会生产main可执行文件

计算素数的例子

//input.c
#include <stdio.h>

int inputInt()
{
    int a;
    scanf("%d",&a);
    return a;
}
//primer.c
int isPrimer(int a)
{
    int i;
    for(i=2;i<a;i++)
    {
        if(a%i==0)
        {
            return 0;           
        }
    }
    return 1;
}
//demo.c
#include <stdio.h>
main()
{
    int a=inputInt();
    int r=isPrimer(a);
    if(r==1)
    {
        printf("%d是素数!\n",a);
    }
    else
    {
        printf("%d是合数!\n",a);
    }
}
//demo.mk
demo:
    gcc -c -fpic input.c
    gcc -c -fpic primer.c
    gcc -shared -olibdemo.so input.o primer.o
    gcc demo.c -ldemo -L. -omain

执行:export LD_LIBRARY_PATH=.
make -f demo.mk demo

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值