进程控制编程

一. 为什么需要多进程,为何需要并发
并发技术:同一时间同时执行多条任务的技术,我们需要更强大的功能,提供更多的服务,所以需要并发

  1. 程序与进程
    (1)程序:硬盘上的一个可执行文件
    (2)在Linux中,当一个程序开始执行后,在开始执行到执行完毕退出这段时间里,它在内存中的部分称作一个进程。
    (3)程序是动态的,进程是静态的(不可迁移);进程是暂时的,程序是长久的。

  2. 进程
    (1)Linux是一个多任务的操作系统,但是常用的单CPU计算机实际上在一个时间片断内只能执行一条命令,那Linux是怎么实现的呢?原来Linux使用了“进程调度”的手段达到了“伪并行”,内核通过为每个进程指派一定的运行时间,这个时间通常很短,短到以毫秒为单位,然后依照某种规则,在众多进程中挑选一个投入运行,其他进程暂时等待,当正在运行的那个进程时间耗尽,或执行完毕退出,或因为某种原因暂停,Linux就会重新进行调度。因为每个进程占用的时间片很短,在我们使用者来看,就好像多个进程同时运行一样。
    (2)每个进程在创建时都会被分配一个数据结构,称为进程控制块PCB
    (3)进程分类:交互进程、批处理进程、守护进程
    (4)进程属性:
    1)进程ID(PID),也称为进程标识符,是一个非负的整数,每个进程的进程ID不同,用来区分进程
    2)父进程和父进程的ID(PPID)
    (5)父进程和子进程:是管理与被管理的关系

二. Linux进程管理

  1. ps监视进程工具
    ps -elf:查看进程命令
    ps -elf | grep test:查看指定进程命令

  2. kill终止进程的工具
    (1)一个程序已经死掉,但又不能退出,这时就可以使用kill
    (2)在涉及数据库服务器程序的父进程,不能用这些工具杀死,因为这些工具在强行终止时,会让数据库产生更多的文件碎片,当碎片达到一定程度时,数据库就有崩溃的危险;当然对于占用资源过多的数据库子进程,我们应该用kill杀掉
    (3)1号进程一定不能死(1号进程是祖先进程)

  3. top监视系统任务的工具:和ps相比,top动态监视 系统任务,输出结果也是连续的

  4. 其他工具
    ctrl z:切换到后台(终端)
    ctrl c:杀死进程
    fg:切换回前台
    which + 命令:查找命令路径
    ./test &:运行时在后面加取地址符,直接跑到后台运行

  5. 进程的优先级:nice和renice
    (1)在Linux中,进程之间是竞争资源(如CPU和内存的占用)的关系
    (2)负值或0表示最高优先级
    (3)硬件技术发展迅速,大多情况下,不必设置进程的优先级,除非在进程失控而疯狂占用资源的情况下

三. Linux进程的三态
1.就绪态:进程已分配到除CPU以外所有必要的资源,只要获得处理器便可立即执行(等待分配CPU的时间片)

2.执行态:进程已经获得处理器,程序正在处理器上执行

3.阻塞状态:正在执行的进程,由于等待某个事件发生而无法执行时,便放弃处理器而处于阻塞状态。引起进程阻塞的事件有多种,如等待I/O完成、申请缓冲区不能满足、等待信号等

4.三态转换
在这里插入图片描述
四. Linux进程结构
1.Linux的一个进程在内存里有三部分数据:数据段、堆栈段、代码段
(1)代码段:存放程序代码
(2)堆栈段:存放子程序的返回地址、子程序的参数、程序的局部变量
(3)数据段:存放程序的全局变量、常数、动态数据分配的数据空间(如用malloc之类的函数取得的空间)

2.系统如果同时运行数个相同的程序,他们之间不能使用同一个堆栈段和数据段

五. Linux进程控制块PCB
1.由结构task_struct所定义的数据结构,系统中实际存在的进程数由其定义的全局变量nr_task来动态记录

2.PCB包含许多信息,包括进程ID等,供系统调度和进程本身执行使用

3.每个PCB约占1KB的内存空间

六. 进程调度
1.调度的目标
(1)程序使用CPU的三种模式:IO密集型、计算密集型、平衡型
1)IO密集型:响应时间很重要
2)计算密集型:CPU的周转时间重要
3)平衡型:响应和周转的平衡最重要
(2)CPU的调度就是要达到极小化平均响应时间、极大化系统吞吐率、保持系统各个功能部件均处于繁忙状态和提供某种公平的机制
(3)对实时系统来说,调度的目标就是要达到截止时间前完成所应该完成的任务和提供性能的预测性

2.调度算法
从最早的先来先服务调度算法到短进程优先调度算法,最后是现在的高优先级优先调度算法+时间片轮转法

3.进程调度方式:抢占式(主流)、非抢占式

4.优先级反转
(1)优先级反转是指一个低优先级的任务持有一个被高优先级任务需要的共享资源,高优先任务由于资源缺乏而处于受阻状态,一直等到低优先级任务释放资源为止
(2)解决方案
1)设置优先级上限:给临界区一个高优先级,进入临界区的进程都将获得这个高优先级,如果其他试图进入临界区的进程的优先级都低于这个高优先级,就不会发生优先级反转(临界区:进程中访问临界资源的那段程序代码成为临界区;临界资源:一次只允许一个进程访问的资源成为临界资源;进程互斥:若干进程使用某一共享资源时,任何时刻最多允许一个进程使用,我们可以通过互斥锁,信号量等工具来实现互斥功能;死锁:多个进程因竞争资源形成僵局,若无外力作用,这些进程将永远不再向前推进。

2)优先级继承:当一个高优先级进程等待一个低优先级进程持有的资源时,低优先级进程暂时获得高优先级进程的优先级别。在释放共享资源后,低优先级进程回到原来的优先级别。VxWorks嵌入式系统就是采用这种策略

进程互斥:若干进程使用某一共享资源时,任何时刻最多允许一个进程使用(我们可以通过互斥锁,信号量等工具来实现互斥功能)死锁:多个进程因竞争资源形成僵局,若无外力作用,这些进程将永远不再向前推进。

七. 进程创建
Linux主要提供了fork、vfork、clone三种进程创建方法。在Linux源码中,这三个调用的过程是执行fork()、vfork()、clone()时,通过一个系统调用表映射到sys_fork()、sys_vfork()、sys_clone(),然后在这三个函数中取调用do_fork()去做具体的创建进程工作

1.获取进程
在这里插入图片描述
2.启动进程:fork():用于创建子进程
在这里插入图片描述
(1) fork创建进程,子进程完全复制父进程的资源,这样得出的子进程独立于父进程,具有良好的并发性,但是二者之间的通信需要专门的通信机制。
(2)fork是一个开销非常大的系统调用,这些开销并不是所有的情况下都是必须的,所以现在的Linux中是采取了copy-on-write(COW写时复制)技术,为了降低开销,fork最初并不会真的产生两个不同的复制,因为在那个时候,大量的数据其实是完全一样的。写时复制是在推迟真正的数据复制。若后来确实发生了写入,那意味着父进程和子进程的数据不一致了,于是产生复制动作,每个进程拿到属于自己的那一份,这样就可以降低系统调用的开销
(3)在fork()之后,子进程和父进程都会继续执行fork()调用之后的指令
(4) 注意和vfork()区分。所以本题中对子进程操作不影响父进程的num值,对父进程操作不影响子进程里面的num值

3.启动进程:vfork():用于建立一个新的进程
在这里插入图片描述
(1)vfork()创建的子进程和父进程共享地址空间
(2)vfork()创建的子进程必须显示调用exit()来结束,否则子进程将不能结束,而fork()不存在这个情况
(3)fork() PK vfork()
1)fork:子进程拷贝父进程的数据
vfork:子进程和父进程共享数据
2)fork:父子进程的执行顺序不确定
vfork:子进程先运行,父进程后运行

4.启动进程:exec函数族(6个函数)
在这里插入图片描述
(1)exec函数族的作用是在调用进程内部执行一个可执行文件,调用成功不会返回,调用失败返回-1,进程ID不变
(2)execl()函数用于执行文件,第一个参数:被执行程序名,一定包含完整路径,后面的参数最后以空指针NULL结束
(3)execv()函数参数中用数组名代替的一定不要加双引号
(4)在子进程中exec函数族后的程序不会执行

5.启动进程:system
例如system函数用于执行shell命令:system(“clear”)实现清屏作用

八.进程等待
1.僵尸进程与孤儿进程
(1)僵尸进程:指那些已经终止的进程,但仍然保留一些信息,即还没有从进程表中删除,记载该进程的退出状态,等待其他进程收集。僵尸进程太多会导致进程表里面数目满了,进而导致系统崩溃,倒是不占用系统资源。僵尸进程等待其父进程为其收尸(如果一个进程终止时自己回收所以分配给他的资源,系统就不会产生僵尸进程)
(2)僵尸进程产生过程:子进程运行结束后,它立即从内存中移除,但是进程描述符仍然保留在内存中(进程描述符占据极少的内存空间),子进程向父进程发送SIGHHLD信号,此时父进程调用wait()清除子进程信息
(3)孤儿进程:父进程先死于子进程,子进程此时就变成了孤儿进程(父进程变成了祖先进程)
(4)如果父进程没有安装SIGCHLD信号处理函数调用wait等待子进程结束,又没有显式忽略该信号,那么它就一直保持僵尸状态;如果这时父进程结束了,那么init进程会自动接手这个子进程,为他收尸,他还是能被清除的。但是如果父进程是一个循环,不会结束,那么子进程就会一直保持僵尸状态。
(5)用top命令查看僵尸进程,zombie前面的数量就是僵尸进程的数量;用ps命令,最后defunct的标记,就表明是僵尸进程

2.如何避免僵尸进程
(1)父进程通过wait和waitpid等函数等待子进程结束,这会导致父进程挂起
(2)fork两次

3.wait函数:等待子进程中断或结束
在这里插入图片描述
在这里插入图片描述
(1)子进程结束状态值会由参数statu返回,wait执行成功返回子进程识别码PID
(2)wait功能:阻塞进程,直到有信号或者子进程结束

九.进程退出
1.在Linux中,进出退出分为正常退出和异常退出
(1)正常退出
1)在main()函数中执行return
2)调用exit()函数
3)调用_exit()函数
(2)异常退出
1)调用about函数
2)进出收到某个信号,而该信号使程序终止
不管哪种退出方式,系统最终都会执行内核中的同一代码,这段代码用来关闭进程所有已经打开的文件描述符,释放它所占用的内存和其他资源

2.exit()与_exit()的区别:exit()在停止进程前会检查文件的打开情况,并把文件缓冲区中的内容写回文件才停止进程。由于Linux的标准函数库中,有一种被称作“缓冲I/O”的操作,其特征是对应每一个打开的文件,在内存中都有一片缓冲区。在每次读写文件时,会连续的读出若干条记录,这样在下次读文件时就可以直接从内存的缓冲区读取;同样,在每次写文件时也仅仅是写入内存的缓冲区,等满足了一定的条件,如达到了一定数量或遇到特定字符等,再将缓冲区的内容一次性写入文件。这种情况下,有一些数据,认为以及写入的文件,实际上因为没有满足特定的条件,他们还只是保存在缓冲区内,这时用_exit()函数直接关闭进程,缓冲区的数据就会丢失。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值