暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

单片机过渡到,对linux的初识(线程到进程)

小昭debug 2022-01-24
459

大家好,我是小昭,一路在debug调试,代码缓慢地优化中,希望生活也能优化起来……


前言


     为了应对自身专业能力的提升和工作的要求,开始学习linux,过程中遇到一些问题,比如像mmu,为什么单片机上不了Linux?mmu的出现是为了解决什么问题?如果这些问题可以得到解决,我相信对入门linux学习会有一定的帮助。学习的过程我参考了很多前辈的总结分享,在这分享我整理总结的内容。


目录



虚拟地址的出现

mmu解决什么问题

fork父子进程资源拷贝

C小测试


话不多说直接上代码例程

    /*ubuntu下环境,gcc编译*/
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    int main(int argc,char* argv[]){
    pid_t pid;
    int a_value=0;
    /*创建子进程*/
    pid = fork();
    if(pid == -1){
    perror("fork_error!\r\n");
    }
    if(pid > 0)
    {/*父进程执行*/
    a_value=1;
    while(1){
    printf("father_process---a_value:%d,adress:%p\r\n",a_value,&a_value);
    sleep(1);//引起进程间调度
    a_value++;
    }
    }
    else if(pid == 0)
    {/*子进程执行*/
    while(1){
    printf(" son_process---a_value:%d,adress:%p\r\n",a_value,&a_value);
    sleep(1);//引起进程间调度
    }
    }
    return 0;
    }


       这个程序运行两个进程,分别打印a_value的数值和地址,先不看后面的运行结果,自己独立思考下,都用着同一个变量地址也一样,按C语言的规则,既然地址一样,数值肯定也一样,但是实际运行出来的效果,数值是不一样的。凸显出linux的进程间资源的独立性。

     

    运行结果:

     


        a_value变量地址相同,父进程的中变量一直自增,子进程中的变量却还是初始值。这样也就为什么说linux中进程间的资源是互相独立,井水不犯河水。但是但是,有一点我们需要思考,与我们的认识的C语言相矛盾,按C语言的解释既然地址一样,为什么数值却不同?

        因为在Linux下,用户看到的地址是虚拟地址,这个地址不是变量在内存中实际的真实地址(物理地址),是需要转换的,确切来说,他们的物理地址是不同的。补充:内核一般会先调度子进程(后文有解释)

     


    什么是虚拟地址和物理地址?




        以stm32等MCU为例,在代码中,实际操作变量的地址全是物理地址,CPU是直接通过地址总线,访问内存(RAM),读取该地址的变量数值,这个地址就是物理地址(Physical Address),简写PA。


        而运行linux的芯片(像cortex-a、arm9和x86等)就比较牛,CPU手底下有MMU(内存管理单元,只有CPU能识别)来帮忙,当要读取变量时,它是不直接访问内存,直接通过访问MMU(虚拟地址Virtual Address 简写VA),MMU再访问内存(物理地址),得到变量的数值。将虚拟地址转换访问内存的不同物理地址这一过程,称地址重定位。所以在linux平台上,用户看到的地址都是虚拟地址。

     



    mmu解决什么问题




        还是以stm32为例子,flash的程序是bootloader+APP1+APP2(固件升级),要让这三个程序独立运行,访问内存地址就不能重叠。那么会出现一个问题,在编译前,就要给每一个程序划分好内存空间,也就物理地址分配使用。但是像linux、window这种系统装那么多软件,显然是做不了,会访问到相同的地址,资源不能独立,访问的数据出错,系统崩溃。虚拟地址就能很好解决这个问题,即使出现相同的地址,当通过MMU地址映射,因为他们的页表不同,就是映射的规则不一样,可以理解为一个函数 PA = f(VA),页表看成函数f(),f()不一样,对应的物理地址肯定就不一样(可以这样简单的理解),这也就解释虚拟地址相同,映射出物理地址不同。像多用户、权限不同、多进程的系统,更需要mmu,可以做到对系统空间保护和安全。(mmu对用户是隐藏)

    补充:stm32单片机没有进程的说法,在RTOS这种多任务系统,程序执行的最小单位:任务(线程)。


    fork父子进程资源拷贝




       fork后,会创建一个与父进程完全相同的子进程,但进程多数是调用exec,打开其他进程。所以出于效率考虑,linux引进了“写时复制”技术,当父进程中的各段发生变化时,父进程才会将对应的段复制给子进程。

        当执行fork后,子进程没有使用exec,子进程指向父进程的代码段、数据段和堆栈的物理空间(VA和PA相同,权限"只读"),当父进程的内容发生改变时,父进程才会复制对应的段分配物理空间给子进程;如果使用exec,两者的代码不用,将分配独立的物理空间,父进程的各段物理空间被子进程占用,父进程不复存在。

        OK,按照这个逻辑,多数情况是使用exec启动子进程,如果先执行父进程,还做“写时复制”是不是会显得有点多余!?如果先执行子进程,就不用多做“写时复制”的骚操作,这也就解释为什么前面说内核会先调度子进程

        不得不说,单单进程这一块就要掌握很多知识,在linux面前只是冰山一角,需要学习的地方特别多。

     



    1、C小测试



      #include<stdio.h>
      void swap_int(int a,int b){
      int temp = a;
      a = b;
      b = temp;
      }
      void swap_str(char* a,char* b){
      char* temp = a;
      a = b;
      b = temp;
      }
      int main(int argc,char* argv[]){
      int a = 10;
      int b = 5;
      char* str_a = "hello world";
      char* str_b = "world hello";
      swap_int(a,b);
      swap_str(str_a,str_b);
      printf("%d %d %s %s\n",a,b,str_a,str_b);
      return 0;
      }

      思考下输出结果?

      正确结果

        10 5 hello world world hello


        如果没有疑问就不用往下看了,要是感到诧异,请继续往下了解,一定对你有所帮助。


            swap_int(a,b);这个应该没什么问题。疑问大部分会出现在swap_str(),其实代码是这样swap_str("hello world","world hello"),传入的是字符,并不是str变量地址,所以值并没发生变化。交换两个字符串,原变量数据类型是char*,要进行值交换,就要传char**二级指针,或者用C++的引用可以解决。具体交换代码示例可以到公众号回复 C测试 查看。

         

        我是小昭debug,如有问题,请麻烦联系我,同时,也很乐意和你做技术交流。

        文章转载自小昭debug,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

        评论