进程的基本概念:
进程是一个程序的一次执行的过程。它和程序是有本质区别的,程序是静态的,它是一些保存在磁盘上的指令的有序集合,没有任何执行的概念;而进程是一个动态的概念,它是程序执行的过程,包括了动态创建、调度和消亡的整个过程。它是程序执行和资源管理的最小单位。当一个程序开始执行后,在执行开始到执行完毕退出这段时间里,它在内存中的部分就被称作一个进程。因此,对系统而言,当用户在系统中键入命令执行一个程序的时候,它将启动一个进程。
进程的组成:
1.程序是描述进程功能的可执行机器指令,它通常作为一个静态文件存储在计算机系统的硬盘等存储设备之中。
2.数据是进程的操作对象,同一段程序在不同数据集上的执行过程是不同的进程。
3. 进程控制块(Process Control Block,PCB)是操作系统对进程的一个标识。它采用数据结构的形式表示。在这个数据结构中主要保存着进程的标识符(PID)(范围在0~32767的一个非负整数)、父进程的标识符(PPID)、启动进程的用户ID(UID)和所归属的组(GID)、进程当前的状态(运行R、休眠S、僵尸Z等)、进程的优先级以及进程的资源占用情况等信息。系统通过进程控制块描述和控制进程。在 Linux 中,进程控制块中的每一项都是一task_struct结构,它是在 include/linux/sched.h 中定义的。
进程结构:
Linux 中的进程包含 3 个段,分别为“数据段” 、 “代码段”和“堆栈段” 。
- “数据段”存放的是全局变量、常数以及动态数据分配的数据空间(如 malloc 函数
取得的空间)等。
- “代码段”存放的是程序代码的数据。
- “堆栈段”存放的是子程序的返回地址、子程序的参数以及程序的局部变量。
进程状态:
进程是程序的执行过程,根据它的生命期可以划分成 3 种状态。
- 执行态:该进程正在,即进程正在占用 CPU。
- ·就绪态:进程已经具备执行的一切条件,正在等待分配 CPU 的处理时间片。
- 等待态:进程不能使用 CPU,若等待事件发生则可将其唤醒。
它们之间转换的关系图如图:
进程调度:
原因:
在多进程并发的环境里,从概念上看,有多个进程在同时执行,具体到单个CPU,实际上任何时刻只能有一个进程处于执行状态;因此OS(Operating system)需要决定哪个进程执行,哪些进程等待,也就是进程的调度。
概念:
按一定算法,从一组待运行的进程中选出一个来占有CPU运行。
(书P218)
调度方式:
- 抢占式
非抢占式
进程控制编程:
获取ID:#include <sys/types.h>
#include <unistd.h>
获取本进程ID
pid_t getpid(void)
获取父进程ID
pid_t getppid(void)
1.// 1get.c
2.进程创建fork()
#include <unistd.h>
pid_t fork(void)
功能:创建子进程
fork的奇妙之处在于它被调用一次,却返回两次,它可能有三种不同的返回值:
1、在父进程中,fork返回新创建的子进程的PID;
2、在子进程中,fork返回0;
3、如果出现错误,fork返回一个负值
// 2fork.c
fork的复制功能:调用fork函数后,所创建的子进程是父进程的一个副本,它完全复制了父进程的所有资源。这样得到的子进程独立于父进程,具有良好的并发性,但这个过程的系统开销比较大,而且并不是在所有的情况下都是必须需要所有这些资源。
Linux系统中采用了写时拷贝(copy-on-write)的技术(P223)。创建进程时并不复制整个进程的地址空间,而是让子进程和父进程共享同一个地址空间,只用在需要写入的时候才进行地址空间的复制。也就是说,复制过程是在需要写入的时候才会进行,在此之前,以只读方式共享。通过这种方式,推迟甚至可以避免数据的拷贝。
http://blog.sina.com.cn/s/blog_6f8e766f0102wexo.html
// 2fork1.c
- 说明:当fork()顺利完成任务时,就会存在两个进程,每个进程都从fork()返回处开始继续执行。
- 两个进程执行相同的代码(text)段,但是有各自的堆栈(stack)段、数据(data)段以及堆(heap)。
- 子进程的stack、data、heap segments是从父进程拷贝过来的。
fork()之后,哪一个进程先执行(scheduled to use the CPU)不确定。但是由于子进程创建好后通常会被其他程序占用不需要使用父进程资源。为了减少子进程对父进程的复制,通常情况下程序
程序先进入子进程运行。
思考:
连续调用三次fork()函数 当前程序产生了多少新的进程?