python_线程_进程_协程
什么是线程?
-- os 能够进行运算调度的最小单位,被包含在进程之中,是一串指令的集合
-- 每个线程都是独立的,可以访问同一进程下所有的资源
什么是进程?
-- 每个进程独立,对应的内存也独立,不可互相访问,为了安全
-- 包含各种对资源的调用,各种资源的集合,以一个整体暴露给 os 管理
-- 进程要操作 cpu,必须先创建一个线程,
-- 进程只是向操作系统要了资源,通过线程才能使用资源
-- 一个进程至少有一个主线程,其主线程可以创建子线程,子线程又可以创建子线程
进程快还是线程快?
-- 线程和进程没有可比性
-- 启动一个线程快还是线程快?启动线程快,假如都启动了,运行起来没有区别
进程与线程区别?
-- 线程共享内存空间,进程内存是独立的,
-- 虽然是克隆相同的数据,但两个子进程数据都是两个独立的数据
-- 同一个进程的线程之间可以直接交流,两个进程想通信,必须通过一个中间代理来实现
-- 创建新线程很简单,创建新进程需要对父进程进行一次克隆
-- 一个线程可以控制和操作同一进程里的其他线程,但是进程只能操作子进程
-- 修改主线程,可能会影响其他线程运行行为,修改父进程而并不是删除,子进程不会受到影响
什么是并发?
-- 假如阅读一本书,我只需要记住读到了第几页,第几行,第几个字符,
也就是说只要记住 3 个数字,可以在休息完了之后,接着读,室友在我休息的时候,
也可以按此方法阅读这本书,从而实现书的共享和利用率,把这本书同样看作 cpu,
依据上下文,就可以实现快速的切换线程,进程
-- 单核 cpu 依据上下文,实现快速的切换,给了一个幻觉,可以同时执行多个任务,实际上,任何时候只能执行一个线程
-- 对于多核心 cpu 实实在在同时可以执行多个任务
什么是 python GIL?
-- 多核 cpu 可以同时执行多个线程
-- 全局解释器锁
-- 在 python 中无论启动多少个线程,无论 cpu 有多少核心,python 在执行的时候,
会淡定的在同一时刻只允许一个线程运行,。
-- python 线程调用 os 原生线程,c 语言原生线程接口,必须把上下文传给 os,对于调用接口,
解释器无法对 cpu 处理顺序处理,解释器无法控制线程执行顺序,只能调用等结果,
对于结果有可能对,有可能错,多个线程可以同时多核执行,但同时只有一个线程对结果进行修改,
Cpython 才有这个问题
如何实现一个最简单的多线程?
- 1 import threading #导入线程模块
- 2 import time
- 3 def run(n): # run可以随便改名
- 4 print("talk", n)
- 5 time.sleep(2)
- 6
- 7 t_1 = threading.Thread(target=run, args =('t1',)) # 实例一个线程
- 8 # target =目标函数 ,args =参数(元组形式,一个参数也要加逗号)
- 9 t_2 = threading.Thread(target=run,args=('t2',))
- 10
- 11 t_1.start() # 启动线程
- 12 t_2.start()
还有个方式,通过类来写
- 1 import threading
- 2 import time
- 3 class MyThread(threading.Thread): # 继承threading.Thread
- 4 def __init__(self,name): # 重构父类析构函数
- 5 super(MyThread,self).__init__() # 继承父类析构函数
- 6 self.name = name
- 7 def run(self): # 必须写run
- 8 print('talk',self.name)
- 9 time.sleep(2)
- 10
- 11 if __name__ == '__main__':
- 12 t_1 = MyThread('t1') # 实例一个线程
- 13 t_2 = MyThread('t2')
- 14
- 15 t_1.start()
- 16 t_2.start()
-- 对于主线程和子线程来说,程序本身就是个主线程,主线程执行时候并不等待子线程,对于整个程序主线程结束时候,会等待子线程全部结束
如何实现简单计算线程运行时间?
- 1 import threading
- 2 import time
- 3
- 4 class MyThread(threading.Thread):
- 5 def __init__(self,name):
- 6 super(MyThread,self).__init__()
- 7 self.name = name
- 8 def run(self):
- 9 print('talk',self.name)
- 10 time.sleep(2)
- 11 print('%s is done'%self.name)
- 12
- 13 start_time = time.time()
- 14 if __name__ == '__main__':
- 15 list_t = [] # 通过列表关联有多少个线程,循环此列表进行线程等待
- 16 for i in range(50):
- 17 t = MyThread(i) # 实例一个线程
- 18 # t.setDaemon(True) # 此为守护线程声明,不重要,必须在start前面加
- 19 t.start()
- 20 list_t.append(t) # 每实例一个线程都加入列表,通过列表长度判断添加了几个进程
- 21 for i in list_t:
- 22 t.join() # 循环列表,给每个线程加上join()等待,当子线程成为守护线程时候,不需要join
- 23 end_time = time.time()
- 24
- 25 print('running time is : %s'%(end_time - start_time))
- 26
- 27 print('The mian process.....')
通过 "线程名. join()" 来进行等待子线程结束,主线程才继续执行
对于多线程同时共享一份数据,更改同一份数据,有哪些坑?
-- GIL 锁 + - 数据进行修改多了,修改不准,大坑,GIl 锁只能保证同一时间只有一个线程在执行,
可以使用 thread lock 保证同一时间只有一个进程进行数据修改,在修改的时候,保证线程串行,
在 2.x 版本有问题,在 3.x 此问题解决了,但还是要加上锁,官方没有声明
在 2.x 上如何解决?
lock = threading.Lock() #实例化锁
def run(self):
lock.acquire #加锁,其他线程进入等待状态
global num
num += 1
lock.release() #释放锁
-- 但是此锁在有嵌套锁的时候,就会出现锁住无法释放锁的时候,就得使用递归锁
lock = threading.RLock() --R 表示递归锁
-- 互斥锁,同时只允许一个线程修改数据。
什么是守护线程?
-- 主线程执行完,退出,不管守护线程是否执行完毕,程序全部退出,主人 + 奴隶
-- 启动线程之前,加上 "线程对象. setDaemon(True)"
如何查看主线程和活跃的线程计数?
threading.current_thread() # 查看主线程
threading,active_count() #查看活跃线程格式
什么是信号量?
-- 信号量和锁一样,拥有多把锁,分配锁,让有锁的线程修改数据,
也说明同时让有锁的线程并行,没有锁的等待,完成的线程释放锁,
等待线程依次获得锁,但是锁数量是有限制的
先实例一个 semphore 对象 "semphore = threading.BoundedSemaphoer(5)"
# 同时运行 5 个获得锁的线程
def run(self):
semphore.acquire #加锁,其他线程进入等待状态
global num
num += 1
semphore.release() #释放锁
什么是事件?
-- Events 进行不同线程之间同步数据
(红绿灯,每隔多少秒变灯,车子不停检查灯的状态,红灯停,绿灯行,
本质上声明全局变量,红绿灯满足条件更改全局变量,车子不停检查全局变量)
event = threading,Event()
event.set() # 设置标志位
event.clear() # 清空标准位
event.is_set() # 判断标志位是否设置
event.wait() # 标志位被清空一直堵塞状态,直到标志位再次设定不堵塞状态
如何实现一个红绿灯程序?
- 1 import threading
- 2 import time
- 3
- 4 event = threading.Event() # 声明事件
- 5
- 6 def light(): # 定义红灯
- 7 event.set() # 设置事件
- 8 conut = 0 # 计时
- 9 while True:
- 10 if conut > 5 and conut < 10: # 在5到10秒内,红灯,清空事件
- 11 event.clear()
- 12 print('light red........')
- 13 elif conut > 10 : # 绿灯,设置事件
- 14 event.set()
- 15 print('light green.......')
- 16 conut = 0
- 17 else:
- 18 print('light green.......')
- 19 time.sleep(1)
- 20 conut += 1
- 21
- 22 def car(name): # 定义车
- 23 while True:
- 24 if event.is_set(): # 检查事件状态,如果是红灯就停止
- 25 print('[%s] is run......'%name)
- 26 time.sleep(1)
- 27 else:
- 28 print('[%s] is wait......'%name)
- 29 event.wait() # 被清空处于堵塞状态
- 30 print('[%s] is run......' % name)
- 31
- 32 if __name__ == '__main__':
- 33 light_1 = threading.Thread(target=light) # 实例红灯线程
- 34 light_1.start() # 启动
- 35 car_1 = threading.Thread(target=car,args=('哈士奇',)) # 实例车进程
- 36 car_1.start() # 启动
什么是 paramiko 模块?
-- 连接远程主机
-- import paramiko # 导入模块
ssh = paramiko.SSHClient() # 创建 ssh 对象
ssh.connect(hostname ='', port = "", username ="", password = "") # 连接远程端口
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 允许连接不在 konw_host 文件中的主机,自动添加,建立安全认证机制
stdin, stdout, stderr = ssh.exec_command("命令")
# 执行命令, 标准输入 (命令本身),标准输出,标准错误
#命令只能是一次性执行完,而不能是时刻输出命令
result = stdout.read() # 获取命令结果
ssh.close() # 关闭连接
-- 如何实现 sftp 下载上传文件?
-- imoprt paramiko
transport = paramiko.Transport(("ip 地址", 端口)) # 创建 transport 对象
transport.connect(username ="", password = "") # 连接
sftp = paramiko.SFTPClient.form_transport(transport) # 启动 sftp
sftp.put('本地文件路径',"远程文件路径") # 将本地文件上传到远程
sftp.get('远程文件路径',"本地文件路径") # 将远程文件下载到本地
transport.close() # 关闭连接
上面用户名和密码都是明文,不安全
-- shh 密钥
公钥 public key 给别人,映射到密钥, 让别人免密码登陆
私钥 private key
天王盖地虎,宝塔镇河妖
什么是队列?
-- 一种数据类型容器,先进先出
列表和队列的区别?
-- 列表取出一个数据,列表中那个数据还在
-- 但对于队列数据,取走一个少一个
为什么需要队列?
-- 提高排队者和处理者的效率
-- 比如 50 个人拷贝一份资料,一份的时间 5 分钟,把 U 盘留下,按顺序来,排队者可以不用排起队,
干等着,可以在这段时间去干其他事,计算号时间然后去拿就行了,对于处理者来说,
1 个人处理要 250 分钟,加 1 个人帮忙,只要 125 分钟可以完成,对于处理者来说我怎么处理,
排队的人不需要关心,处理者只需要把资料拷贝好了交给排队者就行了
如何生成使用一个队列?
queue 模块,有三种队列,先进先出 + 后进先出 + 设置优先级队列
q = queue.Queue(block=True,maxsize = 数字) -- 实例一个 q 对象, 先进先出
-- block 默认等于 True,取数据没有的时候一直处于堵塞状态,等待放入数据
-- maxsize 默认不写,任意 put 数据,写的,多了处于堵塞状态,需等待数据取出再放入
q = queue.LifoQuenue(maxsize=0) -- 后进先出
q = queue,PriorityQueue(maxsize=0) -- 设置优先级
-- q.put((优先级,数据)) -- 数字越小优先级高
q.put(block=True,timeout=None) -- 放入一个元素
q.qsize() -- 查看长度
q.get(block=True,timeout=None)
-- 取一个数据,当没数据时候,一直等待放入数据然后取状态,堵塞状态
q.get_nowait() -- 取一个数据,当没数据时候,抛出异常
q.get(timeout = 1) -- 取数据 1 秒钟,1 秒钟之后没有数据抛出异常
什么是生产与消费模型?
-- 用于解决大多数并发问题,平衡生成能力和消费能力来提高整体处理数据能力
-- 生产者和消费者之间不彼此通信,对于生产者只需要把生产的商品放入队列,
队列满了,停止生产,等待消费者从队列中消费商品。对于消费者,
只需要到队列中拿商品,队列商品没了,进入等待状态,等队列中出现新的商品。
-- 供求关系
如何实现?
- 1 import threading
- 2 import time
- 3 import queue
- 4
- 5 q = queue.Queue(maxsize=10)
- 6
- 7 def p(name):
- 8 count = 1
- 9 while True:
- 10 print('%s生产第%s包子'%(name,count))
- 11 q.put('包子%s'%count)
- 12 count += 1
- 13 time.sleep(1)
- 14
- 15 def s(name):
- 16 while True:
- 17 q.get()
- 18 print('%s作死的吃,如狼似虎'%name)
- 19 time.sleep(1)
- 20
- 21 if __name__ == '__main__':
- 22 p1 = threading.Thread(target=p, args=('一枝花',))
- 23 s1 = threading.Thread(target=s, args=('北门吹雪',))
- 24 s2 = threading.Thread(target=s, args=('西门吹风',))
- 25
- 26 p1.start()
- 27 s1.start()
- 28 s2.start()
-- I/O 操作不占用 cpu,读取输入输出
-- 计算占用 cpu,数学运算不适合多线程
-- python 多线程,不适合 cpu 密集操作型的认为,适合 I/O 密集型的任务,(socketserver)
-- python 中启多个进程,每个进程都有一个主线程,运用多进程来实现多核心 cpu 同时运行的问题
-- 在 linux 中每个子进程都是由父进程启动的,必有一个根进程
如何启动多进程?
imoprt multprocessing #导入多进程模块
p = multiprocessing.Process(target = 方法,args = 元组)
p.start()
# 多进程的语法规则和多线程执行方法类似,进程中可以嵌套启动线程
os.getppid() #获得当前父进程 id
os.getpid() #获得当前程序子进程 id
如何让两个进程之间进行通信?
queue # 线程 队列 ,线程之间共享数据
Queue # 进程 队列 ,进程之间共享数据
进程之间数据和内存地址是独立的,无法互相访问各自的内存地址
-- 如果把线程 q 当做一个一个参数传给子进程,相当于序列化传给子进程
-- 对于进程 Q,把 Q 对象当做参数传入子进程,相当于克隆了一份一样的,本质上是 pikle
一份传给个子进程,本质上虚拟出一个中间 Q,子进程 load 一下,父进程 dump 一下
实现代码:
- 1 from multiprocessing import Process,Queue # 导入
- 2
- 3 def child_1(Q):
- 4 Q.put('hello')
- 5
- 6 if __name__ == '__main__':
- 7 q = Queue()
- 8 p = Process(target=child_1, args=(q,))
- 9 p.start()
- 10 p.join()
- 11 print(q.get())
实现了一个进程放数据,另外一个进程拿到数据
还有个 pipes 实现进程之间通信?
-- 相当于建立起电话连接,类似 socket,发一条收一条,多收了就会进入等待状态
- from multiprocessing import Process,Pipe
- def child_1(Q):
- print('来自一枝花的祝福:',Q.recv())
- Q.send('兄弟,好久没见面了')
- if __name__ == '__main__':
- brother_1, brother_2 = Pipe()
- p = Process(target=child_1, args=(brother_2,))
- p.start()
- brother_1.send('北门吹雪可好?')
- print("来自北门吹雪的回话:",brother_1.recv())
- p.join()
上面只是完成数据的传递,而不是共享,如何解决?
进程之间可以通过 Manager 进行共享数据?
-- 本质上和 Queue 一样
- 1 from multiprocessing import Process,Manager
- 2 import os
- 3
- 4 def show(d,l):
- 5 d[os.getpid()] = os.getpid() #每个进程都修改共享字典
- 6 l.append(os.getpid()) #每个进程都修改共享列表
- 7 if __name__ == '__main__':
- 8 with Manager() as manager: # 等价于manager = Manager()
- 9 d = manager.dict() # 生成一个共享字典
- 10 l = manager.list() # 生成一个共享列表
- 11 p_list = [] # 确定每个进程都完成的列表
- 12 for i in range(10):
- 13 p = Process(target=show, args=(d,l)) #把共享列表和字典当做参数传入进程
- 14 p.start()
- 15 p_list.append(p)
- 16 for p in p_list: #确定每个进程都完成
- 17 p.join()
- 18 print(d,l)
- 19 # 对于此处,修改数据不需要加锁,Manager自动加上锁了
如何逻辑整理?
-- Queue、Pipe、Manager、Process 模块都是 multiprocessing 模块中自带
-- Queue、Manager 实现通信的本质上是一样的,
- 但是 Queue 只能实现简单的 put 和 get,
Manager 不仅可以实现共享字典和列表,还可以进行任何对共享字典和列表的操作
-- Pipe 的使用,有点类似 socket,开始实例两个通信对象,然后把其中一个对象当做参数,传入另外一个进程,同时 send,recv 实现进程之间通信
-- Queue、Pipe、Manager 实现通信的逻辑,都是先生成通信对象的中间桥梁,通过线程实例化的时候,把通信对象当做参数传入进去,从而实现通信
-- 现实生活中,要想打电话的前提,必须要有手机,手机就是其中沟通的桥梁
什么是进程锁?
-- 锁住对屏幕资源的抢占,和线程锁一毛一样,先从 multiprocessing 中导入 Lock,
然后生成锁的实例,然后把锁当做参数传入进程实例,通过 .acquire() 锁上屏幕输出,.release() 释放锁
什么是进程池?
-- 同一时间规定多少个进程在 cpu 上运行
如何实现?
func = 进程函数,args =(进程参数,),callback = 回调函数(异步才有)
回调函数是每个子进程执行完毕,再调用此函数,是主线程进程调用的,提高运行效
先 pool.close(),再 pool.join(),大坑,逻辑上是先 join 再 close,但是此处不符合逻辑
逻辑代码
- 1 from multiprocessing import Pool
- 2 import os
- 3 import time
- 4
- 5 def show(q): # 进程函数
- 6 time.sleep(2)
- 7 print(os.getpid())
- 8
- 9 def callback_1(a): # 回调函数,主进程调用
- 10 print('回调函数',os.getpid())
- 11
- 12 if __name__ == '__main__':
- 13 with Pool(5) as pool: # 线程池只有5个进程,相当于pool = Pool(5)
- 14 for i in range(10): #启动10个进程
- 15 pool.apply_async(func=show,args=(i,),callback=callback_1)
- 16 pool.close() # 坑,先close再join,逻辑坑
- 17 pool.join()
什么是__name__
-- 把此模块当做脚本,就执行,测试用的。如果当做导入,
就不会执行__name__,通过 if 进行判断,就不会执行 if __name__ == '__mian__'下的函数
什么是协程?
-- 微线程,一种用户态的轻量级线程
-- 单线程实现并发,本质上在不同函数中来回切换,串行,通过 yield
优点:
- 无需线程上下文切换
- 无需原子操作锁定及同步的开销
- 方便控制流,简化编程模型
- 高并发 + 高扩展 + 低成本,一个 cpu 可以运行上万个协程
缺点:
- 无法利用多核心 cpu,需要和进程进行配合使用
协程的逻辑?
-- 通过 yield,让函数变成生成器,先通过实例一个迭代对象,
然后通过迭代对象.__next__() 方法进行函数一次调用,遇到 yield 函数跳出当前函数执行,
去执行其他逻辑函数,其他函数中通过迭代对象. send(参数) 方法,参数被迭代器函数 yield 接收到,
唤醒迭代对象函数,执行 yield 函数下的函数体,再次遇到 yield,再次跳出来,
回到 "对象. send(参数)" 后面的逻辑函数,一遇到对象. send(参数),
又回到对象函数中 yield 函数后面的逻辑函数执行,如此反复,在程序中进行跳转,给了一个多并发的幻觉
-- 实现模型是生产者消费模型?
- 1 import time
- 2
- 3 def producer(name): # 定义一个生产者
- 4 c_1.__next__() # 生成器调用
- 5 c_2.__next__()
- 6 for i in range(2,100,2):
- 7 print('%s生产了[%s]包子'%(name,i))
- 8 c_1.send(i)
- 9 # 跳出此函数,唤醒c_1迭代器中yield后面的逻辑函数并且执行,一遇到yield或者
- 10 # c_1迭代对象函数已经执行完了,又跳回到此处,继续执行
- 11 c_2.send(i)
- 12 def consumer(name):
- 13 print('开始要吃包子了')
- 14 while True:
- 15 baozi = yield
- 16 print('%s正在吃包子%s'%(name,baozi))
- 17 time.sleep(1)
- 18
- 19 if __name__ == '__main__':
- 20 c_1 = consumer('一枝花')
- 21 c_2 = consumer('北门吹雪')
- 22 p = producer('酒酒')
-- 上面有个问题,生产者无任何堵塞,幻觉上实现了并发,但是一旦生成者有堵塞,就会有卡顿,如何解决?
协程如何实现的?
-- 一遇到 io 请求,协程把 io 请求和回调函数交个 os,os 处理完了调用回调函数,通知我,然后又跳回来执行剩下的逻辑处理,如此反复
协程切换原则?
-- 遇到 I/O 操作就切换,那什么时候切回去?I/O 操作完成切换回去
-- 通过 greenlet 手动的切换协程
代码实现:
- 1 from greenlet import greenlet # 导入协程模块
- 2
- 3 def f1(): # 定义一个协程函数1
- 4 print(1) # 协程1切换执行的函数
- 5 g_2.switch() # 切换到协程函数2,也是协程函数1的回来执行得起点
- 6 print(2) # 协程1切换回来执行函数
- 7 g_2.switch() # 切换到协程2函数
- 8
- 9 def f2(): # 定义协程2函数
- 10 print(3) # 协程2执行的函数
- 11 g_1.switch() # 切换到协程函数1,也是协程函数2的回来执行得起点
- 12 print(4) # 协程2执行的函数
- 13
- 14 g_1 = greenlet(f1) # 启动协程1
- 15 g_2 = greenlet(f2) # 启动协程2,启动协程并不代表,执行协程函数
- 16
- 17 g_1.switch() # 执行协程1
- 以上函数虽然实现了协程,但是手动执行,没有自动化
如何实现自动挡协程?
- 1 import gevent # 引入协程模块
- 2
- 3 def f1(): # 定义协程函数
- 4 print('hello 1') # cpu处理
- 5 gevent.sleep(3) # 模拟i/o操作,跳出
- 6 print('hellw 1 again')
- 7
- 8 def f2():
- 9 print('hello 2')
- 10 gevent.sleep(2)
- 11 print('hello 2 again')
- 12
- 13 def f3():
- 14 print('hello 3')
- 15 gevent.sleep(4)
- 16 print('hello 3 again')
- 17
- 18 gevent.joinall([
- 19 gevent.spawn(f1), # 启动协程
- 20 gevent.spawn(f2),
- 21 gevent.spawn(f3),
- 22
- 23 ])
-- 上面代码牛逼之处在于,代码的中运行时间与最大运算时间有关,
上面代码最长运行时间 4 秒,一遇到 io 操作就切换,依次从上往下跳,
自动检查 I/O 操作是否完成,实际上 I/O 操作和程序执行进行了异步,分开了
如何使用协程进行大并发爬网页?
-- urlib 和 socket,gevent 无法直接知道其中 I/O 操作
如何实现?
- 1 import requests
- 2 import gevent
- 3 from gevent import monkey
- 4
- 5 monkey.patch_all() # 把当前程序的所有的io操作给单独做上标记,打上补丁
- 6
- 7 def pa(url):
- 8 try:
- 9 r = requests.get(url, timeout=30)
- 10 r.raise_for_status()
- 11 r.encoding = r.apparent_encoding
- 12 data = r.text
- 13 print(url, data)
- 14 except:
- 15 print(url, '爬取错误')
- 16
- 17 gevent.joinall([
- 18 gevent.spawn(pa, 'http://www.ithome.com'),
- 19 gevent.spawn(pa, 'http://www.baidu.com'),
- 20 gevent.spawn(pa, 'http://lol.qq.com')
- 21 ])
如何实现高效 socketserver?高并发?
服务端:
- 1 import socket # 导入socket
- 2 import gevent # 导入协程模块
- 3 from gevent import monkey # 导入协程标记模块
- 4
- 5 monkey.patch_all() # 启动协程标记
- 6
- 7 def server(port): # 定义socket服务
- 8 s = socket.socket() # 实例socket服务
- 9 s.bind(('localhost',port)) # 绑定端口
- 10 s.listen(5) # 启动监听
- 11 while True: # 提供服务
- 12 conn, addr = s.accept() # 监听对象和对象地址
- 13 gevent.spawn(handle, conn) # 启动处理请求协程
- 14
- 15 def handle(Q): # 定义处理函数
- 16 while True: # 循环接收客户端请求
- 17 try:
- 18 data = Q.recv(1024) # 接收数据
- 19 print(data.decode('utf-8')) # 打印接收数据
- 20 Q.send(data.upper()) # 发送处理数据
- 21 except Exception as e: # 异常打印
- 22 print(e) # 打印异常
- 23 break # 异常跳出程序
- 24
- 25 if __name__ == '__main__':
- 26 server(6969) # 输入端口,启动服务
什么是用户空间和内核空间?
os 采用虚拟存储器,对 32os,寻址空间为 2 的 32 次方
os 的核心是内核,独立于普通的应用程序,可以访问保护的内存空间,也有访问底层硬件设备的所有权限
为了保护内核的安全,用户进程不能直接操作内核,os 把虚拟空间划分为两个部分,一个是内核空间,另外一个是用户空间
什么是进程切换?
内核有能力挂起正在运行的进程,并恢复以前挂起的某个进程,
进程的堵塞状态是进程本身主动请求的,通过进程的上下文,快速的在 cpu 中运行与堵塞
什么是进程堵塞?
进程请求资源失败、等待某种操作完成、无工作可做等状态,则有 os 自动执行堵塞原语(block),
让自由运行的进程,进入堵塞状态,获得 cpu 资源的进程才有堵塞状态,
堵塞状态不占用 cpu 资源,堵塞状态是进程的一种主动请求
什么是文件描叙符?
-- 形式上是一个非负整数,实际上它是一个索引值,指向内核为每一个进程所维护的改进程打开文件的记录表
-- 当程序打开一个现有的文件或者创建一个新文件的时候,内核向进程返回一个文件描叙符
什么是缓存 IO?
数据先被拷贝到内核缓存区中,然后才会从 os 内核中拷贝到应用程序地址空间
-- 数据 ——》内核空间——》用户空间
-- 数据的反复拷贝,cpu 和内存资源开销非常大
什么是 IO 模式?
linux 操作系统产生哪些网络模式?
什么是异步 IO?
不会导致请求 IO 用户的堵塞。用户请求,滚,把数据送到用户家门口
任何堵塞是服务器堵塞,任何模式都是改变用户等待请求的方式
什么是 IO 多路复用?
-- select,poll,epoll 区别?
select:一种循环检测请求数据,数据来了,内核告诉用户数据来了,保密,需要用户循环接收
poll:
epoll: 一种循环检测请求数据,数据来了,告诉用户数据来了,哪个活跃,用户拿着这两个数据去取数据
不取数据,数据本身还在内核空间中,内核每次都通知用户去取 -- 水平触发
不取数据,数据本身还在内核空间中,内核不再通知用户去取 -- 边缘触发
如何使用 select 建立一种 socket 模型, 实现并发?
- 1 #!/usr/bin/python3
- 2 import socket
- 3 import select
- 4 import queue
- 5
- 6 server = socket.socket()
- 7 server.bind(('localhost',6969))
- 8 server.listen(5) # 声明协议,启动监听
- 9 server.setblocking(False) #设置为非堵塞
- 10
- 11 inputs = [server,] #生成检测列表
- 12 #inputs = [server,conn,conn2]
- 13 outputs = [] # 下次循环开始就返回上次循环加入的数据
- 14 client_data = {} # 定义一个接收各个连接客户端发来数据的字典
- 15
- 16 while True: # 让select一直检测
- 17 readable, writeable, exceptional = select.select(inputs,outputs,inputs)
- 18 print(exceptional) # readable, writeable, exceptional,三个对象是固定参数,不可更改
- 19 for r in readable: # readable中返回的是活动的连接列表,不活动不返回,r是活着的连接
- 20 if r is server: # 判断是否有新的连接
- 21 conn, addr = server.accept() # 接收数据,非堵塞
- 22 inputs.append(conn) # 把新的连接加入到select中检测
- 23 client_data[conn] = queue.Queue() # 一个新的连接生成一个队列对象
- 24 else:
- 25 try:
- 26 data = r.recv(1024) # 接收数据
- 27 client_data[r].put(data) # 把数据放入字典中的队列中
- 28 out
来源: http://www.cnblogs.com/2bjiujiu/p/6668932.html