• SunnyOS准备4


    8086 CPU一般是这样:CS寄存器初始化为0xF000,IP寄存器初始化为0xFFF0,所以按照CPU实模式地址计算法则,CPU执行的第一条指令地址是CS*10h+IP,即0xFFFF0处

    对于80386以上的CPU:

    第一点,80386及其以上的现代CPU(以下简称CPU)加电Reset之后并不是直接进入实模式;

    第二点,CPU在合成地址的时候不区分实模式和保护模式。

    CPU进入保护模式的方法是CR0寄存器的PE Bit置为1。而在CPU刚加电的时候,CR0寄存器的PE位实际上没有置1,但是此时也不是实模式,Intel并没有给给出表示此时CPU状态的术语名词。自从80386以来,因为增加了保护模式的缘故,CS等段寄存器不再是简简单单的段寄存器了,而是一个包含了段选择器(segment selector)段基址(segment base),以及段限制(segment limit)的一组复杂寄存器。显然段基址决定着内存段的基地址不过需要说明的是作为程序员只能操作CS寄存器中的“段选择器”这16位的大小,其它的区域作为隐藏区域对程序员不可见,我们无法访问。

    合成地址时,假设段选择器(我们能访问的那16位)装入了0xF000,那么CPU会先将F000 * 10h也就是F0000h装入段基址里。之后需要合成地址的时候不考虑别的,而是直接从之前合成好的段基址里读出基地址F0000h加上IP寄存器里的偏移生成地址。如果CS寄存器的值不发生改变,段基址部分就不会发生改变。所以我们说,CPU在合成地址的时候不区分实模式和保护模式,CPU只是机械的从隐藏区域读出来段基址和IP寄存器的数值相加。

    Intel这样做是为了效率,虽然实模式的地址计算很快,但是保护模式计算一个地址还要有去内存中寻找段描述符等工作,这会大大影响CPU的效率

    放弃,无法理解。。。。。

    原文:http://0xffffff.org/2013/03/14/15-x86-boot/

    32硬件平台下,进程拥有4GB的线性地址空间(并不是物理地址空间)

    启动分段机制,未启动分页机制:逻辑地址--> (分段地址转换) -->线性地址==物理地址
    启动分段和分页机制:逻辑地址--> (分段地址转换) -->线性地址-->(分页地址转换) -->物理地址

    逻辑地址空间:从应用程序的角度看,逻辑地址空间就是应用程序员编程所用到的地址空间,比如下面的程序片段: int val=100; int * point=&val;

    其中指针变量point中存储的即是一个逻辑地址。在基于80386的计算机系统中,逻辑地址由16位的段寄存器(也称段选择子,段选择子)和32位的偏移量构成。

    进程线性地址空间:

    内存高地址区域是被操作系统内核所占据的,Linux操作系统占据了高地址区域的1GB内存(Windows系统默认保留2GB给操作系统,但是可以配置为保留1GB)。如果我们想知道一个进程具体的内存空间布局的话,可以去/proc目录找以进程的pid所命名的目录下一个叫maps的文件,使用cat命令查看即可(需要root权限)。

     32位Linux系统中,代码段从线性地址0x08048000处开始的。数据段一般是在下一个4KB(分页机制默认选择4KB一个内存页)对齐的地址处开始。运行时堆是在数据段之后又一个4KB对齐处开始的,并通过malloc()函数调用向上增长(Linux下的malloc()一般依靠调用brk()或者mmap()系统调用实现)。再接着跳过动态链接库的区域就是进程的运行时栈了,需要注意的是栈是由高地址向着低地址增长的(看图中esp寄存器内容增长方向)。栈空间再往上就是操作系统保留区域了,用于驻留内核的代码和数据。即就是在一个进程的眼里,只有它和操作系统在一起。

    我们具体来看看代码段,以C语言为例,程序代码段的入口_start地址处的启动代码(startup code)是在目标文件ctr1.o(属于C运行时库的部分)中定义的,对于特定平台上的C程序都一样。其执行流程如下:

    0x080480c0 <_start>:
     
    调用 __libc_init_first 函数
    调用 _init 函数
    调用 atexit 函数
    调用 main 函数
    调用 _exit 函数 

    例子:

    #include <stdio.h>
    #include <stdlib.h>
     
    int main(int argc, char *argv[])
    {
        int a = 1;              // 栈区
        static int b = 2;       // 全局静态区(读/写段)
        const int c = 3;        // 只读段
        char *str1 = "Hello";   // str1指针在栈区,"hello"字符串在只读段
        char str2[] = "world";             // 只在栈区(字符串)
        char *str3 = (char *)malloc(20);   // str3变量在栈区,指针指向堆区
       
        return EXIT_SUCCESS;
    }

    注释中我们看到了各个元素所在内存段的位置。而编译好的main函数代码本身是存在于代码区的(一般代码段也是只读段)。我们这个程序运行后如果是动态链接的C语言运行时库的话,动态库会存在图示的动态库映射区无论使用C语言运行时库的程序无论有多少,运行时库的代码在内存里只会有一份,对于不同的程序,进行地址映射即可

    栈也经常被叫做栈帧(Stack Frame)或者活动记录(Activate Record)。栈里通常存储以下内容:

    1.函数的临时变量;
    2.函数的返回地址和参数;
    3.函数调用过程中保存的上下文。

    在i386中,使用esp和ebp寄存器划定范围。esp寄存器始终指向栈顶,随着压栈和出栈操作而改变值。ebp寄存器随着调用过程,暂时的指向一个固定的栈位置,便于寻址操作的进行。

    线程中的地址空间:

    从Linux内核的角度来说,它并没有线程这个概念。在内核中,线程看起来就像是一个普通的进程。

    一个进程所创建的所有线程没有创建新的地址空间,而是共享着进程所拥有的4G的线性空间,文件系统资源、文件描述符、信号处理程序以及被阻断的信号等内容。不过即便是共享地址空间,但是每个线程还是有自己的私有数据的,比如线程的运行时栈。

    #include <unistd.h>
    #include <assert.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <pthread.h>
    void *thread_func(void *args)
    {
        printf("tid: %u pid: %u thread id: %un", getpid(), syscall(224), pthread_self());
        while(1) {
            sleep(10);
        }
    }
    int main(int argc, char *argv[])
    {
        pthread_t thread;
        int count = 0;
        while (pthread_create(&thread, NULL, thread_func, NULL) == 0) {
            sleep(1);
            count++;
        }
        perror("Create Error:");
        printf("Max Count:%dn", count);
        return EXIT_SUCCESS;
    }

    要动态链接lpthread

    #include <unistd.h>
    #include <assert.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <pthread.h>
    int *p_num;
    void *thread_1(void *args)
    {
        int test_num = 1;
        printf("test_num: %dn", test_num);
        p_num = &test_num;
        sleep(2);
        printf("test_num: %dn", test_num);
    }
    void *thread_2(void *args)
    {
        sleep(1);
        if (p_num != NULL) {
            *p_num = 2;
        }
    }
    int main(int argc, char *argv[])
    {
        pthread_t thread1, thread2;
        pthread_create(&thread1, NULL, thread_1, NULL);
        pthread_create(&thread2, NULL, thread_2, NULL);
        pthread_join(thread1, NULL);
        pthread_join(thread2, NULL);
        return EXIT_SUCCESS;
    }
    

      

     thread1修改了thread2内容,所以线程内存空间是共享的

     Done!!!

     

    引用:

    http://0xffffff.org/2013/05/23/18-linux-process-address-space/

    http://0xffffff.org/2013/08/02/20-linux-thread-address-space/

  • 相关阅读:
    帆软报表实现全选全不选的功能
    knowledge_others
    skills_kafka
    skills_operation
    problems_others
    skills_windows
    c语言标识符
    快速排序法
    字符串处理scanf("%d%*c",&n);
    Byte.parseByte(String s,int radix)的解释
  • 原文地址:https://www.cnblogs.com/chaunceyctx/p/7339523.html
Copyright © 2020-2023  润新知