进程简单总结

程序是静态的,它是保存在磁盘上的指令的有序集合,没有任何执行的概念 进程是一个动态的概念,它是程序执行的过程,包括了动态创建、调度和销毁的整个过程

并行执行 : 表示多个任务能够同时执行,依赖于物理的支持,比如 cpu 是 8 核芯,则可以同时执行 8 个 任务 

并发执行:在同一时间段有多个任务在同时执行,由操作系统调度算法来实现,比较典型的就是时间片轮转

 

1. 在 Linux 系统中,管理进程使用树型管理方式。

2. 每个进程都需要与其它某一个进程建立父子关系,对应的进程叫做父进程。

3. Linux 系统会为每个进程分配id ,这个id作为当前进程的唯一标识,当进程结束,则会被回收。

4. 进程的id与父进程的id分别通过getpid()与getppid()来获取

进程建立之后,系统则要为这个进程分配相应的空间,32位Linux系统中,会为每个进程分配 4G 的空 间。

4G的进程空间主要分为两部分,高位1G是内核空间,低位3G是用户空间。

stack : 存放非静态的局部变量 heap : 动态申请的内存 .

bss : 未初始化过的全局变量(包括初始化为0的,未初始化过的静态变量(包括初始化为0) .

data : 初始化过并且值不为0的全局变量,初始化过的不为0静态变量 .

rodata : 只读变量(字符串之类) .

text : 程序文本段(包括函数,符号常量)

1. 当用户进程需要通过内核获取资源时,会切换到内核态运行,此时当前进程会使用内核空间的资源

2. 用户需要切换到内核态运行时,主要是通过 系统调用

在程序执行过程中,操作系统会为进程分配4G的虚拟地址空间,因此可以通过下图进行理解

虚拟地址 : 虚拟地址并不代表真实的内存空间,而是一个用于寻址的编号 

直接访问物理地址,会导致地址空间没有隔离,很容易导致数据被修改

通过虚拟地址空间可以实现每个进程空间都是独立的,操作系统会映射到不用的物理地址区间,在 访问时互不干扰

Linux下进程栈的默认大小是8M,可以通过 ulimit -s查看

在Linux下,通过 ulimit -u查看系统的最大进程数

进程的状态管理

1. 三态模型

(1)运行(running)态:进程占有处理器正在运行。

(2)就绪(ready)态:进程具备运行条件,等待系统分配处理器以便运行。

(3)等待(wait)态:又称为阻塞(blocked)态或睡眠(sleep)态,指进程不具备运行条件,正在等待某 个事件的完成

通常,一个进程在创建后将处于就绪状态。每个进程在执行过程中,任意时刻当且仅当处于上述三 种状态之一。同时,在一个进程执行过程中,它的状态将会发生改变。引起进程状态转换的具体原 因如下:

(1)运行态一一等待态:等待使用资源或某事件发生,如等待外设传输;等待人工干预。

(2)等待态一一就绪态:资源得到满足或某事件己经发生,如外设传输结束;人工干预完成。 (3)运行态一一就绪态:运行时间片到,或出现有更高优先权进程。

(4)就绪态一一运行态:CPU空闲时被调度选中一个就绪进程执行

2. 五态模型

在一个实际的系统里,进程的状态及其转换比上面叙述的复杂一些,例如,引入专门的新建态(new) 和终止态(exit )。

(1)NULL一一新建态:执行1个程序,创建一个子进程。

(2)新建态一一就绪态:当操作系统完成了进程创建的必要操作,并且当前系统的性能和内存的容 量均允许。

(3)运行态一一终止态:当1个进程到达了自然结束点,或是出现了无法克服的错误,或是被操作系 统所终结,或是被其他有终止权的进程所终结。

(4)终止态一一NULL:完成善后操作。

经常使用的进程状态:

(1)运行态(TASK_RUNNING) : 此时进程或者正在运行,或者准备运行,就绪或者正在进行都属于 运行态

(2)睡眠态(TASK_SLEEP): 此时进程在等待一个事件的发生或某种系统资源 可中断的睡眠(TASK_INTERRUPT) : 可以被信号唤醒或者等待事件或者资源就绪 不可中断的睡眠(TASK_UNTERRUPT) : 只能等待特定的事件或者资源就绪

(3)停止态(TASK_STOPPED) : 进程暂停接受某种处理。例如:gdb调试断点信息处理。

(4)僵尸态(TASK_ZOMBIE):进程已经结束但是还没有释放进程资源

进程的相关命令

显示所有进程的详细信息 ps -aux

 列出所有的进程,相比 ps -aux 信息要少一些 ps -ef

不显示任何闲置 (idle) 或无用 (zombie) 的进程 top -i

 更数指定次数后,退出top命令 top -n数字

pstree 命令是将所有的进程以树型结构的方式进行展示

kill 命令是用于结束进程的命令或者用于显示相关信号 示例用法 1 2 终止进程 kill -9 进程号

创建进程

通过调用fork()函数,则会产生一个新的进程。调用fork()函数的进程叫做 父进程,产生的新进程则为 子 进程。

通过 fork() 函数创建子进程,有如下特点:

父子进程并发执行,子进程从 fork() 函数之后开始执行

父子进程的执行顺序由操作系统算法决定的,不是由程序本身决定

子进程会拷贝父进程地址空间的内容,包括缓冲区、文件描述符等

文件描述符的拷贝

每个进程都会维护一个文件表项,即文件描述符与文件指针的映射表

在 Linux 内核中有一个 struct file 结构体来管理所有打开的文件

当子进程拷贝了父进程文件描述符后,则会共享文件状态标志与文件偏移量等信息

使用 fork() 函数之后,会创建子进程,fork() 之后的代码会在父子进程中都执行。 如果父子进程执行相同的任务,则正常执行 如果父子进程执行不同的任务,则需要利用 fork() 函数返回值

在创建多个进程时,最主要的原则是由父进程统一创建,统一管理,不能进行递归创建

退出

exit 与 _exit的不同

_exit()属于系统调用,能够使进程停止运行,并释放空间以及销毁内核中的各种数据结构

exit()基于_exit()函数实现,属于库函数, 会自动刷新I/O缓冲区

等待

父进程调用wait()waitpid() 函数等待子进程退出后,释放子进程遗留的资源

进程替换

创建一个进程后,pid 以及在内核中的信息保持不变,但进程所执行的代码进行替换

在 Linux 系统中提供了一组用于进程替换的函数,共有6个

int execl(const char *pathname, const char *arg, .../* (char *) NULL */);

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[]);

进程的通信

Linux下的进程通信手段基本上是从Unix平台上继承而来的

每个进程都有自己独立的地址空间,当两个不同进程需要进行数据交互时,就需要使用进程间通讯 进程间通讯分为单个计算机的进程间通讯和局域网的进程间通讯

进程间通讯方式有 管道,信号,消息队列,共享内存,网络

无名管道

无名管道的特点:

无名管道属于单向通讯 无名管道只能用于父子进程通讯

无名管道发送端叫做写端,接收端叫做读端

无名管道将读端与写端抽象成两个文件进行操作,在无名管道创建成功之后,则会返回将读端与写 端的文件描述符存入数组

创建无名管道需要调用 pipe() 函数

总结:

1. 当管道为空时,读进程会阻塞

2. 当管道的写端被关闭,从管道中读取剩余数据后,read 函数返回0

3. 在写入管道时,确保不超过PIPE_BUF字节的操作是原子的 关于PIPE_BUF 原子操作,可以通过 man 7 pipe, 搜索 PIPE_BUF ,则可以看到相关解释 当写入的数据达到PIPE_BUF字节时,write()会在必要的时候阻塞直到管道中的可用空间足以原子地 完成操作 当写入的数据大于PIPE_BUF字节时,write()会尽可能多传输数据以充满这个管道

4. 管道的大小是有限的,不能让父/子进程同时对管道进行读/写操作

5. 当一个进程试图向管道中写入数据,但没有任何进程拥有该管道的打开着的读取描述符,内核向写 入进程发送一个SIGPIPE信号

有名管道

有名管道是文件系统中可见的文件,但是不占用磁盘空间,仍然在内存中

可以通过 mkfifo命令创建有名管道(在共享目录不能使用mkfifo)

有名管道与无名管道一样,在应用层是基于文件接口进行操作 有名管道用于任意进程之间的通讯,当管道为空时,读进程会阻塞

如果有名管道的一端以只读方式打开,它会阻塞到另一端以写的方式(只写,读写)

如果有名管道的一端以只写方式打开,它会阻塞到另一端以读的方式(只读,读写)

有名管道的优缺点 优点 缺点:

可以实现任意进程间通信,操作起来和文件操作一样

1.打开的时候需要读写一起进行否则就会阻塞,管道大小是 4096个字节

2.半双工的工作模式,如果和多个进程通信则需要创建多个管道

信号

进程在运行过程中,随时可能被各种信号打断

进程可以忽略或者去调用相应的函数去处理信号

进程无法预测信号到达的精准时间

在Linux系统可以通过 kill -l 命令查看信号种类

信号处理流程

信号处理流程包含以下两个方面:

信号的发送 :可以由进程直接发送

信号投递与处理 : 由内核进行投递给具体的进程并处理

  在 Linux 中对信号的处理方式如下

忽略信号,即对信号不做任何处理,但是有两个信号不能忽略:即SIGKILL及SIGSTOP。

捕捉信号,定义信号处理函数,当信号发生时,执行相应的处理函数。 执行缺省操作,Linux对每种信号都规定了默认操作

信号的发送

kill函数:向指定的进程发送一个信号

raise函数:接收信号

在进程没有结束时,进程在任何时间点都可以接受到信号

需要阻塞等待信号时,则可以调用 pause() 函数

处理信号

有三种方式:

忽略 : 不进行处理

默认 : 按照信号的默认方式处理

用户自定义 : 通过用户实现自定义处理函数来处理,由内核来进行调用 每种信号都有相应的默认处理方式

用户自定义

定时器信号

在Linux系统中提供了 alarm 函数,用于设置定时器

定时器的定时任务由内核完成的,alarm 函数负责设置定时时间,并告诉内核启动定时器

当定时时间超时后,内核会向进程发出 SIGALRM 信号

信号量

资源竞争

资源竞争 : 当多个进程同时访问共享资源时,会产生资源竞争,最终最导致数据混乱

临界资源 : 不允许同时有多个进程访问的资源,包括硬件资源(CPU、内存、存储器以及其他外围设 备)与软件资源(共享代码段、共享数据结构)

临界区 : 访问临界资源代码

互斥 : 同一时刻只有一个进程访问临界资源

同步 : 在互斥的基础上增加了进程对临界资源的访问顺序

进程主要的同步与互斥手段是 信号量

信号量

信号量: 由内核维护的整数,其值被限制为大于或等于0

信号量可以执行如下操作: 将信号量设置成一个具体的值

在信号量当前值的基础上加上一个数值 在信号量当前值的基础上减上一个数值

等待信号量的值为 0

一般信号量分为 二值信号量 与 计数信号量

二值信号量:一般指的是信号量的值为1,可以理解为只对应一个资源

计数信号量:一般指的是值大于等于2 ,可以理解为对应多个资源

在 Linux 系统中查询信号量使用 ipcs -s

操作信号量调用 semop 函数

信号量集合调用 semctl 函数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值