当前位置: 首页 > news >正文

Linux8-fork父子进程逻辑地址相同、进程的逻辑地址与物理地址、fork相关例题、僵死进程

一、fork父子进程逻辑地址相同

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>

int main()
{
    char *s=NULL;
    int n=0;//控制父子进程的次数

    //调用fork
    pid_t id=fork();
    assert(id != -1);//fork的返回值,-1表示出错,所以加断言

    if(id == 0)//子进程
    {
        s="child";
        n=3;
    }
    else//父进程
    {
        s="parent";
        n=7;;
    }
    
    //父子进程一起执行
    int i=0;
    for(;i<n;i++)
    {
    	//n的地址=%d  --- 会警告,但能运行 --- 因为将地址转成整形 可用%p,输出地址
    	//注意,在进程中,看到的是进程的逻辑地址,所以父进程和子进程地址显示一样
        printf("s=%s,pid=%d,ppid=%d,n的地址=%d\n",s,getpid(),getppid(),&n);
        sleep(1);

    }
    exit(0);
}

示例运行:

在这里插入图片描述

我们在进程中看到的地址都是逻辑地址;

在这里插入图片描述
父子进程的逻辑地址是一样的,但物理地址是不一样的;

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>

int main()
{
    char *s=NULL;
    int n=0;//控制父子进程的次数

    //调用fork
    pid_t id=fork();
    assert(id != -1);//fork的返回值,-1表示出错,所以加断言

    if(id == 0)//子进程
    {
        s="child";
        n=3;
    }
    else//父进程
    {
        s="parent";
        n=7;;
    }
    
    //父子进程一起执行
    int i=0;
    for(;i<n;i++)
    {
    	//n的地址=%d  --- 会警告,但能运行 --- 因为将地址转成整形 可用%p,输出地址
    	//注意,在进程中,看到的是进程的逻辑地址,所以父进程和子进程地址显示一样
        printf("s=%s,pid=%d,ppid=%d,n的地址=%d\n,n的值=%d",s,getpid(),getppid(),&n,n);
        sleep(1);

    }
    exit(0);
}

在这里插入图片描述

二、进程的逻辑地址与物理地址

在这里插入图片描述
32位系统下的寻址地址空间为2^32 = 4,294,967,296 ~~== 4G;
用户真正能使用的是内核下几个区域,大概为3G,内核占1G;
4G/4k — 物理能有 2^20 个页面,前提是32位系统;

三、fork相关例题

1、打印了几个a?产生了几个进程?

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
    int i=0;
    for(;i<2;i++)
    {
        fork();
        printf("a\n");
    }
    exit(0);
}

运行结果为6个:

在这里插入图片描述
运行结果不一定完全一样,但都是输出六个a;
在这里插入图片描述
最终生成4个进程;
在这里插入图片描述

2、没有’\n’,先printf,在fork

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
    printf("a\n");
    fork();
    
    exit(0);
}

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
    printf("a");
    fork();
    
    exit(0);
}

运行结果:

在这里插入图片描述

3、没有’\n’,循环fork

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
    int i=0;
    for(;i<2;i++)
    {
        fork();
        printf("a");
    }
    exit(0);
}

运行结果:
在这里插入图片描述
在这里插入图片描述
输出8个a;

因为没有\n,所以第一次fork(),打印一个a,但没有打印出来,在缓冲区里;
所以后面fork(),会将缓冲区内本来就有的a保留。

4、‘||’

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
    fork() || fork();
    printf("a\n");
    
    exit(0);
}

运行结果为3个;

在这里插入图片描述
注意:父进程的返回值为子进程的pid+1;大于0;
在这里插入图片描述

5、‘&&’

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
    fork() && fork();
    printf("a\n");
    
    exit(0);
}

运行结果为3个:

在这里插入图片描述
注意:子进程的返回值为0;

在这里插入图片描述

四、僵死进程

1、正常释放结束子进程:

1、进程实体已经被释放;
2、PID内部的exit_code = 0; //进程的退出码,将PID内部的退出码置零;
3、父进程获取退出码;
在这里插入图片描述

2、非正常流程、僵死状态:没有获取退出码

在这里插入图片描述

3、僵死进程产生的原因或者条件:

当子进程先于父进程结束,父进程没有获取子进程的退出码,此时子进程变成僵死进程
;

4、演示僵死进程:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>

int main()
{
    char *s=NULL;
    int n=0;//控制父子进程的次数

    //调用fork
    pid_t id=fork();
    assert(id != -1);//fork的返回值,-1表示出错,所以加断言

    if(id == 0)//子进程
    {
        s="child";
        n=3;
    }
    else//父进程
    {
        s="parent";
        n=7;
    }
    
    //父子进程一起执行
    int i=0;
    for(;i<n;i++)
    {
        printf("s=%s,pid=%d,ppid=%d\n",s,getpid(),getppid());
        sleep(1);

    }
    exit(0);
}

测试运行:

在这里插入图片描述

注意:

./main & — //main为进程名字,'&'表示将进程放在后台运行;
要将进程放在后台运行;这样才能运行ps查看;否则ps不能在进程运行的时候同时运行查看。
ps — 默认显示与当前终端有关的进程信息;
ps -e — 显示系统中所有的进程信息;
ps -f — 显示更多的进程信息;
(子进程结束而父进程未结束的时候才有僵死进程)

5、如何处理、预防僵死进程:

(1)父进程先于子进程结束

比如子进程的n改为7,父进程的n改为3;

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>

int main()
{
    char *s=NULL;
    int n=0;//控制父子进程的次数

    //调用fork
    pid_t id=fork();
    assert(id != -1);//fork的返回值,-1表示出错,所以加断言

    if(id == 0)//子进程
    {
        s="child";
        n=7;
    }
    else//父进程
    {
        s="parent";
        n=3;
    }
    
    //父子进程一起执行
    int i=0;
    for(;i<n;i++)
    {
        printf("s=%s,pid=%d,ppid=%d\n",s,getpid(),getppid());
        sleep(1);

    }
    exit(0);
}

在这里插入图片描述
在这里插入图片描述
注意: 子进程在父进程结束后,子进程的PPID发生改变。有些系统父进程结束后,子进程的ppid为1,这里为1460;当父进程结束后,子进程会变成“孤儿进程”,会被“收养”;

在这里插入图片描述

父进程先于子进程结束,子进程就会被收养,新的"父进程"就会获取退出码;(调用wait实现);

(2)父进程获取退出码

使用 wait
man wait
在这里插入图片描述
pid_t wait(int *wstatus); //返回值是所获取的子进程的pid,参数是将获取的退出码写入这个整型变量中;

    else//父进程 
	{ 
		s="parent"; 
		n=7; 
		int val;
 		wait(&val); 
 		printf("val=%d\n",val);  
	 }

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/wait.h>//wait的头文件

int main()
{
    char *s=NULL;
    int n=0;//控制父子进程的次数

    //调用fork
    pid_t id=fork();
    assert(id != -1);//fork的返回值,-1表示出错,所以加断言

    if(id == 0)//子进程
    {
        s="child";
        n=3;
    }
    /*else//父进程
    {
        s="parent";
        n=7;
    }*/
    else//父进程 
	{ 
		s="parent"; 
		n=7; 
		int val;
 		wait(&val); 
 		printf("val=%d\n",val);  
	 }
    
    //父子进程一起执行
    int i=0;
    for(;i<n;i++)
    {
        printf("s=%s,pid=%d,ppid=%d\n",s,getpid(),getppid());
        sleep(1);

    }
    exit(0);
}

运行结果:
没有僵死进程
在这里插入图片描述
在这里插入图片描述
注意:这个先运行子进程,子进程运行完后才运行父进程;
wait需要获取子程序的退出码,只有当子程序运行结束,才能获取退出码,否则会阻塞,所以先运行子进程;

总结:
其实两种处理僵死进程的方法本质都是一样的,都调用了wait,获取退出码。
但是两种方法又有区别,就是父进程调用wait会阻塞,等子进程执行完之后,父进程才会执行。
一般我们父进程调用wait是配合信号使用的。目前暂时理解为说明父进程调用wait可以避免僵死进程。

相关文章:

  • java毕业设计普通中学体育卫生信息管理系统源码+lw文档+mybatis+系统+mysql数据库+调试
  • 基于C语言的查找算法汇编
  • 多网段多通道IP地址和通讯端口转换
  • 【PyQt】PyQt入门安装和Hello World
  • 怎样创建一个VUE项目(超简单)
  • C++【STL】【queue的使用和模拟实现】【priority_queue的使用和模拟实现】
  • SAP PI PO 接口常见问题处理:在监控器中找不到一个或多个 XI 消息的日志记录
  • L2TP客户端之Strongswan移植(三)
  • matplotlib入门之抛砖引玉
  • java-php-python-springboot携手助学助学交流平台计算机毕业设计
  • Android wifi sniffer log总结分析
  • 山东大学数字图像处理实验(二)
  • linux多个jdk时,java -version显示的版本有错误
  • 【论文笔记】An Image Patch is a Wave: Phase-Aware Vision MLP
  • 【前端升全栈】 五分钟了解Node.js
  • @jsonView过滤属性
  • 78. Subsets
  • ComponentOne 2017 V2版本正式发布
  • EventListener原理
  • laravel 用artisan创建自己的模板
  • Map集合、散列表、红黑树介绍
  • MySQL几个简单SQL的优化
  • oschina
  • 闭包--闭包之tab栏切换(四)
  • 从重复到重用
  • 汉诺塔算法
  • 基于遗传算法的优化问题求解
  • 实现菜单下拉伸展折叠效果demo
  • 使用Tinker来调试Laravel应用程序的数据以及使用Tinker一些总结
  • d²y/dx²; 偏导数问题 请问f1 f2是什么意思
  • ​​​​​​​ubuntu16.04 fastreid训练过程
  • #AngularJS#$sce.trustAsResourceUrl
  • #微信小程序(布局、渲染层基础知识)
  • $con= MySQL有关填空题_2015年计算机二级考试《MySQL》提高练习题(10)
  • ()、[]、{}、(())、[[]]命令替换
  • (1)(1.11) SiK Radio v2(一)
  • (C#)获取字符编码的类
  • (C++17) std算法之执行策略 execution
  • (附源码)spring boot智能服药提醒app 毕业设计 102151
  • (附源码)springboot学生选课系统 毕业设计 612555
  • (六)c52学习之旅-独立按键
  • (三) diretfbrc详解
  • (三)uboot源码分析
  • (四)搭建容器云管理平台笔记—安装ETCD(不使用证书)
  • (一) storm的集群安装与配置
  • (一)VirtualBox安装增强功能
  • (已解决)vue+element-ui实现个人中心,仿照原神
  • (转)Linux下编译安装log4cxx
  • (轉貼) 2008 Altera 亞洲創新大賽 台灣學生成果傲視全球 [照片花絮] (SOC) (News)
  • .net 4.0 A potentially dangerous Request.Form value was detected from the client 的解决方案
  • .NET 5.0正式发布,有什么功能特性(翻译)
  • .Net接口调试与案例
  • .NET平台开源项目速览(15)文档数据库RavenDB-介绍与初体验
  • .NET设计模式(8):适配器模式(Adapter Pattern)
  • .w文件怎么转成html文件,使用pandoc进行Word与Markdown文件转化