linux 环境编程学习笔记 第四、五天 映射虚拟内存,编译工具,静态库,动态库


寒假学习 第四、五天 (linux 高级编程) 笔记 小结

一、映射虚拟内存

mmap / unmap          


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

映射方式:
内存映射:匿名映射。
文件映射:映射到某个文件
               只有文件映射最后两个参数有效。
MAP_ANONYMOUS 
MAP_SHARED   MAP_PRIVATE(二选一)
 
列子:
#include<stdio.h>
#include<unistd.h>
#include<sys/mman.h>
#include<stdlib.h>

int main()
{
	int *p = mmap(
		NULL,
		getpagesize(),     //一个页的大小
		PROT_READ|PROT_WRITE,
		MAP_ANONYMOUS|MAP_SHARED,
		0,0);
	*p = 0;
	*(p+1) = 1;
	*(p+2) = 2;
	*(p+3) = 3;

	printf("%d\n",p[2]);
	printf("%p\n",p);
	printf("%p\n",p+1);
	printf("%p\n",p+2);
	printf("%p\n",p+3);
	munmap(p,getpagesize());
	return 0;
}
 
 

二、编译工具

1.gcc

gcc
	-o  输出文件名
	-O  -O0 -O1 -O2 -O3  编译优化
	-g  -g0 -g1 -g2 -g3  产生调试信息
	-W  all  error   
	    -Wall  显示所有警告  
	    -Werror	  把警告当错误
	-w  关闭警告
		
			
	-c  只编译不连接
	-E  预编译
	-S  汇编
			
	-D	在命令行定义宏。
			在代码中定义宏
			在命令行定义宏
						
	-x  指定编译的语言类型
		c++
		c
                assembler 汇编
		none 自动判定
	-std=C89
	     C99
 
编译过程:-E  -c -S   自动调用连接器
连接器是 ld
注意 gcc只是编译器   链接是用ld
 
 
 

三、静态库


什么是静态库

     函数等代码封装的二进制已经编译的归档文件
     本质就是目标文件集合(归档)
     静态是指编译好的程序运行的时候不依赖库,库作为程序的一部分编译连接

采用静态库方式管理代码优点

      容易组织代码
      复用
      保护代码版权

静态库的编译

  
   1.编译
      gcc -c -static  xxx.c    其中    -static 是可选的
   2.归档成静态库
      ar  -r   xxx.a  xxx.o  xxx.o     xxx.a  就是静态库文件
      ar是一个归档工具 
   3.使用静态库
      方式1:   gcc  xxx.c  xxx.a  -oxxx
      方式2:   把静态库文件的名子写成  lib库名.a(lib库名.a.主版本号.副版本号.批号) 
                        用   gcc  xxx.c -l  库名 -L 库所在的位置

nm工具

   可以察看函数符号表
   nm 静态库或者动态库  或   者目标文件    或   者执行文件
 
 
 
列子:
root@/codes/day04# ar -r libku.a add.o sub.o
root@/codes/day04# gcc main.c -omain -lku -L .
 

四、动态库(共享库)

什么是动态库,跟静态库的区别

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

工具

       ldd  可执行文件
             查看程序需要调用的动态库,只能加可执行文件(动态库文件也是可执行文件)
       readelf  -h   可执行文件
              linux 下可执行文件格式是elf  ,readelf  -h 可用来察看执行程序头

动态库的编译

     gcc  -c -fpic  xxx.c        -fpic  可不用写
     gcc  -shared  -oxxx.so   xxx.c   xxx.c        xxx.so 就是动态库文件

使用动态库

     方法1:gcc xxx.so  xxx.c  -oxxx

     方法2: lib库名.so     gcc xxx.c -l 库名  -L 库所在路径

      这样编译出来的可执行文件还是会出错的,会出现找不到  lib库名.so,要改环境变量LD_LIBRARY_PATH
     (不要忘记加上export )或直接把lib库名.so 放到 /lib /user/lib  等

      方法3:使用libdl.so库 直接在代码加载动态库

        动态库中函数的查找已经封装成库libdl.so
dlopen   打开一个动态库
dlsym     在打开动态库找一个函数
dlclose  关闭动态库
dlerror   返回错误

列子:
#include<stdio.h>
#include<unistd.h>
#include<dlfcn.h>

int main()
{
	void *handle = dlopen("./libku2.so",RTLD_LAZY);
	int (*add)(int,int) = dlsym(handle,"add");
	int (*sub)(int,int) = dlsym(handle,"sub");
	int r = add(55,22);
	int s = sub(55,22);
	dlclose(handle);
	printf("%d %d\n",r,s);
	printf("%d\n",getpid());
	while(1);
	return 0;
}
编译  gcc main.c -omain -ldl

这样在运行就 不用改环境变量 编译时也 -l 指定 上面的 ku2 
但 要指定 libdl.so   加上  -ldl
 
 
   

五、其他知识点


restrict   
只能是用在 函数参数指针上的
优化函数参数的寻址过程,提供运行速度
 
register
修饰普通变量
表示这个数据要保存在寄存器中
 
 
readelf  -h  查看执行文件的头
 
nm  查看库中的函数符号
 
ldconfig  -v 刷新缓冲中so的搜索数据
 
objdump    有点像那个快速查看之类的工具,就是以一种可阅读的格式让你更多地了解二进制文件可能带有的附加信息。
 
strip  去掉多余信息