【Linux】进程的结构,创建,结束,以及程序转化为的进程的过程
本文内容:
1.进程的结构
2.程序转化为进程的过程
3.进程的创建
4.进程的结束
背景知识:
1.进程是计算机中处于运行的程序的实体
2.进程是线程的容器
3.程序本身只是指令,数据以及组织形式的描述,进程才是程序真正的运行实例
4.多个进程可以与同一个程序关联,而每个进程则是以同步或者异步的方式独立运行
一.Linux的进程结构
Linux进程结构由三部分组成:代码段,数据段,堆栈段
代码段:存放程序代码,如果多个进程运行同一个程序则他们使用同一个代码段
数据段:存放程序的全局变量,常量,静态变量
堆栈段:函数的参数,函数内部定义的局部变量,进程控制块PCB(处于进程核心堆栈的底部)
ps:
1.PCB是进程存在的唯一标识,系统通过PCB的存在而感知进程的存在
2.系统通过PCB对进程进行调度和管理,PCB包括创建进程,执行程序,退出进程以及改变进程优先级等
3.进程与PID进程标识符是一对一关系,而与程序文件之间是多对一关系!
二.程序转化为进程过程
Linux程序的生成分为四个阶段:预编译,编译,汇编,链接
ps:编译器G++经过预编译,编译,汇编三个步骤将源程序文件转化为目标文件,如果程序有多个目标文件或者程序使用了库函数,则编译器还需要将所有的目标wen就链接起来,最后形成可执行程序
程序转换为进程的步骤:
1)内核将程序代码和数据读入内存,为程序分配内存空间
2)内核为进程分配进程标识符PID和其他资源
3)内核为进程保存PID以及相应的状态信息,把进程放到运行队列中等待执行,程序转化为进程后就可以被操作系统的调度程序调度执行了
三.进程的创建
背景知识:
1.进程创建有两种方式:由操作系统创建,由父进程创建
2.系统启动时,操作系统会创建一些进程,他们承担着管理和分配系统资源的任务,这些进程通常被叫做系统进程
3.系统允许一个进程创建子进程,从而形成进程树结构
4.整个Linux系统的所有进程也是一个树形结构、
5.除了0号进程是由系统创建的,其他进程都是由他们的父进程创建的
关于进程的创建函数fork:
pid_t fork(void)
1.对于父进程,fork函数返回子进程的PID
2.对于子进程,fork函数返回0
3.如果创建出错,则fork函数返回-1
函数分析:fork函数创建一个新进程,并从内核中为进程分配一个新的可用的进程标识符PID,然后将父进程空间中的内核复制到子进程,包括父进程的数据段和堆栈段,和父进程共享代码段,这个时候子进程和父进程一模一样!
问题:为什么对于不同的进程(父进程,子进程),fork函数的返回值会不一样呢?
因为在复制时复制了进程的堆栈段,所以两个进程都停留在fork函数中,等待返回,因此fork函数会返回两次,为了方便区别父进程和子进程,所以返回值不一样
#include <iostream>
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<semaphore.h>
using namespace std; int main()
{
pid_t pid;
pid=fork();
if(pid<0)
{
cout<<"fork error"<<endl;
exit(-1);//abnormal exit
}
else if(pid==0)
{
cout<<"son process,son:"<<getpid()<<",parent:"<<getppid()<<endl;
}
else
{
cout<<"parent process,parent:"<<getpid()<<"son:"<<pid<<endl;
sleep(2);
}
return 0;
}

分析:getpid为获得当前进程的pid,getppid为获得当前进程的父进程的pid,上述代码验证了fork的不同返回值
下面我们验证一下父进程和子进程只共享了代码段,而没有共享数据段和堆栈段
#include <iostream>
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<semaphore.h>
using namespace std; int data_x=1; int main()
{
pid_t pid;
int stack_x=1;
int *heap=(int*)malloc(sizeof(int));
*heap=3; pid=fork(); if(pid<0)
{
cout<<"fork error"<<endl;
exit(-1);
}else if(pid==0)
{
data_x++;
stack_x++;
(*heap)++;
cout<<"son,data_x="<<data_x<<",stack_x="<<stack_x<<",heap="<<*heap<<endl;
exit(0);
}else
{
sleep(2);
cout<<"parent,data_x="<<data_x<<",stack_x="<<stack_x<<",heap="<<*heap<<endl;
}
return 0;
}

分析:我们发现数据段,栈中,堆中的数据,两个进程的这些数据都是不一样的,证明父进程和子进程没有共享数据段和堆栈段!,对子进程中数据段和堆栈段中内容的修改,并不会影响父进程中的数据,父子进程共享代码段的目的是节省存储空间
父进程的资源大部分被子进程复制,只有小部分是不同的,比如pid,该进程的父进程号等这些东西
关于“写时复制”概念的说明:
现在的Linux内核在实现fork函数时往往在创建子进程时并不立即复制父进程的数据段和堆栈段,而是当子进程修改这些数据内容时复制操作才会发生,内核才会给子进程分配进程空间,将父进程的内容复制过来,然后继续后面的操作,这样的实现对一些为了复制自身完成一些工作的进程来说更为合理!,效率也更高
四.进程的结束:
Linux中分为进程正常退出和进程异常退出
1)正常退出的方式:main函数中return 0,调用exit函数,调用_exit函数
2)异常退出的方式:调用abort函数,进程收到某个信号而该信号会使进程终止
当然,不管哪一种方式,系统最终都会执行一段相同的代码:用来关闭进程打开的文件描述符,释放其锁占用的内存资源
需要区别的是,return之后控制器交给了调用函数,而exit是个函数,执行完后系统的控制权交给了系统
现在我们再来看一下_exit函数和exit函数:
_exit函数更为接近底层,exit函数是_exit函数的一个封装,那么exit函数比 _exit函数多做了什么事情呢?
exit函数会进行【读完/写完缓存IO】的操作,而_exit函数则不会,在不恰当的时候使用_exit函数无法保证数据的完整性!
换句话说就是,exit函数在彻底结束进程之前会检查文件的打开情况,把文件缓冲区的内容写回文件!
那调用_exit函数为什么会出现数据不完整的情况呢?我们深究一下Linux底层
在Linux标准函数库中,有一种被称为【缓冲IO】的操作,其特征就是对应每一个打开的文件,在内存中都有一片缓冲区,每次读文件时会连续的读出若干条数据,这样在下次读数时就可以直接从内存的缓冲区中读取,提高了速度,同样的,每次写文件的时候也仅仅是写入内存缓冲区,等满足一定的条件后(积累到一定数量的字符),再将缓冲区中的内容一次性写入文件,这种技术大大增加了文件的读写速度,但是也给编程增添了一点小坑,比如有一些数据,理论上应该写入了文件,但实际上因为没有满足特定的条件,它还知识保存是内存的缓冲区中,如果采用_exit函数直接结束进程,缓冲区的数据就会丢失,因此想要保证数据的完整性,就一定要使用exit函数,而不是_exit函数
【Linux】进程的结构,创建,结束,以及程序转化为的进程的过程的更多相关文章
- 如何在 Azure 中均衡 Linux 虚拟机负载以创建高可用性应用程序
负载均衡通过将传入请求分布到多个虚拟机来提供更高级别的可用性. 本教程介绍了 Azure 负载均衡器的不同组件,这些组件用于分发流量和提供高可用性. 你将学习如何执行以下操作: 创建 Azure 负载 ...
- Linux进程理解与实践(五)细谈守护进程
一. 守护进程及其特性 守护进程最重要的特性是后台运行.在这一点上DOS下的常驻内存程序TSR与之相似.其次,守护进程必须与其运行前的环境隔离开来.这些环境包括未关闭的文件描述符,控制终端, ...
- {Python之进程} 背景知识 什么是进程 进程调度 并发与并行 同步\异步\阻塞\非阻塞 进程的创建与结束 multiprocess模块 进程池和mutiprocess.Poll
Python之进程 进程 本节目录 一 背景知识 二 什么是进程 三 进程调度 四 并发与并行 五 同步\异步\阻塞\非阻塞 六 进程的创建与结束 七 multiprocess模块 八 进程池和mut ...
- ASP.NET Core Linux下为 dotnet 创建守护进程(必备知识)
前言 在上篇文章中介绍了如何在 Docker 容器中部署我们的 asp.net core 应用程序,本篇主要是怎么样为我们在 Linux 或者 macOs 中部署的 dotnet 程序创建一个守护进程 ...
- Linux内核分析——进程描述与创建
20135125陈智威 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 实验内容 ...
- ASP.ENT Core Linux 下 为 donet创建守护进程(转载)
原文地址:http://www.cnblogs.com/savorboard/p/dotnetcore-supervisor.html 前言 在上篇文章中介绍了如何在 Docker 容器中部署我们的 ...
- Java实现Linux下服务器程序的双守护进程
作者:Vinkn 来自http://www.cnblogs.com/Vinkn/ 一.简介 现在的服务器端程序很多都是基于Java开发,针对于Java开发的Socket程序,这样的服务器端上线后出现问 ...
- Linux进程: task_struct结构体成员
一:简介 为了管理进程,内核必须对每个进程所做的事情进行清除的描叙. 比如:内核必须知道进程优先级,他是正在CPU上运行还是因为某些事件被阻塞了,给它分配了什么样的地址空间,允许它访问哪个文件等等.这 ...
- 关于linux的一点好奇心(五):进程线程的创建
一直以来,进程和线程的区别,这种问题一般会被面试官拿来考考面试者,可见这事就不太简单.简单说一点差异是,进程拥有独立的内存资源信息,而线程则共享父进程的资源信息.也就是说线程不拥有内存资源,所以对系统 ...
随机推荐
- 一个Tomcat下部署多个项目异常:org.springframework.jmx.export.UnableToRegisterMBeanException: Unable to register MBean 的解决方法
内容简介 在测试服务器上Tomcat下部署两个Spring boot项目,总是一个能启动成功,另一个启动不成功.这两个war包单独部署均能正常启动. 查看日志:启动时报出 org.springfram ...
- Python爬虫进阶 | 多线程
一.简介 为了提高爬虫程序效率,由于python解释器GIL,导致同一进程中即使有多个线程,实际上也只会有一个线程在运行,但通过request.get发送请求获取响应时有阻塞,所以采用了多线程依然可以 ...
- Log4net 控制台打印日志(二)
1.创建控制台程序 2.用NuGet添加log4net引用 3.添加应用程序配置文件:App.config 4.添加配置信息: <?xml version="1.0" enc ...
- 开源项目 02 HttpLib
using JumpKick.HttpLib; using Newtonsoft.Json; using System; using System.Collections.Generic; using ...
- vue echarts 给双饼图添加点击事件
在用 echarts 画旭双饼图( https://www.echartsjs.com/examples/zh/editor.html?c=pie-nest )的时候,经常会伴随着点击事件 如果想要在 ...
- 洛谷 P1880 [NOI1995]石子合并 题解
P1880 [NOI1995]石子合并 题目描述 在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分. 试 ...
- 计蒜客 39268.Tasks-签到 (The 2019 ACM-ICPC China Shannxi Provincial Programming Contest A.) 2019ICPC西安邀请赛现场赛重现赛
Tasks It's too late now, but you still have too much work to do. There are nn tasks on your list. Th ...
- rust-vmm 学习
V0.1.0 feature base knowledge: Architecture of the Kernel-based Virtual Machine (KVM) 用rust-vmm打造未来的 ...
- 【洛谷P3369】普通平衡树——Splay学习笔记(一)
二叉搜索树(二叉排序树) 概念:一棵树,若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值: 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值: 它的左.右子树也分别为二叉搜索树 ...
- Don't always upset yourself !