前言
这几年一直在it行业里摸爬滚打,一路走来,不少总结了一些python行业里的高频面试,看到大部分初入行的新鲜血液,还在为各样的面试题答案或收录有各种困难问题
于是乎,我自己开发了一款面试宝典,希望能帮到大家,也希望有更多的Python新人真正加入从事到这个行业里,让python火不只是停留在广告上。
微信小程序搜索:Python面试宝典
或可关注原创个人博客:https://lienze.tech
也可关注微信公众号,不定时发送各类有趣猎奇的技术文章:Python编程学习
进程
程序:程序是一个存储在磁盘上某个目录中的可执行文件
进程:程序在内存中执行的实例被称为进程
进程属性
- 独立的地址,独立的内存空间
- 唯一的身份标识符,PID(该值可复用,延迟复用算法),(CPU处理进程任务,会使用时间片轮转调度算法,一个CPU核心不会在一个进程停留过多时间)
- 进程启动者身份信息(UID),同样可以维护当前进程的权限等
PID为0:该进程为调度进程(idle进程),是系统初始化的第一个进程,是其他进程的祖先
PID为1:该进程是init进程,启动unix系统,由进程0创建,完成剩余的系统引导工作
PID为2:该进程为内核线程管理进程
在linux操作系统下,每一个进程都有自己的pcb
,也叫做进程控制块,是系统为了管理进程设置的一个专门的数据结构,用来记录和管理进程的属性及特征、维护进程的状态和变化过程
PCB(进程控制块)是系统感知进程存在的唯一标识,通常处于系统内存中的一块连续地址,对进程的管理都需 要PCB中维护的内容
- PCB中维护的内容
- 进程标识符、内部|外部
- 调度信息:进程状态、进程优先级等其他
- 进程状态
- 使用的IO设备、屏幕、文件、网卡等等
- 处理器信息:寄存器、计数器、堆栈指针
进程状态
- R:
TASK_RUNNING
就绪/运行状态
实际在CPU
上所运行进程为运行状态,一些可执行的但尚未被调用的处于就绪状态
该状态进程会被存放到CPU
的可执行队列中,去由CPU
具体分配资源,进行工作
- S:
TASK_INTERRUPTIBLE
可中断睡眠状态
这样的进程可能处于一种等待状态、等待socket链接、IO、信号量、CPU等等,而被挂起
等待的进程,会存储在一个专门的等待队列中、如果等待事件完成、等待队列中的进程将被唤醒
- D:
TASK_UNINTERRUPTIBLE
不可中断睡眠状态
一般处于这样状态的是一些内核进程、我们是无法直接使用kill -9
的方式中断杀死
这样的进程、也是处于一种睡眠状态,暂时挂起
比如当我们需要使用内核函数对某些设备进行读写操作、为了避免在这个过程中,使用中断或者异步信号的方式使进程终止,而导致进程与设备交互过程被打断(陷入不可控状态);就需要该状态对这样的进程进行保护
- T:
TASK_STOPPED|TASK_TRACED
暂停状态
使进程停止,可以向进程发送SIGSTOP
信号
使进程开启,发送SIGCONT
信号,将进程回复为TASK_RUNNING
状态
TASK_TRACED
一般为断点,更为安全,但需要使用ptrace
系统函数来开启进程或调试进程结束
- Z:
TASK_DEAD-EXIT_ZOMBIE
退出或僵尸状态
进程在退出的过程中,处于TASK_DEAD
状态
每个进程在退出时,所占用的资源都将被回收、但是会保留一个进程相关信息的数据块
当这个数据库里所维护的东西都被释放掉了,所剩余的就是被作僵尸(会一直占用进程ID)
保留的数据块中药维护进程的退出码、PID
等等信息
- X:
TASK_DEAD-EXIT_DEAD
停止状态
该过程非常短暂,代表进程即将被销毁
释放资源
CPU中断
中断是由硬件或软件发送的一种信号,也叫IRQ
(中断请求)
CPU处理事情主要两种方式:
- 中断
- 轮询
所有的linux操作系统都是基于中断请求来让CPU进行工作;一个CPU一旦接收到了一个中断请求,就会暂停执行手头的工作转而去处理这个中断的请求,并且调用一个叫做中断处理器的特定程序,当处理完这个中断请求之后,他会恢复执行之前被中断的工作
僵尸、孤儿
正常情况下,子进程运行完毕,父进程是需要回收子进程种的内存资源;linux系统会在进程结束后,释放进程所占用的资;比如,内存,打开的文件,一些句柄等等。同时会在一个额外的数据结构体中延续保存:PID,退出状态,运行时间等信息。
但如果子进程已经结束了任务,父进程迟迟没有对他进行资源收集和释放,那么就会造成很多这样的结构体留在内存中,进程的ID值也是有限的,僵尸进程过多,也无法生产新的进程,导致系统性能大幅度下降
- 僵尸进程:父进程活着,子进程已经完成了任务,但是父进程不做回收工作
如果父进程没有回收子进程的资源,直接就退出了,那么此时,执行完毕的子进程,由于没有人回收资源,会形成孤儿进程
但是孤儿进程并不可怕,在linux系统中,还会有init进程前来帮助回收这些孤儿进程,做善后工程
- 孤儿进程:父进程退出,子进程执行完毕,等待回收,一般会被
init
进程接管回收
同步异步
**同步:**多个线程或进程在发出或者接收到请求(或反馈)的时候,只有等到了结果或反馈,才能接着干下面的事情
异步: 多个线程或进程在发出或者接收到请求(或反馈)的时候,不需要等到结果,也可以继续干一些别的事情
mutliprocessing
from multiprocessing import Process
该模块自Python 2.6版本起引入,允许为多核或多CPU
派生进程,并且提供了方便我们实现进程同步及进程通信等API;由于子进程被创建出后,防止僵尸进程的产生,一定要注意使用join
函数在父进程中做子进程退出后的资源收集
- 创建一个进程
p1 = Process(target=None, name=None, args=(), kwargs={},)
'''
target: 子进程调用的对象
agrs: 调用对象的元组参数
kwargs: 调用对象的字典参数
name: 别名
'''
- 支持的属性方法
p.run = func
# 如创建了一个关于Process的进程实例,但是并没有给定执行函数参数,可以通过run类来在运行中赋值创建
p.start()
# 开启进程
p.is_alive()
# 返回当前存活进程
p.join(timeout=None)
# 阻塞等待进程结束,并回收;join函数部分的timeou参数用来指明等待回收的时间,时间一到,就结束阻塞 行为,不在继续等待子进程结束;子线程依然可以继续运行,所以非阻塞可以是在一个循环中来进行
p.terminate()
# 直接杀死进程,该进程不会在执行之后的工作,在该函数执行完成之后,也要使用join用来更新状态及回收
p1.daemon
# 设置该值为True时,父进程结束,该进程结束,且自己不能产生新进程,在p.start()之前设置
p1.exitcode
# 进程在运行时该状态码为None,其他表示结束
p1.name
# 进程的名字,在Process类初始化的时候可以赋值传参定义
p1.pid
# 当前进程PID
multiprocessing
创建多进程
from multiprocessing import Process,current_process
import time
def work_1():
print('this is work_1:',current_process().name)
time.sleep(1)
print("work_1 is over.")
def work_2():
print('this is work_2:',current_process().name)
time.sleep(1)
print("work_2 is over.")
def work_3():
print('this is work_3:',current_process().name)
time.sleep(1)
print("work_3 is over.")
def main():
p1 = Process(target=work_1)
p2 = Process(target=work_2)
p3 = Process(target=work_3)
process_list = [p1,p2,p3]
t_start = time.time()
for p in process_list:
p.start()
while True:
for p in process_list:
p.join(timeout=0)
if not p1.is_alive() and not p2.is_alive() and not p3.is_alive():
break
t_end = time.time()
print('%.2f' % (t_end - t_start))
if __name__ == '__main__':
main()
# 当前程序的结果为1s左右,并非3s,说明是一个并发的多进程行为
- 继承方式创建进程
class Work(Process):
def __init__(self):
super().__init__()
def run(self): #重写run函数
pass
p = Work()
p.start()
p.join()
在继承Process
类创建好一个实例后,可以直接通过start
函数开启进程
start
会查找子类及父类中可用的run
函数来实现业务执行
- 继承创建进程的代码示例
from multiprocessing import Process,current_process
class Work(Process):
def __init__(self):
super(Work,self).__init__() # super函数 调用父类的一个方法
self.num = 1
def run(self):
self.num += 1
print(self.num)
def main():
p1 = Work()
p1.start()
p1.join()
if __name__ == '__main__':
main()