铁佛 发表于 2024-10-14 09:03:24

【Linux】————进程控制

 https://img-blog.csdnimg.cn/direct/9efbcbc3d25747719da38c01b3fa9b4f.gif
                                                      作者主页:     作者主页
                                                      本篇博客专栏:Linux专栏
                                                      创作时间 :2024年10月10日
https://img-blog.csdnimg.cn/direct/9efbcbc3d25747719da38c01b3fa9b4f.gif​

https://img-blog.csdnimg.cn/direct/b2e39275a06843c3b6c48d38a97a376e.jpeg​
一、程序地址空间:

1、C/C++中的程序地址空间:

https://i-blog.csdnimg.cn/direct/36cd19b00a0c4eb4bce168424268b23d.png​
在c++中我们了解了这样的空间分布图。
   我们应怎样去创建和访问变量呢?
本质就是:起始地址+偏移量(实在我们的变量类型就是偏移量)
https://i-blog.csdnimg.cn/direct/f2c027b2d7334b568937b132f2198bbc.png​
   但是上面这些实在不是内存!!!
我们下面来做一个小实验!!!
   #include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int g_val = 0;
int main()
{
   pid_t id = fork();
   if(id < 0)
   {
         perror("fork");
         return 0;
   }
   else if(id == 0)   
   {
         //child
         printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);
   }
   else
   {   
         //parent
         printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);
   }

   sleep(1);
   return 0;
}https://i-blog.csdnimg.cn/direct/95aef79f17ad4abe9b09da3fb8054c29.png​
   这里我们可以看到上面这两个输出的变量和地址是雷同的,这就说明子进程是按照父进程的模板得到的,父进程没有将代码进行修改,但是只要将代码轻微改一下。
    #include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int g_val = 0;
int main()
{
   pid_t id = fork();
   if(id < 0)
   {
         perror("fork");
         return 0;
   }
   else if(id == 0)   
   {
         //child
         g_val = 100;
         printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);
   }
   else
   {   
         //parent
         printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);
   }

   sleep(1);
   return 0;
}https://i-blog.csdnimg.cn/direct/ea0de9a3b1de49ff94ecc8377fc30964.png​
   这里我们发现,两个进程的地址雷同,但是对应的变量的内容竟然差别,这是为什么呢?
    我们可以得出以下的结论:
变量的内容不一样,父子进程绝对不是输出的同一个变量
但地址值是一样的,说明这个地址绝对不是物理地址
在Linux下,这种地址叫做假造地址
我们在C/C++中看到的地址,全部都是假造地址,用户是看不到物理地址的,用OS同一管理
OS负责将假造地址转换为物理地址
2、进程地址空间:

https://i-blog.csdnimg.cn/direct/fe4161ee23cc48c0a90ed074482a025b.png​
   上面的图就可以说明问题,同一个变量,地址雷同,实在是假造地址雷同,内容差别实在是被映射到了差别的物理地址。 
    那么我们应该怎样去理解假造地址雷同而物理地址差别的问题呢?


[*]父进程有自己的假造地址,也有自己的页表
[*]子进程被创建时,父进程会将自己的页表也给到子进程
[*]但是子进程在改变数据时,会发生写时拷贝,物理地址改变了,但是假造地址没有改变
https://i-blog.csdnimg.cn/direct/862f8a96df2442fe86c85392ef86b887.png​

什么是地址空间?什么是区域划分?

我们在创建进程的时候不仅要有 pcb,也要管理地址空间(先形貌,在组织),有一个 struct mm_struct 的结构体。
https://i-blog.csdnimg.cn/direct/b4c3aa4c0efc4ebd8a1d66ea62b18159.png​

为啥要有地址空间?

   1、让进程以同一的视角对待内存,通过假造地址加页表,可以将乱序的内存变为有序,分门别类的规划好,乱序---->有序
2、存在假造地址空间,可以有效地进行进程访问内存的安全查抄
我们怎样去理解 存在假造地址空间,可以有效的进行进程内存的安全查抄呢?
我提一个问题,我们 常量区的变量 为啥不能修改呢?
我们页表中除了有映射外,另有权限的限制,当进程要修改常量区的变量时,直接在页表就没有权限。

 地址空间的增补

   每个进程都有自己的页表。
二、进程创建:

1.作者主页

   在linux中fork函数时非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。
https://i-blog.csdnimg.cn/direct/738b606d5dee46c3b54269adf19597c8.png​
   进程调用fork,当控制转移到内核中的fork代码后,内核做:
https://i-blog.csdnimg.cn/direct/a4c9c3f55a774a0d9bd9455b83cee723.png​
   当一个进程调用fork之后,就有两个二进制代码雷同的进程。而且它们都运行到雷同的地方。但每个进程都将可以开始它们自己的路程,看如下程序: 
    #include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
   printf("Before: %d\n", getpid());

   pid_t id = fork();
   if(id < 0)
   {
         perror("fork");
         return 0;
   }
   else if(id == 0)   
   {
         //child
         printf("child: %d \n", getpid());
         sleep(2);
   }
   else
   {   
         //parent
         printf("parent: %d \n", getpid());
         sleep(2);
   }

   
   return 0;
}https://i-blog.csdnimg.cn/direct/49448599f8e941519f9daeaf7611791c.png​
   从效果中可看出,fork之前父进程独立执行,之后父子进程分别执行,执行先后由调理器完全决定
    此中,默认环境下,父子进程共享代码,但是数据各有一份(但是如果父子进程只对数据进行读取,不须要私有)
程序=代码(逻辑)+数据
代码共享:所有进程共享代码,不外一般都是fork执行之后,为啥代码是共享的,由于代码不可以修改,所以是共享的
为啥各自的数据要私有一份呢,由于进程之间具有独立性,数据是很多的,且不是所有的数据都要全部拷贝,把本来可以在后面拷贝,甚至不要拷贝的数据,都拷贝了,就比力浪费时间和空间,所以拷贝的过程不是立马做的,而是写时拷贝!!!
2、写时拷贝

https://i-blog.csdnimg.cn/direct/1a6255b44b844ae59053bcf74705651c.png​
通常,父子代码共享,父子再不写入时,数据也是共享的,当恣意一方试图写入,便以写时拷贝的方式各自一份副本。具体见下图:
https://i-blog.csdnimg.cn/direct/7d15e7687e8a4e28befcc38d51a08378.png​
   父子进程代码共享,数据独有:当恣意一方试图写入,便以写时拷贝的方式拷贝一份副本
3.fork通例用法

   

[*]一个父进程希望复制自己,使父子进程同时执行差别的代码段。比方,父进程等待客户端哀求,天生子进程来处置处罚哀求。
[*]一个进程要执行一个差别的程序。比方子进程从fork返回后,调用exec函数。
4.fork调用失败的原因

   
[*]系统中有太多的进程。
[*]现实用户的进程数超过了限制。
三、进程终止

1.进程终止的概念

https://i-blog.csdnimg.cn/direct/add51d2ccdf74f60810a876da3e39d9b.png​
   main 函数的返回值可以被父进程获取的,用来判定子进程的干活的环境 。
https://i-blog.csdnimg.cn/direct/4e07cfe56b144c009b3379f815d7f3a7.png​
   检察上一个进程的退出码 
echo $?
https://i-blog.csdnimg.cn/direct/6d0d36c576da4b128decc779122e5f64.png​
   我们父进程就可以通过这两个数字来判定子进程的退出环境 。
代码非常终止,退出码就没有意义了!!!
 2.进程常见退出方法

https://i-blog.csdnimg.cn/direct/c90ed73f52e646b0a622a455aff65a7b.png​

3._exit函数 

   
[*]_exit函数 是系统调用函数。
[*]_exit函数 在终止进程的时候,不会主动革新缓冲区。
https://i-blog.csdnimg.cn/direct/d046cc96c9ba44c99a6e1e1922093c9e.png​

4.exit函数

   
[*]exit函数 是库函数。
[*]exit函数 在终止进程的时候,会主动革新缓冲区。
https://i-blog.csdnimg.cn/direct/c34b2f803a714114ae51502f720b1902.png​
   exit最后也会调用_exit, 但在调用_exit之前,还做了其他工作:

[*]执行用户通过 atexit或on_exit定义的清理函数。
[*]关闭所有打开的流,所有的缓存数据均被写入。
[*]调用_exit。
https://i-blog.csdnimg.cn/direct/7e65de00ad20412b92535ea19edd7ad1.png​
四、进程等待

1.什么是进程等待

   通过 wait/waitpid 的方式,让父进程(一般环境)对子进程进行资源回收等待过程!!!
   
之前讲过,子进程退出,父进程如果不管不顾,就可能造成‘僵尸进程’的问题,进而造成内存走漏。
另外,进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill -9 也无能为力,由于谁也没有办法杀死一个已经死去的进程。
最后,父进程派给子进程的任务完成的怎样,我们须要知道。如,子进程运行完成,效果对还是不对,或者是否正常退出。
父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息。
2.进程程序更换原理 

https://i-blog.csdnimg.cn/direct/cfda2394eaa24f9696f71cb3a2a85d4a.png
   用fork创建子进程后执行的是和父进程雷同的程序(但有可能执行差别的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序更换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。
    我们就可以表明 进程程序更换后另有一个printf没有执行,为啥呢?
https://i-blog.csdnimg.cn/direct/770f133939f542bb989606c40b8b4da3.png
 3.进程程序更换的函数

https://i-blog.csdnimg.cn/direct/a4b660293aa447d380527c109ad9ade7.png
https://i-blog.csdnimg.cn/direct/e875bf3462054e4092a305fb484dd22b.png



最后:

非常感谢你可以耐着性子把它读完和我可以坚持写到这里,送几句话,对你,也对我:
1.一个冷知识:
屏蔽力是一个人最顶级的本领,任何斲丧你的人和事,多看一眼都是你的不对。
2.你不用变得很外向,内向挺好的,但须要你发言的时候,一定要大胆。
正所谓:君子可内敛不可脆弱,面不公可起而论之。
3.成年人的世界,只筛选,不教育。
4.自律不是6点起床,7点准时学习,而是不管别人怎么说怎么看,你也会坚持去做,绝不打乱自己的节奏,是一种自我的恒心。
5.你开始炫耀自己,往往都是劫难的开始,就像老子在《道德经》里写到:光而不耀,静水流深。
最后如果以为我写的还不错,请不要忘记点赞✌,收藏✌,加关注✌哦(。・ω・。)
愿我们一起加油,奔向更优美的未来,愿我们从懵懵懂懂的一枚菜鸟渐渐成为大佬。加油,为自己点赞!


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 【Linux】————进程控制