Linux进程创建与控制


Linux下进程控制

-01进程的创建

1.在上一篇博客中写到了进程的创建,但是讲的有点糙,这篇开篇最为补充。
2.进程的创建主要用到的是fork和vfork
Linux进程创建与控制_第1张图片
3.看一个简单的实例

int main()
{

    std::cout<<"fork 前"<<std::endl;
    pid_t pid = fork();

    std::cout<<"fork 后"<<std::endl;

    return 0;

}
//结果
[skin@bogon ~]$ ./a.out 
fork 前
fork 后
fork 后

从结果可以看到打印了两遍的” fork 后” ,那这是为什么呢?从下图我们来看一下这个程序的执行过程
Linux进程创建与控制_第2张图片
通过上图可以看到当fork之后一个”一模一样”的子进程出现了,而且当fork之后子进程和父进程一样都执行到了fork,结果就是打印,所以会有两” fork 后” 被打印出来。

  • 02.fork都做了什么?
  1. 看一下fork之后与之前的区别,我们可以知道,出现了一个以父进程为模板的子进程,而且这个子进程和父进程程序执行到了相同的位置
  2. fork之后会将父进程程序段,代码段,堆区,栈区都进行复制一份,放到子进程的虚拟地址空间上,所以父子进程有不同的地址空间,那么就可以执行不同的流,子进程一般会进行程序替换,然后去执行不同的逻辑。
  3. 在fork之后,子进程和父进程虽然是不同的虚拟地址空间,虽然会不影响各自的执行流,但是linux为了效率,在映射真实物理内存时采用了”写时拷贝”,这样在子进程没有改变原来的数据时,那么父子进程的通过页表进行MMU映射后其实映射到同一块物理内存上。
  4. 看图吧
    (1)没有修改各段数据时:父子进程虽然都有自己的虚拟地址空间,但是通过MMU映射后都是指向同一块真实的物理空间的。
    Linux进程创建与控制_第3张图片
    (2)当修改了数据时:父子进程还是都有一块自己的虚拟地址空间,通过各自的页表进行映射,但是此时代码段还是映射到同一块物理内存上,但是数据段就变成各自一块物理内存了。如图
    Linux进程创建与控制_第4张图片

总结一下上述所讲,fork之后会创建一个以当前进程为模板的子进程,子进程复制了当前进程的堆,栈,数据段,代码段。但是因为每个进程都有一个独立的,且相同的虚拟地址空间,通过各自的页表映射的真实物理内存也都是同一块。当其中一个进程尝试修改其各段的数据时,那么内核会复制一份真实物理内存的给这个进程,同时当然也要修改其页表,使其可以映射到新分配的物理内存上。

  • 03.简述一下vfork

1.这个函数使用和fork一模一样,没啥区别
2.和vfork的区别是这个函数可以保证,vfork之后一定是子进程运行,然后父进程才去运行。而fork则不能保证那个进程先执行,那个后执行,完全由调度器来决定。
3.看一下vfork的定义
Linux进程创建与控制_第5张图片
4.实例看一下,即使我的子进程等待了5s,父进程依然要让子进程先执行。看一下对比。
Linux进程创建与控制_第6张图片
相信这样就很好的展示了vfork与fork的区别。

  • 04.进程的终止(退出)

1.从main函数返回。
2.调用exit或者_exit函数退出。
3.异常终止,退出。(ctrl -c )
对exit进行进一步说明
Linux进程创建与控制_第7张图片

  • 05.进程等待

1.为什么要进程等待呢?

a. 上篇博客已经简述了僵尸进程,而进程等待可以避免僵尸进程,从而达到资源的回收,防止内存泄漏


b. 因为往往我们开辟子进程是为了让子进程进程程序替换,去做一些其他事情,可以拿到子进程退出信息,做一些对应的事情。比如子进程去负责收集影片的评分,父进程可以获取这些评分后,统一做排名,所以要等所有子进程度完成后才可以去进行排名

2. 进程等待函数wait和waitpid

Linux进程创建与控制_第8张图片

Linux进程创建与控制_第9张图片

返回情况:
1.正常等到了进程,wait/waitpid直接立刻返回,拿到了子进程退出信息,释放资源。
2.设置了WNOHANG,进程没有推出直接返回0,不等待
3.等待的进程不存在,立刻异常返回-1.

下面对其使用一下:

#fork
int main()
{

    pid_t pid = vfork();
    if(pid<0)
    {
        perror("vfork");
        return -1;
    }
    else if(pid>0)
    {
        int sta ;
        wait(&sta);
        if(WIFEXITED(sta))
        {
            std::cout<<"子进程正常返回!"<<"子进程退出码:"<(sta)<<" "<<std::endl;
        }
        else
        {
            std::cout<<"子进程异常返回!"<::endl;
        }
    }
    else
    {
        std::cout<<"子进程退出!"<::endl;
        exit(5);
    }

    return 0;

}

[skin@bogon ~]$ ./a.out 
子进程退出!
子进程正常返回!子进程退出码:5 
#vfork
int main()
{

    pid_t pid = fork();
    if(pid<0)
    {
        perror("fork");
        return -1;
    }
    else if(pid>0)
    {
        int sta ;
        int ret=0;
        do{
            ret =waitpid(pid,&sta,WNOHANG);
            if(ret==0)
            {
                std::cout<<"等待的进程还没有退出 !"<::endl;
                sleep(1);
            }
            else if(ret<0)
            {
                std::cout<<"不存在的进程 !"<::endl;
                return -1;
            }
            else
            {
                std::cout<<"等到了进程退出 !"<::endl;

                if(WIFEXITED(sta))
                {
                    std::cout<<"子进程正常返回!"<<"子进程退出码:"<(sta)<<" "<<std::endl;
                }
                else
                {
                    std::cout<<"子进程异常返回!"<::endl;
                }
            }
        }while(ret ==0);
    }
    else
    {
        sleep(1);
        std::cout<<"子进程退出!"<::endl;
        exit(5);
    }

    return 0;

}

[skin@bogon ~]$ ./a.out 
等待的进程还没有退出 !
等待的进程还没有退出 !
子进程退出!
等到了进程退出 !
子进程正常返回!子进程退出码:5 

  • 06.进程的程序替换-exec家族

1.首先呢,这些函数都是同一个作用,就是进行程序的替换。
2.其中只有execve才是正统的系统函数,其他都是基于它封装的库函数。
3.看下man手册对他的介绍
Linux进程创建与控制_第10张图片
4.程序替换的原理:进行程序替换的进程,将自己的真实物理内存上的代码段,数据段都换成新程序的代码和数据,然后进程从替换代码的程序启动地方开始执行,这样就完成了程序替换,如图
Linux进程创建与控制_第11张图片
5. 来段代码,就知道了

//主程序
int main()
{

    pid_t pid = fork();
    if(pid<0)
    {
        perror("fork");
        return -1;
    }
    else if(pid>0)
    {
        //防止僵尸
        wait(NULL);
    }
    else
    {
        //进行程序替换
        std::cout<<"子进程开始替换当前路径下的show程序 !"<<std::endl;
        execl("./show","./show",NULL);
        //无成功返回
        perror("exec");
        return -1;
    }

    return 0;

}

//被调用的show程序
int main()
{
    std::cout<<"show 程序正在执行 !\n";
    std::cout<<"show 程序执行完成 !\n";

    return 0;
}


//主程序运行结果
[skin@bogon ~]$ ./a.out 
子进程开始替换当前路径下的show程序 !
show 程序正在执行 !
show 程序执行完成 !

如有错误,可以私信我,在这里表示感谢!

你可能感兴趣的:(Linux)