Python自学day-9
一、paramiko模块
(第三方模块,需安装依赖包)
paramiko模块基于SSH来连接远程主机并进行相关操作。
1.SSHClient
SSHClient:用于连接远程主机并执行基本命令。
import paramiko #创建SSH对象
ssh = paramiko.SSHClient()
#允许连接不在know_hosts文件中的主机
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) #当know_hosts文件中不存在远程服务器的公钥时,自动添加进去,类似于使用ssh出现询问时输入yes。
#连接服务器
ssh.connect(hostname='c1.salt.com',port=22,username='wupeiqi',password='passw0rd')
#执行命令
stdin,stdout,stderr = ssh.exec_command('df')
#获取命令结果
result = stdout.read()
2.SFTPClient
SFTPClient:用于基于SSH传输协议SFTP的客户端。
import paramiko transport = paramiko.Transport(('hostname',22)) #建立连接实例
transport.connect(username='wupeiqi',password='') #建立连接 sftp = paramiko.SFTPClient.from_transport(transport) #获取SFTP实例
#将文件/tmp/location.py上传至远程服务器/tmp/test.py
sftp.put('/tmp/location.py','/tmp/test.py')
#将远程服务器文件remote_path 下载到本地 local_path
sftp.get('remote_path','local_path') transport.close()
二、SSH秘钥RSA讲解
1.秘钥原理
为了实现无密码登录远程主机(比使用明文账号密码登录更加安全),使用RSA非对称秘钥。
公钥:公钥给远程主机
私钥:自己拿着
公钥和私钥成对出现。
例如A(10.0.0.31)--->B(10.0.0.41):
1.A主机要连接B主机,在A上使用ssh-keygen来生成一对秘钥对(其中包含一个私钥和一个公钥)。如下图:
2.ssh-keygen生产了/root/.ssh/id_rsa和/root/.ssh/id_rsa.pub。前者是私钥,后者是公钥。
3.将公钥拷贝到远程主机某个用户下的~/.ssh/authorized_keys文件中。必须确保公钥拷贝过去后是一行(由于某些情况下的复制会导致变为多行,从而导致公钥不生效)。authorized_keys文件权限应该是600,即允许文件拥有者具有读写权限,其他用户都不能修改该文件。
4.尝试连接,无需密码
5.拷贝公钥,除了复制公钥以外。还可以使用命令 ssh-copy-id "-p22 user@hostname" 来自动将公钥拷贝到hostname主机的authorized_keys下。
2.使用秘钥的SSHClient
使用秘钥的SSHClient:
import paramiko private_key = paramiko.RSAKey.from_private_key_file('/home/auto/.ssh/id_rsa') #创建SSH对象
ssh = paramiko.SSHClient()
#允许连接不在know_hosts文件中的主机
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
#连接服务器
ssh.connect(hostname='c1.salt.com',port=22,username='wupeiqi',pkey=private_key)
#执行命令
stdin,stdout,stderr = ssh.exec_command('df')
#获取命令结果
result = stdout.read()
使用秘钥的SSHClient只需要在代码中读取本地私钥,然后在连接服务器时将password参数替换为key参数即可。
3.Windows生成秘钥
小技巧——使用Windows通过秘钥连接远程Linux主机:
有两种方法:
1.由于Windows没有ssh-keygen,可以在CRT远程终端上生成一个RSA秘钥对来实现。
2.将一个可用的私钥复制到Windows中。例如在Linux A已经可以通过秘钥登录Linux B,那么我们可以复制Linux A主机中的私钥到Windows中。操作如下:
- 使用CRT登录Linux A,然后使用 sz ~/.ssh/id_rsa 命令,将私钥文件拷贝到Windows的默认路径下(该路径可在CRT中的选项->会话选项->终端->X/Y/Zmodem下修改)。该命令需要lrzsz程序的支持,使用yum install lrzsz -y安装。
- 在程序中导入该私钥:
private_key = paramiko.RSAKey.from_private_key_file('D:/ssh/id_rsa')
- 然后就可以在Windows中使用秘钥连接Linux远程主机了。
三、线程
1.线程概念
线程是什么:线程是操作系统能够进行运算调度的最小单位,是一串操作指令的集合。线程包含在进程中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程执行不同的任务。
进程是什么:进程是计算机进行资源调度的最小单位。一个程序以一个整体的形式暴露给操作系统管理,里面包含对各种资源的调度,内存的管理,网络接口等设备的调用等,对各种资源管理的集合,就可以称为进程。进程之间内存默认是不能互相访问的,除非通过消息、共享内存等进程间通讯机制。
- 进程要操作CPU,必要要先创建一个线程。
- CPU的一颗核心同时只能干一件事,CPU上下文的切换(时间片切换)可以让人感觉是同时在运行。
- 一个进程可以包含一个或多个线程,每个线程共享同一块内存区域,而进程之间是不同的内存区域。
进程和线程的区别:
- 线程共享内存区域,进程的内存是独立的。
- 线程直接访问进程的数据段,而多个进程(父进程和子进程)拥有各自的数据段拷贝。
- 线程之间(同一进程里的)可以直接交流数据,两个进程之间想交流数据必须通过一个中间代理,socket、消息队列或共享内存等。
- 新的线程很容易创建,新的进程则需要父进程来拷贝(克隆)。
- 一个线程可以控制同一进程里的其他线程,进程只能操作子进程。
- 对主线程的修改可能会影响到进程中其他线程的运行,但是对父进程的修改不会影响子进程(杀死父进程除外)。
- 线程启动速度快、进程启动速度满。
2.线程创建
Python中线程创建简单实例:
import threading
import time def run(index,n):
i = 0
while i<=n:
print("Thread %s : " % index,i)
time.sleep(0.5)
i += 1 t1 = threading.Thread(target=run,args=(1,10,)) #创建一个线程,运行run(),参数为(1,10)
t2 = threading.Thread(target=run,args=(2,10,)) t1.start()
t2.start()
用类的形式创建线程:
#通过类的形式
class MyThread(threading.Thread):
def __init__(self,name,num):
super(MyThread,self).__init__()
self.name = name
self.num = num def run(self):
i = 0
while i < self.num:
print("Thread %s : %s" %(self.name,i))
time.sleep(0.5)
i += 1 for i in range(50): #循环启动50个线程
t = MyThread("Leo{}".format(i),10) #创建线程
t.start() #启动线程 print("启动线程完毕")
注意:在上述代码中,50个线程启动完毕后,会直接执行 print("启动线程完毕") ,也就是主线程不等创建的线程执行完毕。
要等待某个线程执行完毕:
class MyThread(threading.Thread):
def __init__(self,name,num):
super(MyThread,self).__init__()
self.name = name
self.num = num def run(self):
i = 0
while i < self.num:
print("Thread %s : %s" %(self.name,i))
time.sleep(0.5)
i += 1 t1 = MyThread("Leo",10)
t2 = MyThread("jone",20) t1.start()
t2.start()
t1.join() #主线程等待t1执行完毕
分别在主线程中子线程中打印线程信息:
class MyThread(threading.Thread):
def __init__(self,name,num):
super(MyThread,self).__init__()
self.name = name
self.num = num def run(self):
i = 0
while i < self.num:
print("Thread %s : %s" %(self.name,i))
time.sleep(0.5)
i += 1
print("Thread %s : " % (self.name),threading.current_thread()) t1 = MyThread("Leo",10) #在run中打印<MyThread(Leo, started 112644)>
t2 = MyThread("jone",20) #在run中打印<MyThread(jone, started 112644)> t1.start()
t2.start()
t1.join()
print("Current_Thread : ",threading.current_thread()) #打印<_MainThread(MainThread, started 113488)>
分别在主线程和子线程中使用threading.current_thread()可以打印出线程信息,主线程默认线程名为MainThread,子线程中我们用Leo和jone覆盖了self.name(默认为Thread-idx,idx为1~n),所以打印的线程名分别为Leo和jone。子线程的线程名可以使用threading.current_thread().getname()来获取。
查询当前存活的线程数量:
print("%s" % (threading.active_count())) #打印当前存活的线程总数,包含主线程。
2.守护线程
守护线程相关知识:
一般情况下:1.主线程中有join,则主线程要等待join的子线程执行完毕后才往下执行。2.主线程没有join,则主线程与子线程并行执行,主线程把最后一句执行完毕后,仍然要等待所有子线程执行完毕后才会结束程序(进程)。
守护线程:将子线程变为守护线程后,主线程一旦执行完毕,不管守护线程时候执行完毕,程序直接结束。如果同时存在一般子线程和守护线程,则主线程只会等待一般的子线程执行完毕,而不会管守护线程是否执行完毕。
将子线程设置为守护线程:
import time
import threading
def run(str):
i = 0
while True:
print("%s" % i)
time.sleep(0.3)
i = i+1 t = threading.Thread(target=run,args=('t-%s',)) t.setDaemon(True) #设置t为守护线程
t.start() #开始执行 time.sleep(5) #主线程休眠5秒,看守护线程是否结束
上述代码中,t.setDaemon(True)必须在t.start()前,否则报错 RuntimeError: cannot set daemon status of active thread
四、GIL全局解释器锁
每一核CPU只能同时执行一个线程(任务),多核的话可以同时执行多个任务。但是在Python中,不管多少核的CPU,都相当于只有一核在执行任务。
GIL只存在在Cpyhton解释器中,Jpython是运行在java虚拟机上的,底层是java自己实现的线程。Pypy也没有GIL。
如图:
假设有数据num=1,python启动4个线程,并且都运行 num+=1 。
GIL机制运行过程如下:
1.Python启动4个python线程。
2.每个python线程底层调用的是C语言的pThread,也即每一个python线程对应一个C线程。
3.python解释器如果将num传递给4个C线程,然后由CPU执行加1操作,最终每一个线程返回的数据都是2,这样是不行的。
4.为了正确得到结果,只能使4个线程有先后的执行加1操作,但是python解释器是调用的pThread,调用过后无法干涉C线程中的执行过程,所以没办法在C线程层面达到先后加1的效果。
5.所以,python线程只能通过GIL,即全局解释器锁。每个python线程要执行前都要先申请一个GIL Lock,获取到Lock后才能进行运算。例如1号线程先执行num+=1,返回num结果为2,然后第二个python线程再申请lock,再将num=2执行加1。
6.(只在Python2.x,Python已优化)在5中所述情况下,有可能出现一个线程执行加1操作还未完成,就被解释器要求释放lock(解释器每n条指令就需要切换线程),此时加1操作还未完成,工作状态被保存(即上下文),然后另一个线程成功执行了加1操作,num变成2。此时前面那个未完成的线程重新申请了lock,然后从先前保存的上下文还是执行,上下文中的num还是为1,假设这次执行加1成功了, 那么num变为2,这个结果是错误的,num经过了两个线程成功加1,结果应该为3。参照下面的图。
6.python的这种机制,看起来像是多线程并行执行,但是实际上同时只有一个线程在运行。
7.但是,4个加1操作虽然是类似串行运行,但每次加1可能是运行在不同的CPU上的,对应的也是4个C线程,只是线程总体有运算先后顺序罢了。
(上述所说的多线程操作一个数据时可能会出现错误,在Python2.x中需要使用 用户数据锁来对数据进行加锁,从而避免运算出错。而在Python3.x中,解释器做了优化,加不加数据锁运算都不会出错。)
五、锁
1.互斥锁
import threading num = 0
lock = threading.Lock() #创建锁
def run(n):
lock.acquire() #获取一把锁
global num
num += 1 #执行加1操作
lock.release() #释放锁 t_objs = [] for i in range(100):
t = threading.Thread(target=run,args=("t",))
t.start()
t_objs.append(t) for t in t_objs:
t.join() print("num:",num)
2.递归锁:RLock
当出现程序中多层数据锁的时候,使用threading.Lock普通锁,可能会出现程序死锁,即程序无法返回,例如以下代码:
import threading num,num2 = 0,0
lock = threading.Lock() #创建锁 def run1():
lock.acquire() #run1 获取一把锁
global num
num += 1
lock.release() #run1 释放锁 def run2():
lock.acquire() #run2 获取一把锁
global num2
num2 += 1
lock.release() #run2 释放锁 def run3():
lock.acquire() #run3 获取一把锁
run1()
run2()
lock.release() #run3 释放锁 t_objs = [] for i in range(10):
t = threading.Thread(target=run3)
t.start() while threading.active_count() != 1: #还有子线程在运行,则打印当前线程数
print(threading.active_count())
else:
print("---all threads done---") #如果子线程都已经执行完毕,则打印done
print("num = %s , num2 = %s" % (num,num2)) #打印num和num2的值
为了避免出现死锁,只需要将threading.Lock()修改为threading.RLock()即可,也即使用递归锁。原理是在使用锁的时候采用了一个字典来保存每一扇门对应的钥匙,这样就不会像普通Lock一样将钥匙弄混,导致死锁。
六、信号量
信号量:
使用方法类似互斥锁,如下代码:
semaphores = threading.BoundedSemaphore(5) #表示可以同时允许5个线程 def run():
semaphores.acquire()
time.sleep(2)
semaphores.release()
可以用在什么地方:例如socket服务器对每个请求都会创建一个Python线程来处理,默认Python是没有限制请求对应的线程数量的。我们可以通过使用信号量来限制线程的数量,从而保证服务器性能。
七、事件
线程间如何交互数据?
例如一个线程模拟红绿灯,每一段时间修改红绿灯状态。另外多个线程模拟汽车,每两秒扫描一下红绿灯,红灯停绿灯行。
我们可以使用一个全局变量来代表红绿灯,然后在红绿灯线程中修改,在汽车线程中查询:
import time
redLight = False #红绿灯状态,全局变量 def run(): #线程方法
global redLight #使用全局变量
counter = 1 #红绿灯时间计数
while True:
if redLight:
print("目前是 : 绿灯 ",counter)
else:
print("目前是 : 红灯 ",counter)
if counter % 30 == 0: #红灯30秒
redLight = True
elif counter % 50 == 0: #绿灯20秒
redLight = False
counter = 0
counter += 1
time.sleep(1) run()
使用事件来实现:
event = threading.Event()
event.set() #设置绿灯
event.clear() #清除绿灯(设置红灯)
event.wait() #等待设置绿灯(即等红灯)
import time
import threading event = threading.Event() #创建一个事件 def run1(): #线程方法 counter = 1 #红绿灯时间计数
while True:
print(counter)
if counter % 30 == 0: #红灯30秒
event.set() #事件set,表示Ture
print("绿灯亮")
elif counter % 50 == 0: #绿灯20秒
event.clear() #事件clear,表示False
counter = 0
print("红灯亮")
counter += 1
time.sleep(1) def run2():
while True:
if event.is_set(): #如果event是set,那就是绿灯
print("绿灯,通过")
else:
print("红灯,等待")
event.wait() #否则是红灯,则使用event.wait等待set
time.sleep(5) t_redlight = threading.Thread(target=run1)
t_car = threading.Thread(target=run2)
t_redlight.start()
t_car.start()
八、队列queue
FIFO:先进先出
LIFO:后进先出
1.普通queue
先进先出:
import queue q = queue.Queue() #定义个队列 queue.Queue(maxsize=100):最多放100个数据 q.put("d1") #放入一个数据
q.put("d2")
q.put("d3")
q.put("d4") print(q.qsize()) #打印队列中数据的数量 print(q.get()) #打印q2
print(q.get()) #打印q1
当queue中没有数据时,使用get()会阻塞:
import queue myQueue = queue.Queue() myQueue.put("Hello")
myQueue.put("World") print(myQueue.get())
print(myQueue.get())
print(myQueue.get()) #这一次运行myQueue.get()程序会阻塞
如果不想程序阻塞可以使用get_nowait(),但是会抛异常:
import queue myQueue = queue.Queue() myQueue.put("Hello")
myQueue.put("World") print(myQueue.get())
print(myQueue.get())
print(myQueue.get_nowait()) #如果queue中没有数据,使用get_nowait()会抛异常"queue.Empty"
get_nowait()相当于get(block=False)。也可以使用get(timeout=2)来设置阻塞时间。
我们可以使用try except来捕获异常,就说明queue中已经没有数据了:
import queue myQueue = queue.Queue() myQueue.put("Hello")
myQueue.put("World") while True:
try:
print(myQueue.get_nowait())
except queue.Empty: #捕获异常
print("Queue get member err")
break
2.LifoQueue
后进先出队列:
import queue myQueue = queue.LifoQueue() myQueue.put("Hello")
myQueue.put("World") print(myQueue.get()) #输出World
print(myQueue.get()) #输出Hello
3.PriorityQueue
PriorityQueue,数据可带优先级的队列:
import queue myQueue = queue.PriorityQueue() myQueue.put((-1,"Hello"))
myQueue.put((9,"World"))
myQueue.put((100,"Love"))
myQueue.put((-9,"Good"))
myQueue.put((87,"You")) print(myQueue.get()) #(-9, 'Good')
print(myQueue.get()) #(-1, 'Hello')
print(myQueue.get()) #(9, 'World')
print(myQueue.get()) #(87, 'You')
print(myQueue.get()) #(100, 'Love')
把优先级和数据存放为一个元组,然后作为参数传递给队列。get数据的时候,会根据元组的第一个数据(优先级)排序取出。
最新文章
- MapReduce
- FIS3的简单使用
- 深入理解PHP内核(三)概览-SAPI概述
- 速战速决 (2) - PHP: 数据类型 bool, int, float, string, object, array
- HTMl5/CSS3/Javascript 学习推荐资源
- 精简高效的CSS命名准则/方法
- iOS开发--泛型
- 【转】我的WIN7分辨率是1920*1080,调低后字体模糊
- 查看ssh服务开启状态
- C# 常用工具合集
- [置顶]
 一个demo学会c#
- Robot Framework 环境安装(一)
- 解决gitk显示文件内容中文乱码
- [HAOI2015]按位或(min-max容斥,FWT,FMT)
- springboot系列之-logging
- 数据结构(C语言版)-C语言和C++相关补充
- netcore 发布 到 windows server IIS
- python 获取自身ip
- Mail.Ru Cup 2018 Round 3 Solution
- 2018.12.21 Cmos- RF
热门文章
- 联合概率(joint probability)、分布函数(distribution function)
- 在Winform或WPF中System.Diagnostics.Process.Start的妙用
- OpenCV中基于HOG特征的行人检测
- MFC中获取App,MainFrame,Doc和View类等指针的方法
- WPF里的一些Effect特效
- CF 455A(Boredom-dp)
- wpf采用Xps实现文档显示、套打功能
- WPF特效-实现3D足球效果
- 【C#】获取任意文件的缩略图
- 【图文教程】de4dot实战字符串解密(演示:hishop微分销系统)