多进程编程
1)forking工作原理
- fork(分岔)在 Linux系统中使用非常广泛
- 当某一命令执行时,父进程(当前进程)fork出一个子进程
- 父进程将自身资源拷贝一份,命令在子进程中运行时, 就具有和父进程完全一样的运行环境
进程的生命周期
- 父进程fork出子进程并挂起
- 子进程运行完毕后,释放大部分资源并通知父进程,这个时候,子进程被称作僵尸进程
- 父进程获知子进程结束,子进程所有资源释放
- 如果子进程还在运行中,父进程已经退出,此时子进程成为孤儿进程,将由systemd接管
僵尸进程
- 僵尸进程没有任何可执行代码,也不能被调度
- 如果系统中存在过多的僵尸进程,将因为没有可用的进程号而导致系统不能产生新的进程
- 对于系统管理员来说,可以试图杀死其父进程或重启系统来消除僵尸进程
2)forking编程
- 需要使用os模块,os.fork()函数实现forking功能
- python中,绝大多数的函数只返回一次,os.fork将返回两次
- 对fork()的调用,针对父进程返回子进程的PID;对于子进程,返回PID=0
#多进程ping
import subprocess
import os
def ping(host):
result = subprocess.run(
'ping -c 2 -W 1 -i 0.5 %s &> /dev/null' % host, shell=True
)
if result.returncode == 0:
print('%s:up' % host)
else:
print('%s:down' % host)
if __name__ == '__main__':
ips = ('192.168.4.%s' % i for i in range(1,255))
for ip in ips:
ret_val = os.fork()
if not ret_val:
ping(ip)
exit()
解决zombie问题
- 父进程通过 os.wait0来得到子进程是否终止的信息
- 在子进程终止和父进程调用Wat0之间的这段时间, 子进程被称为 zombie(僵尸)进程
- 如果子进程还没有终止,父进程先退出了,那么子进程会持续工作。系统自动将子进程的父进程设置为init进程,init将来负责清理僵尸进程
- python可以使用 waitpid()来处理子进程
- waitpid()接受两个参数,第一个参数设置为-1,表示与wait()函数相同;第二参数如果设置为0表示挂起父进程,直到子程序退出,设置为1表示不挂起父进程
- waitpid()的返回值:如果子进程尚未结束则返回0,否则返回子进程的PID
多线程编程
什么是进程:
- 计算机程序只不过是磁盘中可执行的、二进制(或其它类型)的数据
- 进程(有时被称为重量级进程)是程序的一次执行
- 每个进程都有自己的地址空间、内存以及其它记录其运行轨迹的辅助数据
- 操作系统管理在其上运行的所有进程,并为这些进程公平地分配时间
什么是线程:
- 线程(有时被称为轻量级进程)跟进程有些相似。不同的是,所有的线程运行在同一个进程中,共享相同的运行环境
- 一个进程中的各个线程之间共享同一片数据空间,所以线程之间可以比进程之间更方便地共享数据以及相互通讯
1)多线程工作原理
- 在多线程(MT)编程出现之前,电脑程序的运行由一个执行序列组成,执行序列按顺序在主机的中央处理器(CPU)中运行
- 无论是任务本身要求顺序执行还是整个程序是由多个子任务组成,程序都是按这种方式执行的
- 即使子任务相互独立,互相无关(即,一个子任务的结果不影响其它子任务的结果)时也是这样
- 如果并行运行这些相互独立的子任务可以大幅度地提升整个任务的效率
2)线程工作特点
- 它们本质上就是异步的,需要有多个并发事务
- 各个事务的运行顺序可以是不确定的,随机的,不可预测的
- 这样的编程任务可以被分成多个执行流,每个流都有一个要完成的目标
- 根据应用的不同,这些子任务可能都要计算出一个中间结果,用于合并得到最后的结果
3)多线程编程思路
- 主线程(相当于父进程)生成工作线程(相当于子进程)
- 工作线程做具体的工作
4)相关模块
- thread和threading和模块允许程序员创建和管理线程
- thread模块提供了基本的线程和锁的支持,而threading提供了更高级别、功能更强的线程管理功能
- 推荐使用更高级别的 threading模块
5)传递函数
多线程编程有多种方法,传递函数给threading模块的Thread类是介绍的第一种方法
Thread对象使用start)方法开始线程的执行,使用join()方法挂起程序,直到线程结束
'''多线程ping'''
import subprocess
import threading
def ping(host):
result = subprocess.run(
'ping -c 2 -W 1 -i 0.5 %s &> /dev/null' % host, shell=True
)
if result.returncode == 0:
print('%s:up' % host)
else:
print('%s:down' % host)
if __name__ == '__main__':
ips = ('192.168.1.%s' % i for i in range(1,255))
for ip in ips:
t = threading.Thread(target=ping, args=(ip, ))
t.start()
6)传递可调用类
传递可调用类给 Thread类是介绍的第二种方法 相对于一个或几个函数来说,由于类对象里可以使用 类的强大的功能,可以保存更多的信息,这种方法更为灵活
'''多线程ping'''
import subprocess
import threading
class Ping:
def __call__(self,host):
result = subprocess.run(
'ping -c 2 -W 1 -i 0.5 %s &> /dev/null' % host, shell=True
)
if result.returncode == 0:
print('%s:up' % host)
else:
print('%s:down' % host)
if __name__ == '__main__':
ips = ('192.168.1.%s' % i for i in range(1,255))
for ip in ips:
t = threading.Thread(target=Ping(), args=(ip, ))
t.start()
urllib模块
- 拥有request / error / parse / robotparse四个子模块
- 常用是request / error
>>> from urllib import request
>>> url = 'http://www.163.com'
>>> html = request.urlopen(url)
>>> html.read(20)
b' <!DOCTYPE HTML>\n<!-'
>>> html.readline()
b'-[if IE 6 ]> <html class="ne_ua_ie6 ne_ua_ielte8" id="ne_wrap"> <![endif]-->\n'
>>> html.readlines()
'''下载图片'''
import sys
from urllib import request
def get_web(url, fname): #url为目标网址(www.tedu.cn)
html = request.urlopen(url) #使用urllib模块的urlopen函数打开url,赋值给html
with open(fname, 'wb') as fobj: #以写方式打开文件
while True:
data = html.read(4096) #读html获取的数据,保存到data
if not data:
break
fobj.write(data) # 将data写入文件中
if __name__ == '__main__':
get_web(sys.argv[1], sys.argv[2])
-
如果访问的页面不存在或拒绝访问,程序将抛出异常捕获异常需要导入
urllib.error
模块 -
修改请求头
>>> url = 'http://www.jianshu.com'
>>> html = request.urlopen(url) # 403 forbidden
# 403禁止访问,是因为服务器有基础的反措施
>>> headers = {'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0'}
# 创建携带头部消息的请求对象
>>> r = request.Request(url, headers=headers)
>>> html = request.urlopen(r)
>>> html.read(10)
b'<!DOCTYPE '
urllib进阶
- 数据编码:url只允许一部分ascii码字符,其他字符都是不合法的
- 可以使用request.quote()方法进行编码
>>> url = 'https://www.sogou.com/web?query=职友集'
>>> html = request.urlopen(url) # 报错
>>> url = 'https://www.sogou.com/web?query=' + request.quote('职友集')
>>> url
'https://www.sogou.com/web?query=%E8%81%8C%E5%8F%8B%E9%9B%86'
>>> url = 'https://www.sogou.com/web?query=' + request.quote('清明节放假')
>>> url
'https://www.sogou.com/web?query=%E6%B8%85%E6%98%8E%E8%8A%82%E6%94%BE%E5%81%87'
>>> html = request.urlopen(url)
paramiko
- 实现的是ssh客户端功能
[root@localhost day01] pip3 install paramiko
>>> import paramiko
>>> ssh = paramiko.SSHClient()
# 下面代码相当于ssh远程登陆时,回答yes
>>> ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
>>> ssh.connect('127.0.0.1', username='root', password='redhat', port=22)
# 执行命令
>>> result = ssh.exec_command('id root; id alice')
>>> len(result)
3
# 返回值有3项,分别是输入、输出和错误的类文件对象。类文件对象就是像文件一样
的对象,可以使有read方法读取其中的内容。中以将三项分别赋值
>>> stdin, stdout, stderr = ssh.exec_command('id root; id alice')
# 命令执行完毕后,我们关心有哪些输出、有哪些错误
>>> out = stdout.read()
>>> err = stderr.read()
>>> out
b'uid=0(root) gid=0(root) \xe7\xbb\x84=0(root)\n'
>>> err
b'id: alice: no such user\n'
>>> out.decode() # 将bytes转成str
'uid=0(root) gid=0(root) 组=0(root)\n'
>>> err.decode()
'id: alice: no such user\n'
>>> ssh.close()