定义
狭义:进程是占用内存空间的正在运行的程序
广义:进程一个具有一定独立功能的程序关于某个数据集合的一次运行活动,它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。
主要有以下两点
1.进程是一个实体。每一个进程都有它自己的地址空间,一般情况下,包括保存全局变量的数据区域、向malloc等函数的动态分配提供空间的堆区域、函数运行时使用的栈区域。
2.进程是一个“执行中的程序”。程序是一个没有生命的实体,只有处理器赋予程序生命时(操作系统执行之),它才能成为一个活动的实体,称其为进程。
特征
动态性:进程的实质是程序在多道程序系统中的一次执行过程,进程是动态产生,动态消亡的。
并发性:任何进程都可以同其他进程一起并发执行
独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位;
异步性:由于进程间的相互制约,使进程具有执行的间断性,即进程按各自独立的、不可预知的速度向前推进
结构特征:进程由程序、数据和进程控制块三部分组成。
多个不同的进程可以包含相同的程序:一个程序在不同的数据集里就构成不同的进程,能得到不同的结果;但是执行过程中,程序不能发生改变。
切换
进行进程切换就是从正在运行的进程中收回处理器,然后再使待运行进程来占用处理器。
例如运行程序前需要将相应进程信息读入内存,如果运行进程A后需要紧接着运行进程B,就应该将进程A相关信息移出内存,并读入进程B相关信息。
在切换时,一个进程存储在处理器各寄存器中的中间数据叫做进程的上下文,所以进程的 切换实质上就是被中止运行进程与待运行进程上下文的切换。在进程未占用处理器时,进程的上下文是存储在进程的私有堆栈中的。
状态
进程执行时的间断性,决定了进程可能具有多种状态。事实上,运行中的进程可能具有以下三种基本状态。
1) 就绪状态(Ready):
进程已获得除处理器外的所需资源,等待分配处理器资源;只要分配了处理器进程就可执行。就绪进程可以按多个优先级来划分队列。例如,当一个进程由于时间片用完而进入就绪状态时,排入低优先级队列;当进程由I/O操作完成而进入就绪状态时,排入高优先级队列。
2) 运行状态(Running):
进程占用处理器资源;处于此状态的进程的数目小于等于处理器的数目。在没有其他进程可以执行时(如所有进程都在阻塞状态),通常会自动执行系统的空闲进程。
3) 阻塞状态(Blocked):
由于进程等待某种条件(如I/O操作或进程同步),在条件满足之前无法继续执行。该事件发生前即使把处理器资源分配给该进程,也无法运行。
创建进程
进程ID:无论进程是如何创建的,所有进程都会从操作系统分配到ID。此ID称为“进程ID”,其值为大于2的整数
fork函数创建进程:
2.子进程:fork函数返回0
僵尸进程:进程完成工作后应被销毁,但有时这些进程将变成僵尸进程,占用系统的重要资源。这种状态下的进程称作"僵尸进程"。
调用fork函数产生子进程的终止方式:
1.传递参数并调用exit函数
2.main函数中执行return语句并返回值
向exit函数传递的参数值和main函数的return语句返回的值都会传递给操作系统。而操作系统不会销毁子进程,直到把这些值传递给产生该子进程的父进程。处在这种状态下的进程就是僵尸进程。
僵尸进程被销毁方式: 向创建子进程的父进程传递子进程的exit参数值或return语句的返回值
销毁僵尸进程
1.利用wait函数
为了销毁子进程,父进程应主动请求获取子进程的返回值。
2.WEXITSTATUS返回子进程的返回值
调用wait函数时,如果没有已终止的子进程,那么程序将阻塞直到有子进程终止。
2.利用waitpid函数
wait函数会引起程序阻塞,还可以考虑waitpid函数。
pid---等待终止的目标子进程ID,若传递-1,则与wait函数相同,可以等待任意子进程终止
statloc---与wait函数的statloc参数具有相同含义
狭义:进程是占用内存空间的正在运行的程序
广义:进程一个具有一定独立功能的程序关于某个数据集合的一次运行活动,它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。
主要有以下两点
1.进程是一个实体。每一个进程都有它自己的地址空间,一般情况下,包括保存全局变量的数据区域、向malloc等函数的动态分配提供空间的堆区域、函数运行时使用的栈区域。
2.进程是一个“执行中的程序”。程序是一个没有生命的实体,只有处理器赋予程序生命时(操作系统执行之),它才能成为一个活动的实体,称其为进程。
特征
动态性:进程的实质是程序在多道程序系统中的一次执行过程,进程是动态产生,动态消亡的。
并发性:任何进程都可以同其他进程一起并发执行
独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位;
异步性:由于进程间的相互制约,使进程具有执行的间断性,即进程按各自独立的、不可预知的速度向前推进
结构特征:进程由程序、数据和进程控制块三部分组成。
多个不同的进程可以包含相同的程序:一个程序在不同的数据集里就构成不同的进程,能得到不同的结果;但是执行过程中,程序不能发生改变。
切换
进行进程切换就是从正在运行的进程中收回处理器,然后再使待运行进程来占用处理器。
例如运行程序前需要将相应进程信息读入内存,如果运行进程A后需要紧接着运行进程B,就应该将进程A相关信息移出内存,并读入进程B相关信息。
在切换时,一个进程存储在处理器各寄存器中的中间数据叫做进程的上下文,所以进程的 切换实质上就是被中止运行进程与待运行进程上下文的切换。在进程未占用处理器时,进程的上下文是存储在进程的私有堆栈中的。
状态
进程执行时的间断性,决定了进程可能具有多种状态。事实上,运行中的进程可能具有以下三种基本状态。
1) 就绪状态(Ready):
进程已获得除处理器外的所需资源,等待分配处理器资源;只要分配了处理器进程就可执行。就绪进程可以按多个优先级来划分队列。例如,当一个进程由于时间片用完而进入就绪状态时,排入低优先级队列;当进程由I/O操作完成而进入就绪状态时,排入高优先级队列。
2) 运行状态(Running):
进程占用处理器资源;处于此状态的进程的数目小于等于处理器的数目。在没有其他进程可以执行时(如所有进程都在阻塞状态),通常会自动执行系统的空闲进程。
3) 阻塞状态(Blocked):
由于进程等待某种条件(如I/O操作或进程同步),在条件满足之前无法继续执行。该事件发生前即使把处理器资源分配给该进程,也无法运行。
创建进程
进程ID:无论进程是如何创建的,所有进程都会从操作系统分配到ID。此ID称为“进程ID”,其值为大于2的整数
fork函数创建进程:
#include<unistd.h>
pid_t fork(void);
成功时返回进程ID,失败时返回-1
fork函数将创建调用的进程的副本,并非根据完全不同的程序创建进程,而是复制正在运行的。调用fork函数的进程。两个进程都将执行fork函数调用后的语句,但因为通过同一个进程、复制相同的内存空间,之后的程序流根据fork函数的返回值加以区分:
1.父进程:fork函数返回子进程ID2.子进程:fork函数返回0
#include<stdio.h>
#include<unistd.h>
int gval=10;
int main()
{
pid_t pid;
int lval=20;
gval++,lval++;
pid=fork(); //创建子进程,父进程的pid中存有子进程的ID,子进程的pid是0
if(pid==0){ //子进程执行
gval+=2,lval+=2;
printf("I am a child \n");
}
else{ //父进程执行
gval-=2,lval-=2;
printf("I am a parent \n");
}
if(pid==0)
printf("Child Proc:[%d,%d] \n",gval,lval);
else
printf("Parent Proc:[%d,%d] \n",gval,lval);
return 0;
}
僵尸进程:进程完成工作后应被销毁,但有时这些进程将变成僵尸进程,占用系统的重要资源。这种状态下的进程称作"僵尸进程"。
调用fork函数产生子进程的终止方式:
1.传递参数并调用exit函数
2.main函数中执行return语句并返回值
向exit函数传递的参数值和main函数的return语句返回的值都会传递给操作系统。而操作系统不会销毁子进程,直到把这些值传递给产生该子进程的父进程。处在这种状态下的进程就是僵尸进程。
僵尸进程被销毁方式: 向创建子进程的父进程传递子进程的exit参数值或return语句的返回值
销毁僵尸进程
1.利用wait函数
为了销毁子进程,父进程应主动请求获取子进程的返回值。
#include<sys/wait.h>
pid_t wait(int *statloc)
成功时返回终止的子进程ID,失败时返回-1
调用此函数时如果已有子进程终止,那么子进程终止时传递的返回值(exit函数的参数值、main函数的return返回值)将保存到该函数的参数所指内存空间。但函数参数指向的单元中还包含其他信息,因此需要通过下列宏进行分离。
1.WIFEXITED子进程正常终止时返回“真”(true)2.WEXITSTATUS返回子进程的返回值
调用wait函数时,如果没有已终止的子进程,那么程序将阻塞直到有子进程终止。
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>
int main()
{
int status;
pid_t pid=fork();
if(pid==0)return 3;
else{
printf("Child PID:%d \n",pid);
pid=fork();
if(pid==0)exit(7);
else{
printf("Child PID:%d \n",pid);
wait(&status); //调用wait函数,之前终止的子进程相关信息将保存到status变量,同时相关子进程被完全销毁
if(WIFEXITED(status))
printf("Child send one:%d \n",WEXITSTATUS(status));
wait(&status);
if(WIFEXITED(status))
printf("Child send two:%d \n",WEXITSTATUS(status));
sleep(30);
}
}
return 0;
}
2.利用waitpid函数
wait函数会引起程序阻塞,还可以考虑waitpid函数。
#include<sys/wait.h>
pid_t waitpid(pid_t pid,int *statloc,int options)
成功时返回终止的子进程ID,失败时返回-1
pid---等待终止的目标子进程ID,若传递-1,则与wait函数相同,可以等待任意子进程终止
statloc---与wait函数的statloc参数具有相同含义
options---传递头文件sys/wait.h中声明的产量WNOHANG,即使没有终止的子进程也不会进入阻塞状态,而是返回0并退出函数
#include<stdio.h>
#include<unistd.h>
#include<sys/wait.h>
int main()
{
int status;
pid_t pid=fork();
if(pid==0){
sleep(15);
return 24;
}
else{
while(!waitpid(-1,&status,WNOHANG)){ //while循环中调用waitpid函数,向第三个参数传递WNOHANG,因此,若之前没有终止的子进程将返回0
sleep(3);
puts("sleep 3sec.");
}
if(WIFEXITED(status))
printf("child send %d \n",WEXITSTATUS(status));
}
return 0;
}