Linux进程与进程控制

目录

  • 基本概念
  • 进程结构
  • 进程基本操作

基本概念-进程简介

什么是进程?

​ 进程的概念首先是在20世纪60年代初期有 MIT 的Multics 分时系统和IBM的 TSS/360系统引入的,在40多年的发展中,人们对进程有过各种各样的定义,列举较为著名的几种。

​ 进程是一个独立的可调度的活动(Cohen, Jofferson);

​ 进程是一个抽象的实体,当它执行某个任务时,要分配和释放各种资源。

(P.Denning)

​ 进程是可以并行执行的计算单位(Madnick,Donovan)

本质:进程是程序的一次执行的过程,进程是程序执行和资源管理的最小单元。 在Linux环境下,每个正在运行的程序都称为进程

基本概念-进程控制块

什么是进程控制块(PCB)?

进程是Linux 系统中调度和管理资源的单位,那系统如何描述和表示一个进程的变化呢,答案是通过进程控制块来实现的。

进程控制块包含了那些内容呢?

​ 进程的描述信息(进程ID,进程状态…);

​ 进程控制信息;(调度信息,时间片,优先级…)

​ 进程的资源信息(内存资源,打开的文件,使用的定时器…)

Linux 系统中进程控制块中的每一项都是通过 task_struct 结构体来表示,定义在 #include <linux/sched.h >头文件中的。

基本概念-进程标识

既然进程是Linux 系统中基本的调度和管理资源的单位,那系统就需要能够唯一标识出每一个进程,才能更好的进行调度和管理的工作,Linux 系统是通过进程号(PID)来唯一的标识一个进程,

父进程标识号:当前进程的父进程的标识,(PPID)

PID 和 PPID 都是非零的正整数。

进程的特性

  • 动态性
    • 进程的实质是程序的一次执行过程,进程是动态产生,动态消亡的。
  • 并发性
    • 多个进程可以同一时间运行在一个内存空间中,任何进程都可以同其他进程并发执行。
  • 独立性
    • 虽然在一个内存空间中有多个进程在运行,但其实每个进程都运行在各自的虚拟内存空间中,互不干扰。
  • 异步性
    • 由于进程间的相互制约,使得进程具有执行的间断性,也就是说,进程按各自独立的,不可预知的速度向前推进。
  • 结构特性
    • 每个进程都有自己的私有空间,在这个私有空间中,会涉及四个不同的段落,进程在内存中的结构由代码段,数据段,堆段和栈段四部分组成。

进程结构

  • Linux 是一个多进程的系统,进程之间具有并发性,互不干扰,也就是说,每个进程拥有各自的权利和义务,其中,各个进程都运行在独立的虚拟地址空间,因此,即便是一个进程发生异常,也不会影响到系统中其他的进程。
  • Linux 系统的进程主要包含4个段,分别为 “代码段”,“数据段”,“堆段”,“栈段”

在这里插入图片描述

  • 代码段:存放程序代码
  • 数据段:存放数据
  • 栈段: 存放临时变量
  • 堆段: 存放动态申请变量
  • BSS 数据段:没有初始化的全局变量
  • 只读数据段:初始化的全局变量,静态变量,常量

进程模式和类型

  • Linux 系统中,进程执行模式分为用户模式和内核模式,如果当前运行的是用户程序,应用程序或者非内核的系统程序,那么进程就运行在用户模式下,如果用户程序在执行过程中出现系统调用或者发生中断事件,那么就要运行系统程序,进程模式就变为内核模式,内核模式下的进程可以执行机器的特权指令,此时运行的进程不受到用户的干扰,即使是 root 用户也不能干扰内核模式下进程的运行。
  • 在这里插入图片描述

进程的状态

进程有4种基本状态:

  • 运行状态 (RUNNING)

    • 进程正在执行。
  • 就绪 (Ready)

    • 进程已经获取到除处理器外的所有资源,等待分配处理器资源,只要分配到处理器资源就可以运行。一般就绪进程是会有一个就绪进程队列,有系统来调度。
  • 等待(阻塞Blocked)

    • 进程因等待某个事件发生而暂停运行。
  • 停止 (stop)

    • 进程已经完成任务。

进程的状态转化

  • 运行----> 等待
    • 正在运行的进程因等待某事件发生,转入等待状态。
  • 等待—>就绪
    • 处于等待状态的进程,因所等待的事件发生了,进入就绪对队列让系统调度运行。

进程与程序

​ 进程不等同于程序。程序是一些存在磁盘上的一个没有生命的指令集合,是个静态的概念,没有任何执行的概念,而进程是动态的概念。它是程序执行的过程,包括了动态创建、调度和消亡的整个过程。进程是程序执行和资源管理的最小单位。

​ 对系统而言,当用户在各级系统中键入命令执行一个程序的时候,它将启动一个进程,因此,一个程序可以对应多个进程。

进程的基本操作

进程创建
pid_t fork(void);
pid_t vfork(void);
获取进程ID
uid_t getpid(void);
uid_t getppid(void);
进程等待
pid_t wait(int *status);
pid_t waitpid(pid_t pid,int *status, int options);
进程退出
void exit(int status);
void _exit(int status);
进程运行新程序
int execl(const char *path,const char *arg,….);
int execlp(const char *file,const char *arg,….);
int execle(const char *path,const char *arg,….,char *const envp[ ]);
int execv(const char *path, char *const argv[ ]);
int execvp(const char *file, char *const argv[ ]);
int execve(const char *filename, char *const argv[ ],char *const envp[ ]);

1.进程创建

函数名fork
头文件#include <unistd.h>
#include <sys/types.h>
函数原型pid_t fork(void);
功能创建子进程
参数说明
返回值fork 函数的奇妙之处在于调用一次,返回两次,可能有3种不同的返回值:

父进程中,fork返回新创建的子进程的PID
子进程中,fork返回0
出错:返回 -1,并将错误码存入 errno 中

fork函数

​ fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,一个进程调用fork()函数后,就会创建一个新的进程,系统先给新的进程分配资源,然后把原来的进程的所有值都复制到新的新进程中,包括数据空间、堆、栈和进程描述符等,只有少数值与原来的进程的值不同(比如原进程的定时器,操作信号)。相当于克隆了一个自己,子进程从fork函数的返回值处开始执行。

​ 创建新进程成功后,系统中出现两个基本完全相同的进程,这两个进程执行没有固定的先后顺序,哪个进程先执行要看系统的进程调度策略。

函数名vfork
头文件#include <unistd.h>
#include <sys/types.h>
函数原型pid_t vfork(void);
功能创建子进程
参数说明
返回值父进程中,fork返回新创建的子进程的PID
子进程中,fork返回0
出错:返回 -1,并将错误码存入 errno 中
与fork的区别1.fork: 子进程拷贝父进程的数据段,代码段
vfork: 子进程与父进程共享数据段和堆栈空间,所以子进程对变量修改和父进程会同步。详细的查看手册
2.fork: 子进程和父进程运行次序不固定,由系统调度决定
vfork: 子进程会先于父进程运行

2.进程退出

函数名exit和_exit
头文件#include <unistd.h>
#include <stdlib.h>
函数原型void exit(int status);
void _exit(int status);
功能退出,或终止进程
参数说明status:退出码/退出的状态值
返回值
两者之间的差别exit相对于_exit 会多一个缓冲区清理的工作

在这里插入图片描述

exit 和return 区别

void Test()
{
  printf(“Hello\n”);
  return ;
}
void Test()
{
  printf(“Hello\n”);
  exit(EXIT_SUCCESS) ;
}
int main()
{
   while(1)
  {
      Test();
      sleep(1);
    }
   return 0;
}

上述代码中:exit退出整个进程,return 退出当前函数

3.获取进程ID

函数名getpid,getppid
头文件#include <unistd.h>
#include <sys/types.h>
函数原型pid_t getpid(void);
pid_t getppid(void);
功能getpid:获取当前进程ID
getppid:获取当前进程父进程ID
参数说明
返回值getpid:返回当前进程ID
getppid:返回当前进程父进程ID
注意:该两个函数调用不会出错。

4.进程等待

函数名wait
头文件#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
函数原型pid_t wait(int *status);
功能阻塞父进程,直到某个子进程退出
参数说明status :获取到的子进程结束状态值,获取到status后用 WEXITSTATUS(status) 来转换子进程退出时的退出码
返回值成功:返回退出的子进程的ID
错误:返回 -1

说明:wait 是waitpid 的一个特例
函数名waitpid
头文件#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
函数原型pid_t waitpid(pid_t pid, int *status, int options);
功能阻塞调用进程,直到某个子进程退出
参数说明Pid: 等待的子进程ID;
status :获取到的子进程结束状态值;
options:附加选项 (详见 手册说明)
返回值成功:返回退出的子进程的ID
错误:返回 -1

5.进程运行新程序

​ exec函数族的作用是根据指定的文件名找到可执行文件,并用它来取代调用进程的内容,换句话说,就是在调用进程内部执行一个可执行文件。

​ 一个进程调用exec族函数后,代码段,数据段,堆段,栈段的都会被新程序覆盖,唯一保留的是PID(进程号)

​ 函数名与参数的关系:

​ 细看一下,这6个函数都是以exec开头(表示属于exec函数组),前3个函数接着字母l的,后3个接着字母v的,我的理解是l表示list(列举参数),v表示vector(参数向量表)

​ 字母p是指在环境变量PATH的目录里去查找要执行的可执行文件。2个以p结尾的函数execlp和execvp,看起来,和execl与execv的差别很小,事实也如此,它们的区别从第一个参数名可以看出:除 execlp和execvp之外的4个函数都要求,它们的第1个参数path必须是一个完整的路径,如"/bin/ls";而execlp和execvp 的第1个参数file可以仅仅只是一个文件名,如"ls",这两个函数可以自动到环境变量PATH指定的目录里去查找。

​ 字母e是指给可执行文件指定环境变量。在全部6个函数中,只有execle和execve使用了char *envp[]传递环境变量,其它的4个函数都没有这个参数,这并不意味着它们不传递环境变量,这4个函数将把默认的环境变量不做任何修改地传给被执行的应用程序。而execle和execve用指定的环境变量去替代默认的那些。

函数名execl
头文件#include <unistd.h>
函数原型int execl(const char *path,const char *arg,….);
int execlp(const char *file,const char *arg,….);
int execle(const char *path,const char *arg,….,char *const envp[ ]);
功能用path或者file所指定的进程地址空间替换调用进程的地址空间
参数说明Path: 执行程序的全路径;
file :执行程序的名称;
arg…:被执行程序所需的命令行参数,含程序名。以空指针结束 .
返回值-1:错误发生 并记录错误码 errno
注意:该函数只有在错误发生的时候才返回。
函数名execv
头文件#include <unistd.h>
函数原型int execv(const char *path, char *const argv[ ]);
int execvp(const char *file, char *const argv[ ]);
int execve(const char *path, char *const argv[ ],char *const envp[ ]);
功能用path或者file所指定的进程地址空间替换调用进程的地址空间
参数说明Path: 执行程序的全路径;
file :执行程序的名称;
argv :被执行程序所需的
返回值-1:错误发生 并记录错误码 errno
注意:该函数只有在错误发生的时候才返回。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值