命令行参数和虚拟地址空间

发布于:2025-09-08 ⋅ 阅读:(16) ⋅ 点赞:(0)

一.命令行参数

1.什么是命令行参数?

在Linux中,命令行就是我们操作各种功能的指令,eg:ls  cd pwd ...等等。不过我们有时候还要在指令的后面添加一些参数,eg:ls -l    ls -l -a等等,所以这一整行指令就叫做命令行参数,下面以一个自写一个程序为例子:

2.命令行参数的实现:

#include<stdio.h>
#include<string.h>
int main(int argc,char* argv[]){
    if(argc !=2){
        printf("请按照选项来输入指令\n");
        return 0;
    }
    printf("argc = %d\n",argc);
    if(strcmp(argv[1],"a") == 0){
        printf("这是功能1\n");
    }
    else if(strcmp(argv[1],"b") == 0){
        printf("这是功能2\n");
    }
    else {
        printf("该功能不存在\n");
        printf("hello world\n");
        printf("hello bit\n");
        printf("还在等待来发中。。。\n");
    }
    return 0;
}

3.运行效果图:

4.解释代码:

其中指令中./a.out 包括后面的a 或者b 或者c 都是命令行参数,main函数中的argc的表示命令行参数的个数,例如在上述的程序中,当输入的参数个数超过2时,就会报错退出; argv是一个指针数组,用来指向各个命令行参数,在上面的这个程序中,argv[0] ==./a.out  argv[1] == a,除此之外,argv[2] == NULL(就是说,这个指针数组的最后一个元素为NULL,这是规定使然)。

所以当你输入一个指令的时候,argc最少都为1,表示一个命令行参数,根据输入的参数个数自动变换argc的值,这就是命令行参数

二.虚拟地址空间

1.  代码展示1:

#include<stdio.h>
#include<unistd.h>
int gval = 100;
int main(){
    pid_t id = fork();
    if(id == 0){
        //子进程
        while(1){
            printf("我是子进程,pid: %d, ppid: %d, gval: %d, gval地址: %p\n",getpid(),getppid(),gval,&gval);
            sleep(1);
            //gval++;
        }
    }
    else{
        //父进程
        while(1){
            printf("我是父进程,pid: %d, ppid: %d, gval: %d, gval地址: %p\n",getpid(),getppid(),gval,&gval);
            sleep(1);
        }
    }
    return 0;
}

代码1运行结果:                    

2.代码展示2:

#include<stdio.h>
#include<unistd.h>
int gval = 100;
int main(){
    pid_t id = fork();
    if(id == 0){
        //子进程
        while(1){
            printf("我是子进程,pid: %d, ppid: %d, gval: %d, gval地址: %p\n",getpid(),getppid(),gval,&gval);
            sleep(1);
            gval++;
        }
    }
    else{
        //父进程
        while(1){
            printf("我是父进程,pid: %d, ppid: %d, gval: %d, gval地址: %p\n",getpid(),getppid(),gval,&gval);
            sleep(1);
        }
    }
    return 0;
}

代码2结果如图:

        从上面案例可以看出:对于一个全局变量,父子进程视角中的地址都是相同的,但是父子进程中的gval值却可以不同的,所以为什么同一个地址,不同的进程访问到的值却不同的呢?

解释:

什么是虚拟地址?

        其实我们所学的所有的内存地址都是虚拟地址,换句话说,就是我们所有的语言展示出来的可视化的地址都是虚拟地址,就是我们常看到的堆区、栈区、初始化区、未初始化区等等,以32位为例,通俗意义上讲,虚拟地址空间就是4G的大小。每个进程都可以单独拥有自己的一个虚拟地址空间,所以进程的定义就是 

进程 == 内核数据结构(task_struct,mm_struct,页表)(也就是虚拟地址空间)+ 代码和数据

虚拟地址空间可以理解成一个数据结构,他不是真正意义上的内存地址空间。

为什么要有虚拟地址空间?

        物理内存就是通俗意义上的CPU中的内存,物理内存是有限的,进程的调度要消耗内存,为了提高效率和解决物理地址空间不足的问题,就给每个进程配一个独立的虚拟内存,虚拟地址空间与物理内存之间通过页表来映射。子进程的创建就以父进程为模板,在不发生修改代码和数据的情况下,父子进程的虚拟地址空间和页表是一样的,也就是说,父子进程通过虚拟地址访问到的物理地址中的数据是一样的,这个就能解释代码展示1中的父子进程中的gval值一样的情况。这样就极大的提高了操作系统调度进程的效率。(通俗上的讲,虚拟地址空间就好像是操作系统给进程画的一张超级大的饼,让进程大胆的进行调度和运行,进程以为操作系统只给她一个人的承诺,但其实操作系统给每个进程都这么画,并且各个进程是相互独立的,不知道其他进程的存在,最终沦为操作系统池塘中的一条鱼)

当然了,采用虚拟地址空间的原因有很多,下面是AI出的好处:

什么是写时拷贝?

        创建子进程后,子进程的虚拟内核数据结构是以父进程为模板继承而来的,代码和数据也只有只读属性,当子进程修改代码或者数据时,操作系统会在物理内存中开辟一块新的空间,将修改后的数据写到新的空间中,但是子进程中数据的虚拟地址并没有改变,还是和父进程的虚拟地址一样,改变的是物理地址与虚拟地址之间的这种的映射关系,使得看似一模一样的虚拟地址,通过页表映射访问到物理地址空间中的真实的数据却不一样了,这就是写时拷贝。这样似乎就能解释代码展示2中的,子进程中的gval和父进程中的gval值不一样,但是他们展示出来的虚拟地址却是一样的情况了。写时拷贝,也是进程保证独立性的手段。


网站公告

今日签到

点亮在社区的每一天
去签到