dlopen(3) dlclose(3) dlerror(3) dlsym(3)
#include <dlfcn.h>
void *dlopen(const char *filename, int flags);
参数 filename 指定了共享库的文件名
flags 二选一 RTLD_LAZY :懒绑定,代码执行时才绑定
RTLD_NOW : 立即绑定,在函数返回之前已经绑定
返回值 成功时返回 非null 错误返回null
使用时gcc后加上 -ldl. link with -ldl
int dlclose(void *handle);
功能:共享库文件的引用计数减一,减到0的时候自动卸载共享库
参数:
handle指定已经加载的共享库的地址
返回值:成功返回0,否则非0
以上这些函数的错误信息,可以使用dlerror(3)获取
举例说明 动态加载动态库
代码参见 dynamic.c
#include<dlfcn.h>
char *dlerror(void);
功能:获取最近一次dlopen API函数执行的错误信息
参数:
返回值:如果返回NULL则表示没错误 否则返回字符串首地址,这个字符串是我们可以读懂的错误信息
#include <dlfcn.h>
void *dlsym(void *handle, const char *symbol);
功能:获取共享库中某一函数的地址
参数:
handle-指定了共享库
symbol- 指定了函数的名字
返回值:成功 返回symbol加载到内存的地址
失败
void *
void *malloc(1024)
char *p=(char *)malloc(1024)
三、程序中的错误处理
程序执行过程中,调用库函数或者系统调用。这些函数错误的时候,如何获取这些函数执行的错误信息呢?
系统维护着一个全局变量 int errno.当以上函数调用失败的时候,记录errno的值。根据errno的值获取函数执行失败的错误信息
errno(3)
#include<errno.h>
代码引入错误处理,参见代码,file.c、
strerror
#include <string.h>
char *strerror(int errnum);
功能:返回errnum描述错误原因的信息
参数:
返回值: unknown error nnn
返回跟错误编号对应的错误描述的字符串首地址
perror(3)
#include <stdio.h>
void perror(const char *s);
功能:输出一条系统错误消息
参数:
s:用户自定义字符串
返回值:
标准输入 键盘 FILE* stdin
标准输出 显示器 FILE* stdout
标准错误输出 显示器 FILE* stderr
#define E_MSG(string,val) do{perror(string);return (val);}while(0)
今天内容
一gdb调试器的使用
可执行文件要想在执行的时候被调试,可执行文件中必须包含有调试信息。
那如何给可执行文件加调试信息呢?
gcc hello.c -g或者-ggdb
启动调试器,调试可执行程序
gdb a.out
调试命令
l 列出程序清单
断点 让程序执行到断点的时候,停止
b 行号|函数的名字 设置断点
r 运行程序
n 执行下一条语句
p 变量的名字 输出变量的值
q 退出调试
s step 单步调试
二、内存管理
物理地址 虚拟地址
cpu的寻址空间(能力) 0~4G-1
页 页表 页框 页的单位是4k
每次分配34页
cpu可以有两种身份 有四种(操作系统只用两种) 内核态 用户态
cpu工作在用户态的情况下,可以访问的虚拟地址空间是0~3G-1
工作在内核态的情况下,可以访问的虚拟地址空间是3G~4G-1
代码验证段错误 参见 segment.c
每个进程都有自己独立的4G虚拟地址空间
每个进程都有自己的一份页表 当进程需要访问内核1G的地址空间的时候,向操作系统申请访问,操作系统代理进程访问这些资源。
数据区间 代码区间 栈区间
代码段 数据段 栈段
代码段 不能写 只读 可执行
数据段 能读能写 不能执行
堆
获取进程的pid
getpid(2)
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);
功能:获取当前进程的pid
参数:
返回值:当前进程的pid
pid_t 数据类型
举例说明 代码参见 memory.c
/proc/进程的pid/maps
BBS 未初始化的全局变量
代码段 保存程序代码的
静态存储 已经初始化的全局变量或者使用静态分配的变量
栈 局部变量、参数表
堆 动态内存
三 使用mmap将物理地址映射到进程的虚拟地址空间
try.c
#include<stdio.h>
#include<string.h>
int main(int argc,char* argv[]){
int i=0;
if(strcmp(argv[1],"zhaocb")==0){
printf("shihaoren\n");
}
printf("%d\n",argc);
for(i=0;i<argc;i++){
printf("->%s\n",argv[i]);
}
return 0;
}
segment.c
#include<stdio.h>
int main(void){
int val=300;
int *p=(int *)0xa0000000;
printf("p content is %p\n",p);
printf("*p content is %d\n",*p);
return 0;
}
file.c
#include<stdio.h>
#include<errno.h>
#include<string.h>
#include"E_MSG.h"
int main(int argc,char* argv[])
{
//打开一个文件 argv[1]
FILE *fp=fopen(argv[1],"r");
if(fp==NULL){
#if 0
printf("file open failed number is %d...reason is %s\n",errno,strerror(errno));
perror("fopen");
return -1;
#endif
E_MSG("fopen",-1);
}
printf("file fopen success...\n");
fclose(fp);
return 0;
}
E_MSG.h
#ifndef E_MSG_H_
#define E_MSG_H_
#define E_MSG(string,val) do{perror(string);return (val);}while(0)
#endif
dynamic.c
#include<stdio.h>
#include<dlfcn.h>
int main(int argc,char *argv[]){
//func是指针类型的变量,但是对func变量的内容作为地址,对这个地址里的内容访问的时候,遵循函数的访问规则。
int (*func)(int,int);
//动态加载共享库,argv[1]指定共享库文件的名字
void *h=dlopen(argv[1],RTLD_NOW);
if(h==NULL)//打开共享库文件失败
{
printf("dlopen failed...\n");
printf("%s\n",dlerror());
return -1;
}
printf("dlopen success..\n");
//在共享库中找函数的地址
void *f= dlsym(h,"t_div");
if(f==NULL)//没找到函数
{
printf("%s\n",dlerror());
return -1;
}
//这个函数找到了,函数的入口地址,但是是void,不访问
//如何访问这个函数的代码
func=f;
printf("6/2=%d\n",func(6,2));
//关闭共享库文件
dlclose(h);
return 0;
}