在说进程之前,我们先来说说冯诺依曼体系结构。
像我们常见得计算机,或者不常见的计算机,如服务器等。都是遵循冯诺依曼体系结的。
下图为冯诺依曼体系结构:
对于冯诺依曼,要注意几点:
1.此处的存储器是指内存。
2.不考虑缓存对的情况,这里的CPU只能对内存进行读写,不能访问外设。
3.外设要输入或者输出数据,也只能写入内存或者从内存中读取。
操作系统:
操作系统包括:
1.内核。如:内存管理,文件管理,进程管理。
2.其他程序。如:库,shell程序。
操作系统特点:
1.一款搞“管理”的软件。操作系统在内核中,描述被管理对象,组织被管理对象。
2.操作系统就是在管理计算机的软硬件资源。
3.用struct来描述被管理的资源,用双向链表来组织被管理的资源。
在看进程之前,我们应该知道,操作系统管理进程方式—先把进程描述起来,在把进程组织起来。
进程
概念:
课本观点:程序的一个执行实例。
内核观点:进程是操作系统分配资源的最小单位。
通过系统调用(fork)创建进程
int main()
{
int ret=fork();
if(ret<0)
perror("fork");
else if(re==0)
{//子进程}
else
//父进程
}
1.fork函数返回值。
(1.)返回值大于0,说明是父进程,返回值为子进程的pid。
(2.)返回值等于0,说明是子进程。
(3.)返回值小于0,说明创建进程失败。
2.父子进程代码共享,是因为子进程通过写时拷贝来拷贝父进程的地址空间。
描述进程----PCB
概念:
1.用PCB(进程控制块)来描述进程,PCB里面放的是进程的信息。
2.在Linux系统下的PCB是task_struct,是描述进程的结构体。
3.task_struct是Linux内核的一种数据结构,其会被装载到内存中并且包含进程信息。
task_struct里内容的分类:
1.标识符
进程标识符—pid,也称之为进程号,当一个程序运行起来后,操作系统会在/proc目录下创建一个以pid命名的文件夹,保存进程的信息。并且进程号是唯一的。
如何在代码中获取进程pid?
获取子进程标识符:getpid();
获取父进程标识符:getppid();
2.状态
查看进程状态:ps aux
进程的状态。进程存在以下几种状态
R(running):运行状态。并不代表着进程一定在运行中,它表明进程要么在运行中要么在运行队列中。
S(sleeping):睡眠状态。表明进程在等待时间的完成。
T(stopped):停止状态。通过发送SIGSTOP信号来停止这个进程。
X(dead):死亡状态。只是一个返回状态。
Z:僵尸状态。当进程退出时,其父进程在占用CPU导致没有回收进程的资源(没有读取到子进程退出的返回代码),就会产生僵尸进程。总而言之,子进程退出,其父进程还在运行,导致父进程没有回收子进程的资源,子进程就会进入僵尸状态。
僵尸进程的危害:
a.造成内存泄漏。
孤儿进程:当父进程先退出,子进程退出后资源无法归还,此时子进程称为孤儿进程。这时1号进程将会回收(领养)孤儿进程,以免造成内存泄漏。
3.优先级
CPU资源分配的先后顺序,优先级高的进程有优先执行的权利。
查看系统进程:ps -l
我们可以更改nice的值:
命令行输入top后,按r,随后输入进程的pid,最后输入nice值。
4.程序计数器
程序中即将被执行的吓一跳指令的地址。
5.内存指针
6.上下文数据
程序执行时,寄存器中的数据。
7.I/O状态信息
显示的I/O请求,分配给进程的I/O设备,而被进程使用文件列表。
8.记账信息
CPU使用时间的总和。
并行:每一个进程在同一时间,都会占用一个CPU来进行运算。
并发:多个进程在使用同一个CPU的时候,通过操作系统调度,来决定哪个进程可以独占CPU一阵子。
程序地址空间:
先来看一段代码:
1 #include<stdio.h>
2 #include<sys/types.h>
3 #include<stdlib.h>
4 #include<unistd.h>
5 int g_val=0;
6 int main()
7 {
8 pid_t ret=fork();
9 if(ret<0)
10 perror("fork");
11 else if(ret==0)//子进程
12 {
13 g_val=10;
14 printf("child:[%d]:%d:%p\n",getpid(),g_val,&g_val);
15 }
16 else{//父进程
17 sleep(2);
18 printf("parent:[%d]:%d:%p\n",getpid(),g_val,&g_val);
19 }
20 return 0;
21 }
上述程序,子进程先运行完,修改变量。最后让父进程读取变量内容。按我们所学的知识来说,父子进程中变量值应该都被修改了。但是看结果:
之前我们知道子进程是将父进程的地址空间作为模板拷贝的,所以子进程与父进程中变量的地址和值一样是肯定的。但是通过结果,我们发现父子进程中变量地址确实一样,但是值却不同,父进程的值并没有修改,子进程的值修改了。得出以下结论:
1.由于父子进程中内容不同,说明二者绝对不是同一变量。
2.但地址是一样的,说明该地址绝对不是物理地址。在Linux中这种地址叫虚拟地址。所以子进程是拷贝了父进程的虚拟地址空间。
3.我们之前在C/C++所有看到的地址,都是虚拟地址。物理地址我们一般看不见。
所以之前说的程序地址空间是不准确的,准确的说应该是进程地址空间。