一.进程概念:
程序的一个执行实例
正在执行的程序
能分配处理器并由处理器执行的实体
这三种说法都是对的
那么正式进入进程的认识吧
内核:担当分配和管理系统资源(CPU时间,内存)的实体
管理的两个步骤:描述,组织
操作系统对进程进行管理,都是对结构体数据(PCB(进程描述符,是一个结构体,包含了进程的所有描述信息))进行管理
进程与可执行程序的区别:
1>进程是动态的,进程状态是可以改变的;程序是静态的,处于存储状态
2>进程在内存上,程序在硬盘上
3>进程比程序多了很多的描述信息,其中最重要的是PCB(进程控制块),PCB经常改变
进程控制块:是task_struck结构体,这个结构体具体的实现我会之后写到,其中进程控制块包含了标识符,状态,优先级,程序计数器,内存指针,上下文数据,I/O状态信息,记账信息 等!
*1>标识符:描述本进程的唯一标识符,用来区别其他进程
*2>状态:任务状态,退出代码,退出信号等
*3>优先级:相对于其他进程的优先级
*4>程序计数器:程序中即将被执行的下一条指令的地址。
5>内存指针:包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
*6>上下文数据:进程执行时处理器的寄存器中的数据
7>I/O状态信息:包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表
8>记账信息:包括处理器时间总和,使用的时钟数总和,时间限制,记账号等
所有运行在系统里的进程都以task_struck链表的形式存在内核里
二.操作系统中的进程
对于操作系统来说,一个任务就是一个进程(Process),比如打开一个浏览器就是启动一个浏览器进程,打开一个记事本就启动了一个记事本进程,打开两个记事本就启动了两个记事本进程,打开一个Word就启动了一个Word进程。
有些进程还不止同时干一件事,比如Word,它可以同时进行打字、拼写检查、打印等事情。在一个进程内部,要同时干多件事,就需要同时运行多个“子任务”,我们把进程内的这些“子任务”称为线程(Thread)。
三.操作系统:是一款做软件与硬件管理的软件
操作系统与硬件之间有一个层次叫做驱动
硬件<-驱动<-操作系统(核心层)<-外壳<-用户(最外层到最底层)
对操作系统而言,对下要管理各种软件与硬件,对上而言,要提供各种使用方式对于用户
内核:对下
外壳:对上
四.系统调用:调用的是一个个的函数,是操作系统提供的接口(操作系统给的一个个函数接口,用来进行各种操作)
库函数是基于系统调用的二次开发,节省开发成本,增减代码移植性
操作系统内核的四大要点
进程管理,内存管理,设备管理,文件管理
这里我们不做过多叙述,这里只是一个概念描述
五.介绍一些指令和进程的位置
ps aux指令:显示所有进程
ps aux | grep “目标进程”:显示所要查询的进程信息
getpid() 获得系统调用的进程id,返回值为pid_t类型,是一个被封装过的类型,为一个无符号整数
getppid() 获得系统调用的父进程id,同样返回一个无符号整数
Linux下创建 子进程(PCB) 的方法
fork指令:有两个返回值,创建子进程,成功给子进程返回0,给父进程返回子进程的pid;失败的时候给父进内容程返回-1;
解释为什么要给父进程返回子进程的pid:因为父进程的子进程不止有一个,所以这里给父进程返回子进程的pid,是为了让父进程 区别这是哪个子进程,但每个子进程只会有一个父进程。
子进程 :fork()创建的子进程与父进程的代码是一样的(用的就是父进程的数据结构模板),但数据是各自一份,子进程的数据是私有的,以写时拷贝创建的。
Linux下c语言中的地址空间是虚拟地址,看下面代码,创建的变量i,用fork创建子进程,子进程与父进程里变量i的值不同但是i的地址是相同的,&i直接打印出来的地址其实是虚拟地址,但是i的输出是从物理地址中取得的 ,所以才会出现同一个虚拟地址中的打印的值不同,因为他们的物理地址不同。
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{
int i = 0;
pid_t id = fork();
if(id==-1)
{
printf("fork error\n");
}
else if(id==0) //child
{
i = 10;
printf("this is child\n");
printf("i = %d ",i);
printf("&i:%p\n",&i);
printf("child pid is:%d father pid is:%d\n",getpid(),getppid());
}
else
{
i = 100;
printf("this is father\n");
printf("i = %d ",i);
printf("&i:%p\n",&i);
printf("father pid is:%d \n",getpid());
}
return 0;
}
运行结果:
并且我们可以看见同一个变量i在子进程和父进程中的地址是相同的,但是值却不同,这在我们通常的认知中这简直是不可能的,这里我们就引入了一个概念叫做虚拟地址,也就是代码中变量所指向的地址其实是一个虚拟地址,但是每一个虚拟地址通过页表映射转换为物理内存,也就是真正的的存储空间,来让我们看看这是一个怎样的过程
父进程与子进程共享代码:经过页表同时只想一块物理地址
父进程与子进程私有数据:经过页表指向不同的物理地址