可执行程序工作原理、

1、ELF(Executable and Linkable Format)可执行和可链接文件,其包含了以下三类:

  1. 可重定位文件(Relocatable File):保存着代码和适当的数据,用来和其它的目标文件一起来创建一个可执行文件、静态库文件或者是一个共享目标文件(主要是.o文件)
  2. 可执行文件(Executable File):保存着一个用来执行的程序,一般由多个可重定位文件结合生成,是完成了所有重定位工作和符号解析(除了运行时解析的共享库符号)的文件。
  3. 共享目标文件(Shared Object File):保存着代码和合适的数据,用来被两个链接器链接。第一个是链接编辑器(静态链接),可以和其它的可重定位和共享目标文件来创建其它的object。第二个是动态链接器,联合一个可执行文件和其它的共享目标文件来创建一个进程映象。

2、ELF文件格式:

ELF文件的主体是各种节,以及描述这些节属性的信息(Program header table和 Section header table),以及ELF文件的整体性信息(ELF header),如下图。

3、程序编译

程序从源代码到可执行文件的步骤:预处理、编译、汇编、衔接--以hello.c为例。

预处理: gcc -E hello.c -o hello.i  -m32
编译:gcc -S hello.i -o hello.s -m32
汇编:gcc -c hello.s -o hello.o -m32
默认衔接(动态库):gcc hello.o -o hello -m32
衔接静态库:gcc hello.o -o hello.static -m32 -static

最后得到的hello和hello-static文件就是可执行文件。可执行文件中的内容包括有编译后的机器指令代码、数据还包括了链接时所须要的一些信息,比如符号表、调试信息、字符串等。

其文件格式,如下图。

4、静态衔接和动态衔接

静态链接:在编译链接时直接将需要的执行代码复制到最终可执行文件中,优点是代码的装载速度快,执行速度也比较快,对外部环境依赖度低。缺点是如果多个应用程序使用同一库函数,会被装载多次,浪费内存。

动态链接:编译时不直接复制可执行代码,而是通过记录一系列符号和参数,在程序运行或加载时将这些信息传递给操作系统。操作系统负责将需要的动态库加载到内存中,在程序运行到指定的代码时,去共享执行内存中已经加载的动态库去执行代码,最终达到运行时链接的目的。优点是多个程序可以共享同一段代码,而不需要在磁盘上存储多个复制。缺点是在运行时加载可能会影响程序的前期执行性能,而且对使用的库依赖性较高。(分为装载时动态链接和运行时动态链接)

使用gcc hello.o -o hello.static -static进行静态链接,发现得到的可执行程序文件大小可达到动态链接的100倍,如图。

5、动态衔接

动态衔接分为以下两种;

  1. 可执行程序装载时动态衔接

    2.运行时动态衔接。

装载时动态衔接意味着在程序一开始启动的时候其所调用的库需要在一开始就提供,而运行时动态衔接只有程序运行到相关语句才会访问dllibexample。

6、fork和execve区别与联系

对于fork():

1、子进程复制父进程的所有进程内存到其内存地址空间中。父、子进程的

“数据段”,“堆栈段”和“代码段”完全相同,即子进程中的每一个字节都

和父进程一样。

2、子进程的当前工作目录、umask掩码值和父进程相同,fork()之前父进程

打开的文件描述符,在子进程中同样打开,并且都指向相同的文件表项。

3、子进程拥有自己的进程ID。

对于exec():

1、进程调用exec()后,将在同一块进程内存里用一个新程序来代替调用

exec()的那个进程,新程序代替当前进程映像,当前进程的“数据段”,

“堆栈段”和“代码段”背新程序改写。

2、新程序会保持调用exec()进程的ID不变。

3、调用exec()之前打开打开的描述字继续打开(好像有什么参数可以令打开

的描述字在新程序中关闭)

实验1:

编程使用exec* 库函数加载一个可执行文件,动态链接分为可执行程序装载时动态链接和运行时动态链接,编程练习动态链接库的这两种使用方式。

1、实验楼上说要从某个网站上下载下面两个源码:

shlibexample.h (1.3 KB) - Interface of Shared Lib Example

shlibexample.c (1.2 KB) - Implement of Shared Lib Example

但是后来查到是网易云付费课的附件,为了两个代码没必要花这么多钱,再说书上已经有源码了,可以自己写一遍,但是书上的代码有一个大坑,直接影响了我后面做实验的进度,之后来我会说明。

shlibexample.h
#ifndef _SH_LTB_EXAMPLE_H_
#define _SH_LTB_EXAMPLE_H_
#define SUCCESS 0
#define FAILURE (-1)
#ifdef __cplusplus
extern "C" {
#endif
int SharedLibApi();
#ifdef __cplusplus
}
#endif
#endif dllibexample.h
#ifndef _DL_LTB_EXAMPLE_H_
#define _DL_LTB_EXAMPLE_H_
#ifdef _cplusplus
extern "C"{
#endif
int DynamicalLoadingLibApi();
#ifdef _cplusplus
}
#endif
#endif shlibexample.c
#include <stdio.h>
#include "shlibexample.h"
int SharedLibApi()
{
printf("This is a shared libary!\n");
return SUCCESS;
} dllibexample.c
#include <stdio.h>
#include "dllibexample.h"
#define SUCCESS 0
#define FAILURE(-1)
int DynamicalLoadingLibApi()
{
printf(“This is a Dynamical Loading library”!\n”);
return SUCCESS;
}

2、编译成libshlibexample.so文件

$ gcc -shared shlibexample.c -o libshlibexample.so -m32
$ gcc -shared dllibexample.c -o libdllibexample.so -m32

错误到这里就显现出来了,为什么会报错我在网上查了很多答案,什么需要更新gcc啊,什么C文件头名字冲突啊,反正各种答案,耗费了很久时间就是依旧会报错,于是我重新打了好几遍代码,直到发现有一个贼细小的地方,书上很容易误导。

**注意看!这分明看着是就是LIB对吧,我在vim编辑器中敲的时候也和书上的样子差不多,然而我仔细看了一下I的上横和下横还是有点长短不一的,这货该不会是T吧!!,结果事实验证了我的判断,这货就是T,LTB。终于找到原因了 **

3、mian函数中分别以共享库和动态加载共享库的方式使用libshlibexample.so文件和libdllibexample.so文件

main.c
#include <stdio.h>
#include "shlibexample.h"
#include <dlfcn.h> int main()
{
printf("This is a Main program!\n");
/* Use Shared Lib */
printf("Calling SharedLibApi() function of libshlibexample.so!\n");
SharedLibApi(); //直接调用共享库
/* Use Dynamical Loading Lib */
void * handle = dlopen("libdllibexample.so",RTLD_NOW);//打开动态库并将其加载到内存
if(handle == NULL)
{
printf("Open Lib libdllibexample.so Error:%s\n",dlerror());
return FAILURE;
}
int (*func)(void);
char * error;
func = dlsym(handle,"DynamicalLoadingLibApi");
if((error = dlerror()) != NULL)
{
printf("DynamicalLoadingLibApi not found:%s\n",error);
return FAILURE;
}
printf("Calling DynamicalLoadingLibApi() function of libdllibexample.so!\n");
func();
dlclose(handle); //卸载库
return SUCCESS;
}

4、动态衔接运行测试

因为shilibexample在衔接时就需要提供路径,对应的头文件shilibexample.h也需要在编译器能找到位置。使用参数-L表明文件路径,-l表示库文件名。

dllibexample只有在程序运行到相关语句才会访问,在编译时不需要任何的相关信息,使用-ldl指明其所需要的共享库dlopen,同时修改LD_LIBRARY_PATH确保dllibexample.so可以查到。

gcc main.c -o main -L./ -l shlibexample -ldl -m32
export LD_LIBRARY_PATH=$PWD
./main

实验2:

使用gdb跟踪分析一个execve系统调用内核处理函数sys_execve ,验证您对Linux系统加载可执行程序所需处理过程的理解。

实验二就只能在实验楼环境做了,毕竟本地虚拟机没有配好的环境。

1、首先从github上下载包含test_exec.c文件的文件夹,进行编译。

ls
cd ~/LinuxKernel
rm menu -rf
git clone https://github.com/mengning/menu.git
cd menu
mv test_exec.c test.c
make rootfs

2、在qemu界面使用help和exec命令查看是否编译成功。

3、随后重新启动qemu。

4、加载内核和端口。

5、设置三个断点。

6、调试运行程序。

2019-2020-1 20199314 《Linux内核原理与分析》 第八周作业的更多相关文章

  1. 2019-2020-1 20199329《Linux内核原理与分析》第九周作业

    <Linux内核原理与分析>第九周作业 一.本周内容概述: 阐释linux操作系统的整体构架 理解linux系统的一般执行过程和进程调度的时机 理解linux系统的中断和进程上下文切换 二 ...

  2. 2019-2020-1 20199329《Linux内核原理与分析》第二周作业

    <Linux内核原理与分析>第二周作业 一.上周问题总结: 未能及时整理笔记 Linux还需要多用 markdown格式不熟练 发布博客时间超过规定期限 二.本周学习内容: <庖丁解 ...

  3. 20169212《Linux内核原理与分析》第二周作业

    <Linux内核原理与分析>第二周作业 这一周学习了MOOCLinux内核分析的第一讲,计算机是如何工作的?由于本科对相关知识的不熟悉,所以感觉有的知识理解起来了有一定的难度,不过多查查资 ...

  4. 20169210《Linux内核原理与分析》第二周作业

    <Linux内核原理与分析>第二周作业 本周作业分为两部分:第一部分为观看学习视频并完成实验楼实验一:第二部分为看<Linux内核设计与实现>1.2.18章并安装配置内核. 第 ...

  5. 2018-2019-1 20189221 《Linux内核原理与分析》第九周作业

    2018-2019-1 20189221 <Linux内核原理与分析>第九周作业 实验八 理理解进程调度时机跟踪分析进程调度与进程切换的过程 进程调度 进度调度时机: 1.中断处理过程(包 ...

  6. 2017-2018-1 20179215《Linux内核原理与分析》第二周作业

    20179215<Linux内核原理与分析>第二周作业 这一周主要了解了计算机是如何工作的,包括现在存储程序计算机的工作模型.X86汇编指令包括几种内存地址的寻址方式和push.pop.c ...

  7. 2019-2020-1 20209313《Linux内核原理与分析》第二周作业

    2019-2020-1 20209313<Linux内核原理与分析>第二周作业 零.总结 阐明自己对"计算机是如何工作的"理解. 一.myod 步骤 复习c文件处理内容 ...

  8. 2018-2019-1 20189221《Linux内核原理与分析》第一周作业

    Linux内核原理与分析 - 第一周作业 实验1 Linux系统简介 Linux历史 1991 年 10 月,Linus Torvalds想在自己的电脑上运行UNIX,可是 UNIX 的商业版本非常昂 ...

  9. 《Linux内核原理与分析》第一周作业 20189210

    实验一 Linux系统简介 这一节主要学习了Linux的历史,Linux有关的重要人物以及学习Linux的方法,Linux和Windows的区别.其中学到了LInux中的应用程序大都为开源自由的软件, ...

  10. 2018-2019-1 20189221《Linux内核原理与分析》第二周作业

    读书报告 <庖丁解牛Linux内核分析> 第 1 章 计算工作原理 1.1 存储程序计算机工作模型 1.2 x86-32汇编基础 1.3汇编一个简单的C语言程序并分析其汇编指令执行过程 因 ...

随机推荐

  1. SpringCache - 请求级别缓存的简易实现

    前言 在SpringCache缓存初探中我们研究了如何利用spring cache已有的几种实现快速地满足我们对于缓存的需求.这一次我们有了新的更个性化的需求,想在一个请求的生命周期里实现缓存. 需求 ...

  2. 创建新镜像-从已创建的容器中更新镜像并提交镜像(以Nginx为例)

    目标:现在我们主要是修改nginx的index.html,然后做一个新镜像 1.基于nginx:1.12运行一个容器 docker run -d -p 8080:80 --name nginx ngi ...

  3. Java8 Stream性能如何及评测工具推荐

    作为技术人员,学习新知识是基本功课.有些知识是不得不学,有些知识是学了之后如虎添翼,Java8的Stream就是兼具两者的知识.不学看不懂,学了写起代码来如虎添翼. 在上篇<Java8 Stre ...

  4. 设计模式----行为型模式之观察者模式(Observer Pattern)

    下面是阅读<Head First设计模式>的笔记. 观察者模式 定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新. JDK API内置机制 ...

  5. BeetleX服务网关之限流和缓存

    限流和缓存相关是网关中两个非常重要的功能,前者是保障服务更可靠地运行,后者则可以大大提高应用的吞吐能力.Beetlex.Bumblebee微服务网关提供了两个扩展插件来实现这两个功能,分别是Beetl ...

  6. Mysql数据类型TINYINT(1)与BOOLEAN踩坑记

    熟悉Mysql的同学应该都知道,Mysql查询的boolean结果将输出为0或者1. 比如: ; 其输出结果为1. 查阅mysql官方文档仅找到如下描述: 11.10 Using Data Types ...

  7. SpringBootSecurity学习(24)前后端分离版之OAuth2.0 应用登记

    应用登记 一个应用要求 OAuth 授权,必须先到对方网站登记,让对方知道是谁在请求.举个例子,下面是github的登记页面: https://github.com/settings/applicat ...

  8. git分支的创建、删除、切换、合并

    需求背景 开发新功能和修改bug一般放在新建分支,如果觉得可行,可以合并到master分支上. 方式 1.查看分支 git branch (查看本地分支及当前所属分支) git branch -a ( ...

  9. python编程基础之七

    运算关系:也就是常说比较运算,返回值只有True, False ==  判断是否相等 != 判断是否不相等 > ,< ,>= , <=    判断是否大于,小于,大于等于,小于 ...

  10. Python3 解决 ModuleNotFoundError: No module named 'pygal.i18n' 问题

    在获取国别码集通过导入模块pygal报以下问题: from pygal.i18n import COUNTRIES  解决方法: 安装模块 pip3 install pygal_maps_world ...