IO

这一生的挚爱 提交于 2020-01-22 03:54:02

【IO input output】

  1. 在内存中存在数据交换的操作都可以认为是输入输出
    比如:
    内存和磁盘交互 读写操作
    内存和网络交互 recv send

  2. IO密集型程序:程序执行大量的IO操作,而较少的cpu运算
    特点:消耗cpu较少,运行时间长

  3. CPU密集型程序(计算密集型):程序中大量的操作都需要cpu运算,IO操作较少
    特点:消耗cpu大,运行速度快

  4. IO分类:
    阻塞IO 非阻塞IO IO多路复用 事件IO 异步IO

    1. 阻塞IO:
      默认形态 效率很低的一种IO情形
      阻塞情况:
      1. 因为某种条件没有达成造成的阻塞
      e.g. accept recv input
      2. 处理IO事件的时候耗时比较长形成阻塞
      e.g. 文件的读写过程,网络数据发送过程

    2. 非阻塞IO:
      通过修改IO事件的属性,使其变为非阻塞的状态。(改变了第一种阻塞的状况)
      通常和循环搭配使用,不断检测循环条件是否已经满足
      s.setblocking()
      功能:将套接字设置成非阻塞状态
      参数:bool 设置为False则表示设置为非阻塞

      示例:

      # block_server.py
      
      from socket import *
      from time import sleep,ctime
      
      s = socket()
      s.bind(('127.0.0.1',8888))
      s.listen(5)
      
      # 将s设置为非阻塞
      s.setblocking(False)
      
      while True:
          print('waiting for connect......')
          try:
      	connfd,addr = s.accept()
          except BlockingIOError:
      	sleep(2)
      	print(ctime())
      	continue
          connfd,addr = s.accept()
          print('Connect from',addr)
          # connfd.setblocking(False)
          # 将recv变成非阻塞
          while True:
      	data = connfd.recv(1024),decode()
      	if not data:
      	    break
      	print(data)
      	connfd.sendall(ctime().encode())
          connfd.close()
      s.close()
      
  5. 超时检测
    定义:
    将原本阻塞的函数,设置一个阻塞的最长时间,
    在规定的时间内,如果条件达到则正常执行,如果仍然阻塞则抛出异常
    s.settimeout(sec)
    功能:设置套接字超时时间
    参数:设置的时间
    示例:

    # timeout.py
    from socket import *
    from time import sleep,ctime
    
    s = socket()
    s.bind(('127.0.0.1',8888))
    s.listen(5)
    # 设置s的超时时间(设置为5秒)
    s.settimeout(5)
    while True:
        print('waiting for connect......')
        try:
    	connfd,addr = s.accept()
        except timeout:
    	sleep(2)
    	print(ctime())
    	continue
        connfd,addr = s.accept()
        print('Connect from',addr)
        # connfd.setblocking(False)
        # 将recv变成非阻塞
        while True:
    	data = connfd.recv(1024),decode()
    	if not data:
    	    break
    	print(data)
    	connfd.sendall(ctime().encode())
        connfd.close()
    s.close()
    
  6. IO多路复用(重难点):
    原来传统的自上而下的执行过程中,一个一个来执行的,第二个要等到第一个执行完毕才可以执行
    IO多路复用把他们并列,谁可以执行了就先执行,不必依照顺序来

    1. 定义:
      同时监控多个IO事件,当哪个IO事件准备就绪就执行哪个IO事件。
      以此形成,多个IO事件都可以操作,不必逐个等待执行的效果
      目的:当程序中有多个IO事件的时候提高IO的执行效率

    2. 准备就绪:
      IO事件即将发生的临界状态

    3. import select

      select —> windows / linux / unix
      poll------> linux / unix
      epoll-----> linux / unix

    4. select函数原型:
      r,w,x = select(rlist, wlist, xlist[, timeout])
      功能:
      监控IO事件,阻塞等待IO事件的发生
      参数:
      rlist:列表 存放我们监控等待处理的IO事件
      wlist:列表 存放我们要主动处理的IO事件
      xlist:列表 存放如果发生异常需要我们处理的IO事件
      timeout:数字 超时时间
      返回值:
      r:列表 rlist当中准备就绪的IO
      w:列表 wlist当中准备就绪的IO
      x:列表 xlist当中准备就绪的IO
      注意事项:
      1. 在处理IO过程中不应该发生死循环(某个IO单独占有服务器)
      2. IO多路复用形成了一种并发的效果,效率较高

    示例:
    写一个select服务端,同时关注客户端的连接,客户端的发送和终端的输入。
    将客户端发送的内容和终端输入的内容均写入到一个文件中

    # select_server_zhongduan.py
    
    from socket import *
    from select import select
    import sys
    
    s = socket()
    s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
    s.bind(('127.0.0.1',8888))
    s.listen(5)
    
    rlist = [s,sys.stdin]
    wlist = []
    xlist = []
    f = open('select_file','w')
    while 1:
        rs,ws,xs = select(rlist,wlist,xlist)
        for r in rs:
    	if r is s:
    	    # 表明有客户端连进来
    	    connfd,addr = r.accept()
    	    # print('Connect from',addr)
    	    rlist.append(connfd)
    	elif r is sys.stdin:
    	    # 表明从终端输入内容了
    	    data = r.readline()
    	    f.write(data)
    	else:
    	    # 表明有客户端发消息
    	    data = r.recv(1024)
    	    if not data:
    		rlist.remove(r)
    	    else:
    	       f.write(data.decode())
        for w in ws:
    	pass
        for x in xs:
    	pass
    

7.位运算:

按照二进制位进行操作运算
&	|	^	<<	>>
按位与	按位或	按位异或  左移	右移
11	1011
14	1110
&	1010(一0则0)
|	1111(一1则1)
^	0101(相同为0不同为1)
11<<2	101100(右边补0)==44
14>>2	11(挤掉低位的数字)	==3


使用:
	1.在做底层硬件的寄存器操作
	2.在做标志位过滤时
标志位过滤:
	1	0	1	1(查有没有)	0---->22
	0	0	0	0	0	
	----------------------------------------
	(用00000按位与,如果那项按位与后不等于0则表示有,否则为0)
	0	0	0	1	0----->!=0
	0	0	0	0	0----->=0    
  1. poll的用法

    1. 创建poll对象
      p = select.poll()

    2. 添加关注对象
      p.register(s,POLLIN | POLLERR)
      括号内的格式事件分类,用按位或
      p.unregister(s)
      poll IO事件类型分类:
      POLLIN POLLOUT POLLERR POLLHUP POLLPRI POLLVAL
      rlist wlist xlist 断开 紧急处理 无效数据

    3. 进行监控
      events = p.poll()
      功能:阻塞等待reigister的事件发生
      返回值:events 是一个列表,列表中每个元素表示准备就绪需要处理的IO
      [(fileno,event, (),()]
      描述符 具体什么就绪了
      描述符地图 {s.fileno()😒}

    4. 处理IO事件

    5. 示例:

      # poll_server.py
      
      from socket import *
      from select import *
      
      # 创建套接字
      s = socket()
      s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
      s.bind(('127.0.0.1',8888))
      s.listen(5)
      
      # 创建poll对象
      p = poll()
      # 建立通过fileno查找IO对象的地图
      fdmap = {s.fileno():s}
      # 添加关注
      p.register(s,POLLIN | POLLERR)
      
      while True:
          # 进行监控
          events = p.poll()
          for fd,event in events:
      	# 有客户端连接进来了
      	if fd == s.fileno():
      	    c,addr = fdmap[fd].accept()
      	    print('Connect from',addr)
      	    # 注册新的套接字
      	    p.register(c,POLLIN)
      	    # 时刻维护地图更新
      	    fdmap[c.fileno()] = c
      	# 表示POLLIN准备就绪了
      	elif event & POLLIN:
      	    data = fdmap[fd].recv(1024)
      	    if not data:
      		p.unregister(fd)
      		fdmap[fd].close()
      		del fdmap[fd]
      	    else:
      		print(data.decode())
      		fdmap[fd].send('收到了'.encode())
      
  2. epoll的用法:
    使用方法:
    代码基本与poll(见上)相同
    1. 将生成对象的poll()函数变为
    创建epoll对象:p = epoll()
    2. 将register注册IO事件时,关注的事件类别改为epoll类别
    添加关注:p.register(s,EPOLLIN | EPOLLERR)
    区别:
    epoll效率要高于poll和select
    epoll的关注触发方式多一些

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!