Linux进程

注:该文章提及的函数请查看man手册来查找具体用法、头文件与返回值,文章里只描述部分功能

一、进程概述

程序是有序指令的合集,而程序的执行过程就是进程

进程是一个独立的可调度的活动,由操作系统进行统一调度,相应的任务会被调度到 cpu 中进行执行。

进程一旦产生,则需要分配相关资源,同时进程是资源分配的最小单位

作用:可以使多个任务同时进行,提高cpu运行效率。

二、进程的内存分配和虚拟地址

Linux在程序运行后为进程分配的空间如下图(Linux系统分配的4g内存是虚拟地址):
在这里插入图片描述
的特点是先进后出 (FILO) ,可以用来存储函数的调用栈、内存的分配操作、表达式求值的临时变量以及与程序中的控制流相关的数据,还是一种高效的内存结构,用于存放基础数据类型和引用类型的变量,大大简化内存的管理,提高了程序的执行效率。

则为程序提供了极为灵活的空间分配和管理手段,既可以手动管理,也可以交由垃圾回收机制自动管理,带来了很大的方便。

进程的4g虚拟空间可以通过下图理解:
在这里插入图片描述

虚拟地址:并不是一个真实的内存地址,而是一个寻址的编号
物理地址:是真实的内存地址
物理地址和虚拟地址通过cpu的硬件内存管理单元MMU来实现虚拟内存和物理内存的映射
(操作系统可以设置 MMU 中的映射内存段)

linux系统运用虚拟地址作用
1.直接访问物理地址,会导致地址空间没有隔离,很容易导致数据被修改
2.通过虚拟地址空间可以实现每个进程空间都是独立的,操作系统会映射到不用的物理地址区间,在
访问时互不干扰.

三、进程的相关命令

ps:显示进程的相关信息

显示所有进程的详细信息:ps -aux
与-aux相似,只是信息要少一些:ps -ef
可以配合管道符使用:例:使用 ps -ef | grep “可执行文件名” 查找进程

top:实时显示进程的信息

常用选项:
[ -i ] 不显示任何闲置 (idle) 或无用 (zombie) 的进程
[ -n 数字 ] 更数指定次数后,退出top命令

pstree:将所有的进程以树型结构的方式进行展示
kill 命令是用于结束进程的命令或者用于显示相关信号

结束进程:kill -9 进程id

四、并行与并发

并行:硬件上增加cpu的内核,增加cpu的核心数,这样cpu可以在不同的内核上同时执行程序
并发:在同一时间段有多个任务在同时执行,由操作系统调度算法来实现,比较典型的就是时间片轮转(如下图)
在这里插入图片描述
解释:我们给时间分成很短暂的时间片,由于每段时间cpu都只能执行一个任务,那么我们在每段时间片后切换执行的任务,这样在宏观感知上就像是在同时在执行几个任务。

五、进程的状态周期(生命周期)

进程是程序的执行过程,即程序从建立、运行到终止释放的过程,这样一个循环就是进程的生命周期

在三态模型中,进程状态分为三个基本状态,即运行态,就绪态,阻塞态。

运行(running)态:进程占有处理器正在运行。
就绪(ready)态:进程具备运行条件,等待系统分配处理器以便运行。
等待(wait)态:又称为阻塞(blocked)态或睡眠(sleep)态,指进程不具备运行条件,正在等待某个事件的完成。
三态模型如下图:
在这里插入图片描述

在五态模型中,进程分为新建态、终止态,运行态,就绪态,阻塞态

NULL一一>新建态:执行1个程序,创建一个子进程。
新建态一一>就绪态:当操作系统完成了进程创建的必要操作,并且当前系统的性能和内存的容
量均允许。
运行态一一>终止态:当1个进程到达了自然结束点,或是出现了无法克服的错误,或是被操作系
统所终结,或是被其他有终止权的进程所终结。
终止态一一>NULL:完成善后操作。

常用的状态:
1.运行态(TASK_RUNNING) : 此时进程或者正在运行,或者准备运行,就绪或者正在进行都属于运行态
2.睡眠态(TASK_SLEEP): 此时进程在等待一个事件的发生或某种系统资源
1) 可中断的睡眠(TASK_INTERRUPT) : 可以被信号唤醒或者等待事件或者资源就绪
2) 不可中断的睡眠(TASK_UNTERRUPT) : 只能等待特定的事件或者资源就绪
3.停止态(TASK_STOPPED) : 进程暂停接受某种处理。例如:gdb调试断点信息处理。
4.僵尸态(TASK_ZOMBIE):进程已经结束但是还没有释放进程资源

六、Linux的进程管理

Linux的进程管理系统是树形结构,每个进程都要有它的父进程
(通过pstree命令可以将所有进程都以树形结构显示出来)

而每个进程都会被分配一个唯一的标识符,即为进程id

获取进程id的函数:
pid_t getpid(void);(返回当前进程的id)
pid_t getppid(void);(返回当前进程的父进程id)

pid_t 实际上是 int 类型

-------------------------------------------------以下章节是进程的具体实现-------------------------------------------------

七、创建进程与多个子进程

1.用fork()函数来创建子进程
2.创建的进程都只能由父进程来管理不允许进行递归创建,即子进程里不允许创建进程

3.fork函数往下的程序就是子进程的程序,但也是父进程的程序

4.如果要对子进程或父进程添加独立的程序,就要使用fork()的返回值来进行
5.父子进程的执行顺序由操作系统算法决定的,不是由程序本身决定
6.子进程会拷贝父进程地址空间的内容,包括缓冲区、文件描述符等

(对文件操作时文件描述符是作为操作文件的唯一标识,也就是说文件操作时的文件偏移量和文件状态都被子进程复制了)

fork函数:
函数原型:pid_t fork(void);
返回值:
成功:返回给父进程是子进程的pid,返回给子进程的是0
失败:返回-1,并设置errno

演示父子进程的使用并添加独立的程序:例:

在这里插入图片描述
代码运行结果:
在这里插入图片描述
如上可知:父子都打印了hello
并且可发现问题:hello为什么有时会打印在命令输入行上?
这是因为当父进程比子进程先结束,所以父进程完成,界面回到命令输入之后子进程才打印。

创建多个子程序

在这里插入图片描述

代码运行结果:
在这里插入图片描述

七、进程的退出(exit函数与_exit函数)

在进程结束时,需要释放分配给进程的地址空间以及内核中产生的各种数据结构,才能真正退出

exit与_exit函数就能实现上述功能

exit

原型:void exit(int status);

参数status是退出的状态,可以自己设定,而linux系统中有两个已设定的状态值:
EXIT_SUCCESS 正常退出,EXIT_FAILURE 异常退出,具体定义在stdlib.h 中

#define EXIT_FAILURE 1 // Failing exit status.
#define EXIT_SUCCESS 0 // Successful exit status.

在退出的时候还会刷新缓冲区

_exit

参数与返回值都与exit一样,大体功能也相似,但是并不会刷新缓冲区

两者还有一个区别,exit是库函数中的,属于用户空间,而_exit是系统调用,是内核空间
exit是基于_exit函数实现的

八、进程的等待(wait,waitpid)

子进程在结束退出(使用exit或_exit)的时候会进入僵死状态,其在内核中的数据结构还保留着
这时父进程调用就可以等待子进程结束并且释放这些保留的资源

wait
父进程阻塞等待子进程退出并释放子进程遗留资源

原型:pid_t wait(int *wstatus);

参数 wstatus的作用是保存子进程的退出状态,通过调用系统内部定义的宏函数可以查看退出的子进程的各种状态(宏函数及使用可以通过Man手册查询)
返回值:退出的子程序的pid(进程号)

waitpid
作用与wait相似但比它更强大,可理解为wait底层调用waitpid

原型:pid_t waitpid(pid_t pid, int *wstatus, int options);

参数
pid:进程id
-1 可以等待任意子进程
>0 等待id为pid的进程

wstatus:保存子进程退出状态值的指针

options选项:WNOHANG —非阻塞选项;0 —阻塞式与wait等同

函数返回值
>0 退出进程的pid
=0 在非阻塞模式下,没有进程退出
-1 调用失败,并设置errno

waitpid使用阻塞的方式等待任意子进程退出: waitpid(-1,&status,0);
waitpid使用非阻塞的方式等待子进程退出: while((cpid=waitpid(-1,&status,WNOHANG))==0);

九、进程替换(execl函数族)

:创建的pid进程在内核的信息不变,替换到别的代码运行

通过execl函数族实现

最基本的execl函数:
原型:int execl(const char *pathname, const char arg, …/ (char *) NULL */);
用法如下图:主进程替换到 /bin/ls文件来运行,参数列表为:“ls” “-l” (NULL为结束标志)
在这里插入图片描述
其他的execl函数族函数如下:
int execlp(const char *file, const char arg, …/ (char *) NULL */);
int execle(const char *pathname, const char arg, …/, (char *) NULL, char
*const envp[] */);
int execv(const char *pathname, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],char *const envp[]);

参数:
path:可执行文件的路径名
file:可执行文件名,可以通过PATH环境变量指定的路径
arg:参数列表,以NULL结尾
argv[]:参数数组
envp[]:环境变量数组

例:
使用execlp实现同上功能如下图:
在这里插入图片描述

execv:
在这里插入图片描述
结尾:linux进程的具体实现先到这,如要了解进程之间的通信请进入本人主页查看后面的文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值