在线预览:http://github.lesschina.com/python/base/concurrency/1.并发编程~进程先导篇.htmlhtml
Python3 与 C# 并发编程之~ 进程篇:http://www.javashuo.com/article/p-hufkkonm-d.htmlpython
先写几个问号来概况下今天准备说的内容:(谜底本身解开,文中都有)linux
Ctrl+C
终止进程的本质吗?你知道Kill -9 pid
的真正含义吗?僵尸进程
和孤儿进程
的悲催生产史吗?孤儿找干爹
,僵尸送往生
想知道不?李代桃僵
吗?ps aux | grep xxx
的背后到底隐藏了什么?密密私语
等着你来查看哦~关于帮助文档的说明:git
man
查看,eg:man 2 pipe
help
查看,eg:help(os.pipe)
正式讲课以前,先说些基本概念,难理解的用程序跑跑而后再理解:若有错误欢迎批评指正程序员
并发 :一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行。github
并行 :当一个CPU执行一个线程时,另外一个CPU能够执行另外一个线程,两个线程互不抢占CPU资源,能够同时进行,这种方式咱们称之为并行(Parallel)。(在同一个时间段内,两个或多个程序执行,有时间上的重叠)算法
通俗的举个例子:mongodb
小明、小潘、小张、小康去食堂打饭,4个小伙子Coding了3天,饿爆了,如今须要1分钟内让他们都吃上饭,否则就有可怕的事情发生。编程
按照正常的流程,1分钟可能只够他们一我的打饭,这不行啊,因而有了几种处理方法:json
并发:快快快,一人先吃一口,轮着来,一直喂到大家都饱了(只有一个食堂打饭的窗口)(单核CPU)
并行:
对于操做系统来讲,一个任务就是一个进程(Process),好比打开一个浏览器就是启动一个浏览器进程,打开两个浏览器就启动了两个浏览器进程。
有些进程还不止同时干一件事,好比Word,它能够同时进行打字、拼写检查、打印等事情。在一个进程内部,要同时干多件事,就须要同时运行多个“子任务”,咱们把进程内的这些“子任务”称为线程(Thread)。
因为每一个进程至少要干一件事,因此,一个进程至少有一个线程。像Word这种复杂的进程能够有多个线程,多个线程能够同时执行,多线程的执行方式和多进程是同样的,也是由操做系统在多个线程之间快速切换,让每一个线程都短暂地交替运行,看起来就像同时执行同样。
通俗讲:线程是最小的执行单元,而进程由至少一个线程组成。如何调度进程和线程,彻底由操做系统决定,程序本身不能决定何时执行,执行多长时间
PS:进程5态下次正式讲程序的时候会说,而后就是==> 程序实战不会像今天这样繁琐的,Code很简单,可是不懂这些基本概念日后会吃不少亏,逆天遇到太多坑了,因此避免你们入坑,简单说说这些概念和一些偏底层的东西~看不懂没事,有个印象便可,之后遇到问题至少知道从哪一个方向去解决
示例代码:https://github.com/lotapp/BaseCode/tree/master/python/5.concurrent/Linux
示例代码:https://github.com/lotapp/BaseCode/tree/master/python/5.concurrent/Linux/base
(linux/unix)操做系统提供了一个fork()
系统调用。普通的函数调用,调用一次,返回一次,可是fork()
一次调用,两次返回。
由于操做系统自动把父进程复制了一份,分别在父进程和子进程内返回。为了便于区分,操做系统是这样作的:子进程永远返回0,而父进程返回子进程的ID
查看下帮助文档:
import os
help(os.fork)
Help on built-in function fork in module posix:
fork()
Fork a child process.
Return 0 to child process and PID of child to parent process.
咱们来跑个程序验证一下:(PID返回值若是小于0通常都是出错了)
import os
def main():
print("准备测试~PID:%d" % os.getpid())
pid = os.fork()
if pid == 0:
print("子进程:PID:%d,PPID:%d" % (os.getpid(), os.getppid()))
elif pid > 0:
print("父进程:PID:%d,PPID:%d" % (os.getpid(), os.getppid()))
if __name__ == '__main__':
main()
结果:
准备测试~PID:11247 父进程:PID:11247,PPID:11229 子进程:PID:11248,PPID:11247
能够查看下这个进程是啥:
这个指令若是还不熟悉,Linux基础得好好复习下了:http://www.javashuo.com/article/p-zvrxpwdj-bx.html,简单分析下吧:a是查看全部(能够联想ls -a),u是显示详细信息,x是把不依赖终端的进程也显示出来(终端能够理解为:人与机器交互的那些)
技巧:指令学习能够递增式学习:ps
,ps a
ps au
ps aux
ps ajx
如今验证一下复制一份是什么意思:(代码原封不动,只是在最下面添加了一行输出)
import os
def main():
print("准备测试~PID:%d" % os.getpid())
pid = os.fork() # 子进程被父进程fork出来后,在fork处往下执行
if pid == 0:
print("子进程:PID:%d,PPID:%d" % (os.getpid(), os.getppid()))
elif pid > 0:
print("父进程:PID:%d,PPID:%d" % (os.getpid(), os.getppid()))
print("PID:%d,我是卖报的小行家,大风大雨都不怕" % os.getpid())
if __name__ == '__main__':
main()
输出:(父子进程的执行顺序是系统调度决定的)
准备测试~PID:13081 父进程:PID:13081,PPID:9388 PID:13081,我是卖报的小行家,大风大雨都不怕 子进程:PID:13083,PPID:13081 PID:13083,我是卖报的小行家,大风大雨都不怕
的确是Copy了一份,Code都同样(玩过逆向的应该知道,这份Code其实就放在了.text
(代码段)里面)
子进程被父进程fork出来后,在fork处往下执行(Code和父进程同样),这时候他们就为了抢CPU各自为战了
最后验证一下:各个进程地址空间中数据是彻底独立的(有血缘关系的则是:读时共享,写时复制,好比父子进程等)
import os
def main():
num = 100
pid = os.fork()
# 子进程
if pid == 0:
num += 10
elif pid > 0:
num += 20
print("PID:%d,PPID:%d,Num=%d" % (os.getpid(), os.getppid(), num))
if __name__ == '__main__':
main()
输出:(进程间通讯下一节课会系统的讲,今天只谈Linux和概念)
PID:6369,PPID:6332,Num=120 PID:6376,PPID:6369,Num=110
扩展:(简单了解下便可)
init
or systemd
)先看看Linux启动的图示:(图片来自网络)
查看一下init进程
CentOS进行了优化管理~systemd
其实程序开机启动方式也能够知道区别了:systemctl start mongodb.service
and sudo /etc/init.d/ssh start
Win系列的0号进程:
第5点的说明:(以远程CentOS服务器为例) pstree -ps
systemd(1)─┬─NetworkManager(646)─┬─{NetworkManager}(682)
│ └─{NetworkManager}(684)
├─agetty(1470)
├─auditd(600)───{auditd}(601)
├─crond(637)
├─dbus-daemon(629)───{dbus-daemon}(634)
├─firewalld(645)───{firewalld}(774)
├─lvmetad(483)
├─master(1059)─┬─pickup(52930)
│ └─qmgr(1061)
├─polkitd(627)─┬─{polkitd}(636)
│ ├─{polkitd}(639)
│ ├─{polkitd}(640)
│ ├─{polkitd}(642)
│ └─{polkitd}(643)
├─rsyslogd(953)─┬─{rsyslogd}(960)
│ └─{rsyslogd}(961)
├─sshd(950)───sshd(50312)───sshd(50325)───bash(50326)───pstree(54258)
├─systemd-journal(462)
├─systemd-logind(626)
├─systemd-udevd(492)
└─tuned(951)─┬─{tuned}(1005)
├─{tuned}(1006)
├─{tuned}(1007)
└─{tuned}(1048)
再看一个例子:
[dnt@localhost ~]$ pstree dnt -ps
sshd(50325)───bash(50326)───pstree(54471)
[dnt@localhost ~]$ pstree 50325 -ps
systemd(1)───sshd(950)───sshd(50312)───sshd(50325)───bash(50326)───pstree(54489)
其实你能够在虚拟机试试干死1号进程,就到了登陆页面了【如今大部分系统都不让你这么干了】 kill -9 1
-bash: kill: (1) - 不容许的操做
示例代码:https://github.com/lotapp/BaseCode/tree/master/python/5.concurrent/Linux/base
先看看定义:
孤儿进程 :一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工做。
僵尸进程 :一个进程使用fork建立子进程,若是子进程退出,而父进程并无调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。
通俗讲就是:
孤儿进程:你爸在你以前死了,你成了孤儿,而后你被进程1收养,你死后的事宜你干爹帮你解决
僵尸进程:你挂了,你爸忙着干其余事情没有帮你安葬,你变成了孤魂野鬼,你的怨念一直长存世间
举个例子看看:
import os
import time
def main():
pid = os.fork()
if pid == 0:
print("子进程:Pid=%d,PPID=%d" % (os.getpid(), os.getppid()))
time.sleep(1) # 睡1s
elif pid > 0:
print("父进程:Pid=%d,PPID=%d" % (os.getpid(), os.getppid()))
print("pid=%d,over" % os.getpid())
if __name__ == '__main__':
main()
输出:
import os
import time
def main():
pid = os.fork()
if pid == 0:
print("子进程:Pid=%d,PPID=%d" % (os.getpid(), os.getppid()))
elif pid > 0:
print("父进程:Pid=%d,PPID=%d" % (os.getpid(), os.getppid()))
while True:
print("父亲我忙着呢,没时间管你个小屁孩")
time.sleep(1)
print("pid=%d,over" % os.getpid())
if __name__ == '__main__':
main()
输出+测试:
其实僵尸进程的危害真的很大,这也就是为何有些人为了追求效率过分调用底层,不考虑本身实际状况最后发现还不如用自托管的效率高
僵尸进程是杀不死的,必须杀死父类才能完全解决它们,下面说说怎么让父进程为子进程‘收尸’
讲解以前先简单分析一下上面的Linux指令(防止有人不太清楚)
kill -9 pid
==> 之前逆天说过,是无条件杀死进程,其实这种说法不许确,应该是发信号给某个进程
-9指的就是信号道里面的SIGKILL
(信号终止),你写成kill -SIGKILL pid
也同样
-9只是系统给的一种简化写法(好像记得1~31信号,各个Linux中都差很少,其余的有点不同)
dnt@MZY-PC:~/桌面/work/PC/python/Thread/Linux kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
通常搜索进程中的某个程序通常都是用这个:ps -aux | grep xxx
(|
其实就是管道,用于有血缘关系进程间通讯,等会讲)
若是安装了pstree
就更方便了:pstree 13570 -ps
(Ubuntu自带,CentOS要装下yum install psmisc
)
systemd(1)───systemd(1160)───gnome-terminal-(21604)───bash(8169)───python3(13570)───python3(13571)
扩展:咱们平时Ctrl+C
其实就是给 2)SIGINT
发一个信号
代码实例:https://github.com/lotapp/BaseCode/tree/master/python/5.concurrent/Linux/wait
步入正题:
Python的Wait和C系列的稍有不一样,这边重点说说Python:
help(os.wait)
Help on built-in function wait in module posix:
wait()
Wait for completion of a child process.
Returns a tuple of information about the child process:
(pid, status)
os.wait()
返回一个元组,第一个是进程id,第二个是状态,正常退出是0,被九号信号干死就返回9
来个案例:
import os
import time
def main():
pid = os.fork()
if pid == 0:
print("子进程:Pid=%d,PPID=%d" % (os.getpid(), os.getppid()))
elif pid > 0:
print("父进程:Pid=%d,PPID=%d" % (os.getpid(), os.getppid()))
wpid, status = os.wait()
print(wpid)
print(status)
print("pid=%d,over" % os.getpid())
if __name__ == '__main__':
main()
输出:
父进程:Pid=22322,PPID=10139 子进程:Pid=22323,PPID=22322 pid=22323,over 22323 0 pid=22322,over
演示一下被9号信号干死的状况:
import os
import time
def main():
pid = os.fork()
if pid == 0:
print("子进程:Pid=%d,PPID=%d" % (os.getpid(), os.getppid()))
while True:
print("孩子老卵,就是不听话")
time.sleep(1)
elif pid > 0:
print("父进程:Pid=%d,PPID=%d" % (os.getpid(), os.getppid()))
wpid, status = os.wait() # 调用一次只能回收一次,想都回收,就来个while循环,-1则退出
print(wpid)
print(status)
if status == 0:
print("正常退出")
elif status == 9:
print("被信号9干死了")
print("pid=%d,over" % os.getpid())
if __name__ == '__main__':
main()
输出:
扩展:(回收全部子进程,status返回-1表明没有子进程了,Python里面没有子进程会触发异常)
import os
import time
def main():
i = 0
while i < 3:
pid = os.fork()
# 防止产生孙子进程(能够本身思索下)
if pid == 0:
break
i += 1
if i == 0:
print("i=%d,子进程:Pid=%d,PPID=%d" % (i, os.getpid(), os.getppid()))
time.sleep(1)
elif i == 1:
print("i=%d,子进程:Pid=%d,PPID=%d" % (i, os.getpid(), os.getppid()))
time.sleep(1)
elif i == 2:
print("i=%d,子进程:Pid=%d,PPID=%d" % (i, os.getpid(), os.getppid()))
time.sleep(3)
while True:
print("(PID=%d)我又老卵了,怎么滴~" % os.getpid())
time.sleep(3)
elif i==3: # 循环结束后,父进程才会退出,这时候i=3
print("i=%d,父进程:Pid=%d,PPID=%d" % (i, os.getpid(), os.getppid()))
while True:
print("等待回收子进程")
try:
wpid, status = os.wait()
print(wpid)
print(status)
if status == 0:
print("正常退出")
elif status == 9:
print("被信号9干死了")
except OSError as ex:
print(ex)
break
print("pid=%d,over,ppid=%d" % (os.getpid(), os.getppid()))
if __name__ == '__main__':
main()
演示:看最后一句输出,父进程扫尾工做作完就over了
代码实例:https://github.com/lotapp/BaseCode/tree/master/python/5.concurrent/Linux/waitpid
上面说的wait
方法是阻塞进程的一种方式,waitpid
能够设置不阻塞进程
help(os.waitpid)
Help on built-in function waitpid in module posix:
waitpid(pid, options, /)
Wait for completion of a given child process.
Returns a tuple of information regarding the child process:
(pid, status)
The options argument is ignored on Windows.
等待进程id为pid的进程结束,返回一个tuple,包括进程的进程ID和退出信息(和os.wait()同样),参数options会影响该函数的行为。在默认状况下,options的值为0。
官方原话是这样的:(英语好的能够看看我有没有翻译错)
If pid is greater than 0, waitpid() requests status information for that specific process. If pid is 0, the request is for the status of any child in the process group of the current process. If pid is -1, the request pertains to any child of the current process. If pid is less than -1, status is requested for any process in the process group -pid (the absolute value of pid).
options:(宏)
os.WNOHANG - 若是没有子进程退出,则不阻塞waitpid()调用
os.WCONTINUED - 若是子进程从stop状态变为继续执行,则返回进程自前一次报告以来的信息。
os.WUNTRACED - 若是子进程被中止过并且其状态信息尚未报告过,则报告子进程的信息。
补充:
用法和wait
差很少,就是多了一个不阻塞线程的方法:
import os
import time
def main():
pid = os.fork()
if pid == 0:
print("[子进程]PID:%d,PPID:%d" % (os.getpid(), os.getppid()))
time.sleep(2)
elif pid > 0:
print("[父进程]PID:%d,PPID:%d" % (os.getpid(), os.getppid()))
while True:
try:
wpid, status = os.waitpid(-1, os.WNOHANG)
if wpid > 0:
print("回收子进程wpid:%d,状态status:%d" % (wpid, status))
except OSError:
print("没有子进程了")
break
print("父进程忙着挣钱养家呢~")
time.sleep(3)
print("[over]PID:%d,PPID:%d" % (os.getpid(), os.getppid()))
if __name__ == '__main__':
main()
输出:
[父进程]PID:1371,PPID:29604 [子进程]PID:1372,PPID:1371 父进程忙着挣钱养家呢~ [over]PID:1372,PPID:1371 回收子进程wpid:1372,状态status:0 父进程忙着挣钱养家呢~ 没有子进程了 [over]PID:1371,PPID:29604
代码实例:https://github.com/lotapp/BaseCode/blob/master/python/5.concurrent/Linux/wait3.py
help(os.wait3)
Help on built-in function wait3 in module posix:
wait3(options)
Wait for completion of a child process.
Returns a tuple of information about the child process:
(pid, status, rusage)
help(os.wait4)
Help on built-in function wait4 in module posix:
wait4(pid, options)
Wait for completion of a specific child process.
Returns a tuple of information about the child process:
(pid, status, rusage)
这个是Python扩展的方法,用法和wait、waitpid
差很少,我就不一个个的举例子了,以wait3
为例
import os
import time
def main():
pid = os.fork()
if pid == 0:
print("[子进程]PID:%d,PPID:%d" % (os.getpid(), os.getppid()))
time.sleep(2)
elif pid > 0:
print("[父进程]PID:%d,PPID:%d" % (os.getpid(), os.getppid()))
while True:
try:
wpid, status, rusage = os.wait3(os.WNOHANG)
if wpid > 0:
print("回收子进程wpid:%d,状态status:%d\n详细信息:%s" % (wpid, status,
rusage))
except OSError:
print("没有子进程了")
break
print("父进程忙着挣钱养家呢~")
time.sleep(3)
print("[over]PID:%d,PPID:%d" % (os.getpid(), os.getppid()))
if __name__ == '__main__':
main()
输出
[父进程]PID:2638,PPID:29604 [子进程]PID:2639,PPID:2638 父进程忙着挣钱养家呢~ [over]PID:2639,PPID:2638 回收子进程wpid:2639,状态status:0 详细信息:resource.struct_rusage(ru_utime=0.0052179999999999995, ru_stime=0.0052179999999999995, ru_maxrss=7032, ru_ixrss=0, ru_idrss=0, ru_isrss=0, ru_minflt=869, ru_majflt=0, ru_nswap=0, ru_inblock=0, ru_oublock=0, ru_msgsnd=0, ru_msgrcv=0, ru_nsignals=0, ru_nvcsw=2, ru_nivcsw=0) 父进程忙着挣钱养家呢~ 没有子进程了 [over]PID:2638,PPID:29604
代码实例:https://github.com/lotapp/BaseCode/blob/master/python/5.concurrent/Linux/execl.py
以前有说fork后,至关于copy了一份,.text里面放的是代码段,若是想要调用另外一个程序,可使用execlxxx
,他会把.text里面的代码替换掉
help(os.execl)
Help on function execl in module os:
execl(file, *args)
execl(file, *args)
Execute the executable file with argument list args, replacing the
current process.
help(os.execlp)
Help on function execlp in module os:
execlp(file, *args)
execlp(file, *args)
Execute the executable file (which is searched for along PATH)
with argument list args, replacing the current process.
来看个例子,os.execl("绝对路径","参数或者指令")
or os.execlp("Path中包含的命令","参数或者指令")
提示:查看命令路径:eg:which ls
import os
def main():
pid = os.fork()
if pid == 0:
# 第二个参数不能为None,,第一个路径为绝对路径 eg:os.execl("/bin/ls"," ")
os.execl("/bin/ls", "ls", "-al")
# os.execlp("ls", "ls", "-al") # 执行Path环境变量能够搜索到的命令
print("exec函数族会替换代码,我是不会被执行的,除非上面的出问题了")
print("-" * 10) # 父进程执行一次,子进程不会执行
if __name__ == '__main__':
main()
注意输出信息:os.execlp("ls", "ls", "-al")
---------- 总用量 28 drwxrwxr-x 6 dnt dnt 4096 7月 26 05:23 . drwxrwxr-x 9 dnt dnt 4096 7月 24 20:55 .. drwxr-xr-x 2 dnt dnt 4096 7月 19 14:47 .ipynb_checkpoints drwxrwxr-x 6 dnt dnt 4096 7月 26 06:27 Linux -rw-rw-r-- 1 dnt dnt 93 7月 26 05:49 temp.py drwxrwxr-x 2 dnt dnt 4096 7月 24 15:29 .vscode drwxrwxr-x 2 dnt dnt 4096 7月 25 12:18 进程
代码实例:https://github.com/lotapp/BaseCode/tree/master/python/5.concurrent/Linux/进程通讯/1.file
讲管道以前,先说个简单的:经过文件进行通讯
来一个简单读写的案例先适应下文件操做:
!ls
# 这种写法相似于Net的 using 托管
with open("test.txt", "w") as f:
f.write("从前有座山,山上有座庙,庙里有个老和尚和一个小和尚。有一天,老和尚对小和尚说:")
with open("test.txt", "r") as f:
data = f.read()
print(data)
!ls
来个简单的案例:
import os
import time
def main():
pid = os.fork()
if pid > 0:
print("父进程(pid=%d)开始写入:" % os.getpid())
with open(str(pid), "w") as f:
f.write("[父进程写入]从前有座山,山上有座庙,庙里有个老和尚和一个小和尚。有一天,老和尚对小和尚说:\n")
time.sleep(2)
print("父进程(pid=%d)开始读取:" % os.getpid())
with open(str(pid), "r") as f:
print(f.read())
wpid, status = os.wait() # 收尸
print("pid=%d已经回收,status:%d" % (wpid, status))
elif pid == 0:
print("子进程(pid=%d)开始读取:" % os.getpid())
with open(str(os.getpid()), "r") as f:
print(f.read())
print("子进程(pid=%d)开始追加:" % os.getpid())
with open(str(os.getpid()), "a") as f: # 追加
f.write("[子进程追加]从前有座山,山上有座庙,庙里有个老和尚和一个小和尚。有一天,老和尚对小和尚说:\n")
print("\n进程(pid=%d)完蛋了" % os.getpid())
if __name__ == '__main__':
main()
图示:
代码实例:https://github.com/lotapp/BaseCode/tree/master/python/5.concurrent/Linux/进程通讯/2.Queue
from multiprocessing import Queue
help(Queue)
Help on method Queue in module multiprocessing.context:
Queue(maxsize=0) method of multiprocessing.context.DefaultContext instance
Returns a queue object
实例化对象帮助文档:
from multiprocessing import Queue
q = Queue(2)
help(q)
Help on Queue in module multiprocessing.queues object:
class Queue(builtins.object)
| Methods defined here:
|
| __getstate__(self)
|
| __init__(self, maxsize=0, *, ctx)
| Initialize self. See help(type(self)) for accurate signature.
|
| __setstate__(self, state)
|
| cancel_join_thread(self)
|
| close(self)
|
| empty(self)
|
| full(self)
|
| get(self, block=True, timeout=None)
|
| get_nowait(self)
|
| join_thread(self)
|
| put(self, obj, block=True, timeout=None)
|
| put_nowait(self, obj)
|
| qsize(self)
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)
详细内容(如:非阻塞、池中应用等)下次讲代码的时候会详说,简单看个例子:
import os
from multiprocessing import Queue
def main():
q = Queue(1) # 建立一个容量为1的队列(只能put接受1条,get取出后才能够放)
pid = os.fork()
if pid == 0:
print("[子进程]:pid:%d,ppid:%d" % (os.getpid(), os.getppid()))
q.put("父亲大人,我能够出去玩吗?")
output = q.get()
print("[子进程]收到父亲大人回复:%s" % output)
elif pid > 0:
print("[父进程]:pid:%d,ppid:%d" % (os.getppid(), os.getppid()))
output = q.get() # 儿子天天出去都会说,等待ing
print("[父进程]收到儿子的话:%s" % output)
q.put("准了")
wpid, status = os.wait()
print("[父进程]帮pid:%d收尸,状态:%d" % (wpid, status))
print("[OVER]:pid:%d,ppid:%d" % (os.getpid(), os.getppid()))
if __name__ == '__main__':
main()
输出:
[父进程]:pid:12403,ppid:12403 [子进程]:pid:744,ppid:743 [父进程]收到儿子的话:父亲大人,我能够出去玩吗? [子进程]收到父亲大人回复:准了 [OVER]:pid:744,ppid:743 [父进程]帮pid:744收尸,状态:0 [OVER]:pid:743,ppid:12403
代码实例:https://github.com/lotapp/BaseCode/tree/master/python/5.concurrent/Linux/进程通讯/3.pipe
知识普及:
若是终端的概念还不清楚能够看以前的文章:http://www.javashuo.com/article/p-dgpdyqpb-x.html
help(os.pipe)
Help on built-in function pipe in module posix:
pipe()
Create a pipe.
Returns a tuple of two file descriptors:
(read_fd, write_fd)
匿名管道的模式其实咱们平时都在用,只是不知道而已,好比:ps aux | grep "python"
这个 |
就是匿名管道
本质:内核的缓冲区,不占用磁盘空间(能够当作伪文件)【默认4k,相差不大的状况下系统会自动微调大小】
咱们来看一下:ulimit -a
Ubuntu 18.04
core file size (blocks, -c) 0 data seg size (kbytes, -d) unlimited scheduling priority (-e) 0 file size (blocks, -f) unlimited pending signals (-i) 14894 max locked memory (kbytes, -l) 16384 max memory size (kbytes, -m) unlimited open files (-n) 1024 pipe size (512 bytes, -p) 8 POSIX message queues (bytes, -q) 819200 real-time priority (-r) 0 stack size (kbytes, -s) 8192 cpu time (seconds, -t) unlimited max user processes (-u) 14894 virtual memory (kbytes, -v) unlimited file locks (-x) unlimited
CentOS 7.5
core file size (blocks, -c) 0 data seg size (kbytes, -d) unlimited scheduling priority (-e) 0 file size (blocks, -f) unlimited pending signals (-i) 3543 max locked memory (kbytes, -l) 64 max memory size (kbytes, -m) unlimited open files (-n) 1024 pipe size (512 bytes, -p) 8 POSIX message queues (bytes, -q) 819200 real-time priority (-r) 0 stack size (kbytes, -s) 8192 cpu time (seconds, -t) unlimited max user processes (-u) 3543 virtual memory (kbytes, -v) unlimited file locks (-x) unlimited
原理:算法实现的环形队列(队列:先进先出)
特色:
4的意思是这样的(网上找个图,而后改造一下)
验证一下3:
为何是3开始呢?查看一下源码:(https://github.com/python/cpython/blob/v3.7.0/Lib/pty.py)
STDIN_FILENO = 0 # 看这:文件描述符输入(读端)
STDOUT_FILENO = 1 # 看这:文件描述符输出(写端)
STDERR_FILENO = 2 # 已经被占用了0~2了,天然从3开始
# 下面的不用你会,上面Code看完,咱们的目的就达到了,下面看看便可
def fork():
"""fork() -> (pid, master_fd) Fork分叉后让子进程成为控制终端的会话领导者"""
try:
pid, fd = os.forkpty() # 设置会话领导
except (AttributeError, OSError):
pass
else: # 没有错误执行
if pid == CHILD:
os.setsid()
return pid, fd
master_fd, slave_fd = openpty()
pid = os.fork()
if pid == CHILD:
# 创建一个新的会话
os.setsid()
os.close(master_fd)
# 把子进程里面的 slave_fd 重定向到 stdin/stdout/stderr
os.dup2(slave_fd, STDIN_FILENO)
os.dup2(slave_fd, STDOUT_FILENO)
os.dup2(slave_fd, STDERR_FILENO)
if (slave_fd > STDERR_FILENO):
os.close (slave_fd)
# 显式打开tty,使它成为一个tty控制
tmp_fd = os.open(os.ttyname(STDOUT_FILENO), os.O_RDWR)
os.close(tmp_fd)
else:
os.close(slave_fd)
# Parent and child process.
return pid, master_fd
画个大纲图理解一下:(读的时候关闭写,写的时候关闭读)
结合单向传输理解一下:(父子只能一我的写,另外一我的只能读) 简单概况上图:子进程只读,父进程只写 or 子进程只写,父进程只读 (若是想要相互读写通讯~两根管道走起)
简单分析一下 ps aux | grep python
,原本ps aux是准备在终端中输出的,如今写入内核缓冲区了,grep从内核缓冲区里面读取,把符合条件的输出到终端
终端文件描述获取:
import sys
sys.stdin.fileno() # STDIN_FILENO = 0:文件描述符输入(读端)
sys.stdout.fileno() # STDOUT_FILENO = 1:看这:文件描述符输出(写端)
咱们用程序实现一个一样效果的:(grep
有颜色,其实就是加了--color=auto
)
import os
import sys
def main():
# 建立内核缓存区(伪文件)
read_fd, write_fd = os.pipe()
print("read_fd:%s\nwrite_fd:%s" % (read_fd, write_fd))
pid = os.fork()
if pid > 0:
print("[父进程]pid=%d,ppid=%d" % (os.getpid(), os.getppid()))
# 写或者读,则须要关闭另外一端(防止本身写本身读)
os.close(read_fd)
# dup2(oldfd,newfd) 把写端数据重定向到文件描述符输出端
os.dup2(write_fd, sys.stdout.fileno()) # STDOUT_FILENO==1 (文件描述符输出,写端)
# 僵桃李代
os.execlp("ps", "ps", "aux")
elif pid == 0:
print("[子进程]pid=%d,ppid=%d" % (os.getpid(), os.getppid()))
# 子进程如今须要读,关闭写段
os.close(write_fd)
# dup2(oldfd,newfd) 把读端数据重定向到文件描述符输入端
os.dup2(read_fd, sys.stdin.fileno()) # STDOUT_FILENO == 0 (文件描述符输入,读端)
# 僵桃李代 (默认是从终端读,重定向后从内核缓冲区读)
os.execlp("grep", "grep", "python", "--color=auto")
if __name__ == '__main__':
main()
输出:(用到的函数:os.pipe()
and os.dup2(oldfd,newfd)
)
PS:在C系列里面若是你该关闭的fd没关,会资源浪费,python好像作了处理,没可以问题复现,因此仍是建议父子一方只读,一方只写
概念再理解:fork了两个子进程,则文件描述符被复制了2份,你们文件描述符的三、4都指向了pipe管道
的read_fd
和write_fd
来张图理解一下,哪些fd被close了(若是让子进程之间通讯,父进程由于不读不写,因此读写都得关闭) 代码演示:(此次注释很全)
import os
import sys
import time
def main():
read_fd, write_fd = os.pipe() # 能够思考为啥在上面建立管道(提示.text代码段都同样)
i = 0
while i < 2:
pid = os.fork()
# 防止子进程生猴子
if pid == 0:
break
i += 1
# 子进程1
if i == 0:
print("[子进程%d]pid=%d,ppid=%d" % (i, os.getpid(), os.getppid()))
# 准备重定向到写端,因此先关了读端
os.close(read_fd)
os.dup2(write_fd, sys.stdout.fileno()) # STDOUT_FILENO == 1 (文件描述符输出,写端)
# 僵桃李代
os.execlp("ps", "ps", "-aux")
# 僵桃李代后,.text代码段都被替换了,天然不会执行
print("我是不会执行的,不信你看呗")
elif i == 1:
print("[子进程%d]pid=%d,ppid=%d" % (i, os.getpid(), os.getppid()))
# 准备重定向到读端,因此先关了写端
os.close(write_fd)
os.dup2(read_fd, sys.stdin.fileno()) # STDIN_FILENO == 0 (文件描述符输入,读端)
# 僵桃李代 ”bash“是查找关键词,你写你想找的字符串便可
os.execlp("grep", "grep", "bash", "--color=auto")
# 僵桃李代后,.text代码段都被替换了,天然不会执行
print("我是不会执行的,不信你看呗")
elif i == 2:
print("[父进程]pid=%d,ppid=%d" % (os.getpid(), os.getppid()))
# 我不写不读
os.close(read_fd)
os.close(write_fd)
# 为了你们熟练掌握wait系列,此次用waitpid
while True:
info = ()
try:
info = os.waitpid(-1, os.WNOHANG) # 非阻塞的方式回收全部子进程
except OSError:
break # waitpid返回-1的时候,Python会抛出异常
if info[0] > 0:
print("父进程收尸成功:pid=%d,ppid=%d,状态status:%d" %
(os.getpid(), os.getppid(), info[1]))
print("父进程作其余事情...")
time.sleep(0.005) # 休息 0.005s
print("[父进程-遗言]pid=%d,ppid=%d" % (os.getpid(), os.getppid()))
if __name__ == '__main__':
main()
结果:
[父进程]pid=18678,ppid=27202 [子进程0]pid=18679,ppid=18678 [子进程1]pid=18680,ppid=18678 父进程作其余事情... 父进程作其余事情... 父进程作其余事情... 父进程作其余事情... dnt 4622 0.0 0.1 24880 5688 pts/2 Ss 05:28 0:00 bash 父进程作其余事情... dnt 15419 0.0 0.1 25152 5884 pts/0 Ss+ 06:29 0:00 /bin/bash dnt 18680 0.0 0.0 16184 1044 pts/4 S+ 13:25 0:00 grep bash --color=auto dnt 27202 0.0 0.1 25012 6052 pts/4 Ss 08:25 0:00 bash 父进程收尸成功:pid=18678,ppid=27202,状态status:0 父进程作其余事情... 父进程收尸成功:pid=18678,ppid=27202,状态status:0 父进程作其余事情... [父进程-遗言]pid=18678,ppid=27202
# 说管道读写以前,先复习个知识点:
bts = "尴尬".encode()
b_str = bts.decode()
print(bts)
print(b_str)
上面知识点忘了能够复习一下:http://www.javashuo.com/article/p-tmcacaan-d.html
用到的函数:(这个就不须要使用dup2
来重定向到终端了【有血缘关系的进程之间通讯,并不依赖于终端显示】)
os.write(fd, str)
写入字符串到文件描述符 fd中. 返回实际写入的字符串长度
os.read(fd, n)
从文件描述符 fd 中读取最多 n 个字节,返回包含读取字节的字符串
若是文件描述符fd对应文件已达到结尾, 返回一个空字符串
举个父子间通讯的例子(比C系列简单太多)【下次讲的通用Code会更简单】
import os
def close_fd(*fd_tuple_args):
"""关闭fd,fd_tuple_args是可变参数"""
for item in fd_tuple_args:
os.close(item[0])
os.close(item[1])
def main():
# 管道是单向的,相互读写,那就建立两个管道
fd_tuple1 = os.pipe() # 进程1写,进程2读
fd_tuple2 = os.pipe() # 进程2写,进程1读
i = 0
while i < 2:
pid = os.fork()
if pid == 0:
break
i += 1
# 子进程1
if i == 0:
print("[子进程]pid:%d,ppid:%d" % (os.getpid(), os.getppid()))
os.close(fd_tuple1[0]) # 进程1写,则关闭下读端
msg_str = "进程1说:兄弟,今天撸串吗?"
os.write(fd_tuple1[1], msg_str.encode()) # 把字符串xxx转换成bytes
# 不读的我关闭掉:
os.close(fd_tuple2[1]) # 进程2写,我不须要写,关闭写端
bts = os.read(fd_tuple2[0], 1024)
print("[子进程1]", bts.decode())
exit(0) # 退出后就不执行下面代码块语句了
# 子进程2
elif i == 1:
print("[子进程2]pid:%d,ppid:%d" % (os.getpid(), os.getppid()))
os.close(fd_tuple1[1]) # 进程2读,则关闭下写端
bts = os.read(fd_tuple1[0], 1024)
print("[子进程2]", bts.decode())
# 不读的我关闭掉:
os.close(fd_tuple2[0]) # 进程2写,关闭读端
msg_str = "进程2说:能够能够~"
os.write(fd_tuple2[1], msg_str.encode()) # 把字符串xxx转换成bytes
exit() # 不加参数默认是None
# 父进程
elif i == 2:
print("[父进程]pid:%d,ppid:%d" % (os.getpid(), os.getppid()))
# 父进程不读不写,就看看
close_fd(fd_tuple1, fd_tuple2)
# 收尸ing
while True:
try:
wpid, status = os.wait()
print("[父进程~收尸]子进程PID:%d 的状态status:%d" % (wpid, status))
except OSError:
break
# 子进程都exit()退出了,不会执行这句话了
print("[父进程遗言]pid:%d,ppid:%d" % (os.getpid(), os.getppid()))
if __name__ == '__main__':
main()
输出结果:
[父进程]pid:12002,ppid:27202 [子进程2]pid:12004,ppid:12002 [子进程]pid:12003,ppid:12002 [子进程2] 进程1说:兄弟,今天撸串吗? [子进程1] 进程2说:能够能够~ [父进程~收尸]子进程PID:12003 的状态status:0 [父进程~收尸]子进程PID:12004 的状态status:0 [父进程遗言]pid:12002,ppid:27202
队列的get
,put
方法默认也是阻塞的,若是想非阻塞能够调用get_nowait
和put_nowait
来变成非阻塞,那pipe管道呢?
C系列通常使用fcntl
来实现,Python进行了封装,咱们能够经过os.pipe2(os.O_NONBLOCK)
来设置非阻塞管道
help(os.pipe2) Help on built-in function pipe2 in module posix: pipe2(flags, /) Create a pipe with flags set atomically. Returns a tuple of two file descriptors: (read_fd, write_fd) flags can be constructed by ORing together one or more of these values: O_NONBLOCK, O_CLOEXEC.
举个例子:
import os
import time
def main():
r_fd, w_fd = os.pipe2(os.O_NONBLOCK | os.O_CLOEXEC)
pid = os.fork()
if pid == 0:
print("子进程:pid=%d,ppid=%d" % (os.getpid(), os.getppid()))
time.sleep(0.5)
# 和父进程进行通讯
os.close(r_fd)
os.write(w_fd, "老爸,我出去玩了~".encode())
exit(0) # 子进程退出
elif pid > 0:
print("父进程:pid=%d,ppid=%d" % (os.getpid(), os.getppid()))
# 读儿子的留言
os.close(w_fd)
b_msg = b""
while True:
try:
b_msg = os.read(r_fd, 1) # 没有数据就出错(通常都是等待一会,也能够和信号联合使用)
except OSError:
print("儿子怎么没有留言呢?")
print("父进程:作其余事情...")
if len(b_msg) > 0:
break
time.sleep(0.1)
# 继续读剩下的消息
b_msg += os.read(r_fd, 1024)
print("儿子留言:", b_msg.decode())
wpid, status = os.wait()
print("帮儿子作扫尾工做:pid=%d,status=%d" % (wpid, status))
print("父进程遗言:pid=%d,status=%d" % (os.getpid(), os.getppid()))
if __name__ == '__main__':
main()
输出:
父进程:pid=31430,ppid=27202 子进程:pid=31431,ppid=31430 儿子怎么没有留言呢? 父进程:作其余事情... 儿子怎么没有留言呢? 父进程:作其余事情... 儿子怎么没有留言呢? 父进程:作其余事情... 儿子怎么没有留言呢? 父进程:作其余事情... 儿子怎么没有留言呢? 父进程:作其余事情... 父进程:作其余事情... 儿子留言: 老爸,我出去玩了~ 帮儿子作扫尾工做:pid=31431,status=0 父进程遗言:pid=31430,status=27202
扩展:
代码实例:https://github.com/lotapp/BaseCode/tree/master/python/5.concurrent/Linux/进程通讯/4.fifo
p
,大小为0
的管道文件(伪文件,大小始终为0)对2的验证: 其实你用ll
来查看,就是文件类型为p
的文件(大小始终为0)
Linux底层提供了mkfifo
函数,Python建立使用os.mkfifo()
画个图来看3:
知识普及:
help(os.open)
Help on built-in function open in module posix:
open(path, flags, mode=511, *, dir_fd=None)
Open a file for low level IO. Returns a file descriptor (integer).
If dir_fd is not None, it should be a file descriptor open to a directory,
and path should be relative; path will then be relative to that directory.
dir_fd may not be implemented on your platform.
If it is unavailable, using it will raise a NotImplementedError.
flags
-- 该参数能够是如下选项,多个使用 |
隔开:
不少人直接使用了Open方法open(fifo_path, "r")
和open(fifo_path, "w")
貌似也是能够的,可是不推荐
咱们使用官方推荐的方法:
fifo操做很是简单,和文件IO操做几乎同样,看个无血缘关系进程通讯的例子:
进程1源码:r_fifo.py
import os
def main():
file_name = "fifo_file"
if not os.path.exists(file_name):
os.mkfifo(file_name)
fd = os.open(file_name, os.O_RDONLY) # 只读(阻塞)
while True:
b_msg = os.read(fd, 1024)
if len(b_msg) > 0:
print(b_msg.decode())
if __name__ == '__main__':
main()
进程2源码:w_fifo.py
import os
import time
def main():
file_name = "fifo_file"
if not os.path.exists(file_name):
os.mkfifo(file_name)
fd = os.open(file_name, os.O_WRONLY) # 只写
while True:
time.sleep(1) # 模拟一下实际生产环境下的 读快写慢
try:
os.write(fd, "我是说话有魔性,喝水会长胖的小明同窗".encode()) # 写入bytes
except BrokenPipeError:
print("若是读端所有关闭,管道破裂,进程自动被终止")
break
if __name__ == '__main__':
main()
作个读端的测试:
读写双测:(fifo文件大小始终为0,只是伪文件而已)
扩展一下,若是你经过终端读写呢?(同上)
再来个读写的案例
3.rw_fifo1.py
import os
def main():
file_name = "fifo_temp"
if not os.path.exists(file_name):
os.mkfifo(file_name)
fd = os.open(file_name, os.O_RDWR) # 你输入os.O_rw就会有这个选项了,不用硬记
msg = os.read(fd, 1024).decode() # 阻塞的方式,不用担忧
print("[进程2]%s" % msg)
os.write(fd, "小明啊,你忘记你长几斤肉了?".encode())
if __name__ == '__main__':
main()
rw_fifo2.py
import os import time def main(): file_name = "fifo_temp" if not os.path.exists(file_name): os.mkfifo(file_name) fd = os.open(file_name, os.O_RDWR) # 你输入os.O_rw就会有这个选项了,不用硬记 os.write(fd, "小潘,撸串去不?".encode()) time.sleep(3) # 防止本身写的被本身读了 msg = os.read(fd, 1024).decode() # 阻塞的方式,不用担忧 print("[进程1]]%s" % msg) if __name__ == '__main__': main()
来个父子间通讯:(代码比较简单,和上面差很少,看看便可)
import os
def main():
file_name = "fifo_test"
if not os.path.exists(file_name):
os.mkfifo(file_name)
fd = os.open(file_name, os.O_RDWR) # 读写方式打开文件描述符 (O_RDONLY | O_WRONLY)
pid = os.fork()
if pid == 0:
print("子进程:PID:%d,PPID:%d" % (os.getpid(), os.getppid()))
os.write(fd, "子进程说:老爸,我想出去玩".encode()) # 写
msg = os.read(fd, 1024).decode() # 读
print("[子进程]%s" % msg)
elif pid > 0:
print("父进程:PID:%d,PPID:%d" % (os.getpid(), os.getppid()))
msg = os.read(fd, 1024).decode() # 阻塞方式,不用担忧
print("[父进程]%s" % msg)
os.write(fd, "父进程说:去吧乖儿子".encode())
# 给子进程收尸
wpid, status = os.wait()
print("父进程收尸:子进程PID=%d,PPID=%d" % (wpid, status))
print("进程遗言:PID=%d,PPID=%d" % (os.getpid(), os.getppid())) # 剩下的代码段
if __name__ == '__main__':
main()
输出:
父进程:PID:21498,PPID:20943 子进程:PID:21499,PPID:21498 [父进程]子进程说:老爸,我想出去玩 [子进程]父进程说:去吧乖儿子 进程遗言:PID=21499,PPID=21498 父进程收尸:子进程PID=21499,PPID=0 进程遗言:PID=21498,PPID=20943
代码实例:https://github.com/lotapp/BaseCode/tree/master/python/5.concurrent/Linux/进程通讯/5.mmap
好处:内存操做,比IO快
缺点:和文件同样不会像管道同样阻塞(读的可能不全,须要本身考虑读写效率)
画个简单的图示: PS:内存映射一个文件并不会致使整个文件被读取到内存中:
以Linux为例,简单解析一下帮助文档:(加粗的是必填参数)
mmap.mmap(fileno,length[,flags=MAP_SHARED][,prot=PROT_WRITE|PROT_READ][,access=ACCESS_DEFAULT][,offset])
文件描述fd
os.open()
直接打开fdf.fileno()
文件大小 os.path.getsize(path)
传进去就能够了# 这个够明了了,\0转换成二进制就是\x00
"\0".encode()
# 老规矩,开始以前,扩充一个小知识点:(空字符串和'\0'区别)
a = "" # 空字符串 (Python里面没有char类型)
b = "\x00" # `\0` 的二进制写法
c = "\0"
print(a)
print(b)
print(c)
print(len(a))
print(len(b))
print(len(c))
看个简单的案例快速熟悉mmap模块:(大文件处理这块先不说,之后要是有机会讲数据分析的时候会再提)
m.size() # 查看文件大小
m.seek(0) # 修改Postion位置
m.tell() # 返回 m 对应文件的Postion位置
m.read().translate(None, b"\x00") # 读取全部内容并把\0删除
m.closed # 查看mmap是否关闭
# 支持切片操做
m[0:10] # 取值
m[0:10] = b"1234567890" # 赋值
# 对自行模式大文件处理的同志,给个提示
m.readline().decode() # 读一行,并转换成str
m.size()==m.tell() # while循环退出条件
熟悉一下上面几个方法:
import os
import mmap
def create_file(filename, size):
"""初始化一个文件,并把文件扩充到指定大小"""
with open(filename, "wb") as f:
f.seek(size - 1) # 改变流的位置
f.write(b"\x00") # 在末尾写个`\0`
def main():
create_file("mmap_file", 4096) # 建立一个4k的文件
with mmap.mmap(os.open("mmap_file", os.O_RDWR), 0) as m: # 建立映射
print(m.size()) # 查看文件大小
m.resize(1024) # 从新设置文件大小
print(len(m)) # len也同样查看文件大小
print(m.read().translate(None, b"\x00")) # 读取全部内容并把\0删除
print(m.readline().decode()) # 读取一行,bytes转成str
print(m.tell()) # 返回 m 对应文件的当前位置
m.seek(0) # 修改Postion位置
print(m.tell()) # 返回 m 对应文件的当前位置
print(m[0:10]) # 支持切片操做
print("postion_index:%d" % m.tell())
m[0:10] = b"1234567890" # 赋值
print("postion_index:%d" % m.tell())
print(m[0:10]) # 取值
print("postion_index:%d" % m.tell())
print(m[:].decode()) # 所有读出来
print(m.closed) # 查看mmap是否关闭
if __name__ == '__main__':
main()
输出:(测试了一下,切片操做【读、写】不会影响postion)
4096 1024 b'' 1024 0 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' postion_index:0 postion_index:0 b'1234567890' postion_index:0 1234567890 True
看下open打开的案例:
import os
import mmap
def main():
with open("temp", "wb") as f:
f.write("小明同窗最爱刷碗\n小潘同窗最爱打扫".encode())
# 打开磁盘二进制文件进行更新(读写)
with open("temp", "r+b") as f:
with mmap.mmap(f.fileno(), 0) as m:
print("postion_index:%d" % m.tell())
print(m.readline().decode().strip()) # 转成str并去除两端空格
print("postion_index:%d" % m.tell())
print(m[:].decode()) # 所有读出来
print("postion_index:%d" % m.tell())
m.seek(0)
print("postion_index:%d" % m.tell())
if __name__ == '__main__':
main()
输出:
postion_index:0 小明同窗最爱刷碗 postion_index:25 小明同窗最爱刷碗 小潘同窗最爱打扫 postion_index:25 postion_index:0
其余方法能够参考:这篇文章(Python3不少都和Python2不太相同,辩证去看吧)
注意一点:
经过MMap内存映射以后,进程间通讯并非对文件操做,而是在内存中。文件保持同步只是由于mmap的flags默认设置的是共享模式(MAP_SHARED)
PS:还记得以前讲类方法和实例方法的时候吗?Python中类方法能够直接被对象便捷调用,这边mmap实例对象中的方法,其实不少都是类方法 步入正轨:
来看一个有血缘关系的通讯案例:(通常用匿名)
import os
import time
import mmap
def create_file(file_name, size):
with open(file_name, "wb") as f:
f.seek(size - 1)
f.write(b"\0x00")
def main():
file_name = "temp.bin"
# mmap映射的时候不能映射空文件,因此咱们本身建立一个
create_file(file_name, 1024)
fd = os.open(file_name, os.O_RDWR)
with mmap.mmap(fd, 0) as m: # m.resize(1024) # 大小能够本身调整的
pid = os.fork()
if pid == 0:
print("[子进程]PID:%d,PPID:%d" % (os.getpid(), os.getppid()))
m.write("子进程说:老爸,我想出去玩了~\n".encode())
time.sleep(3)
print(m.readline().decode().strip())
exit(0)
elif pid > 0:
print("[父进程]PID:%d,PPID:%d" % (os.getpid(), os.getppid()))
time.sleep(1) # 和文件同样,非堵塞
print(m.readline().decode().strip())
m.write("父进程说:去吧去吧\n".encode())
wpid, status = os.wait()
print("[父进程]收尸:PID:%d,Status:%d" % (wpid, status))
exit(0)
if __name__ == '__main__':
main()
输出:
[父进程]PID:6843,PPID:3274 [子进程]PID:6844,PPID:6843 子进程说:老爸,我想出去玩了~ 父进程说:去吧去吧 [父进程]收尸:PID:6844,Status:0
父进程建立了一份mmap对象,fork产生子进程的时候至关于copy了一份指向,因此能够进行直接通讯(联想fd的copy)
import os
import time
import mmap
def main():
# 不记录文件中,直接内存中读写(这个地方len就不能为0了,本身指定一个大小eg:4k)
with mmap.mmap(-1, 4096) as m:
pid = os.fork()
if pid == 0:
print("[子进程]PID:%d,PPID:%d" % (os.getpid(), os.getppid()))
m.write("[子进程]老爸我出去嗨了~\n".encode())
time.sleep(2)
msg = m.readline().decode().strip()
print(msg)
exit(0)
elif pid > 0:
print("[父进程]PID:%d,PPID:%d" % (os.getpid(), os.getppid()))
time.sleep(1)
msg = m.readline().decode().strip()
print(msg)
m.write("[父进程]去吧,皮卡丘~".encode())
wpid, status = os.wait()
print("[父进程]收尸:PID:%d,Status:%d" % (wpid, status))
exit(0)
if __name__ == '__main__':
main()
输出:
[父进程]PID:8115,PPID:3274 [子进程]PID:8116,PPID:8115 [子进程]老爸我出去嗨了~ [父进程]去吧,皮卡丘~ [父进程]收尸:PID:8116,Status:0
由于不一样进程以前没有关联,必须以文件为媒介(文件描述符fd)
进程1:
import os
import time
import mmap
def create_file(file_name, size):
with open(file_name, "wb") as f:
f.seek(size - 1)
f.write(b"\0x00")
def main():
file_name = "temp.bin"
if not os.path.exists(file_name):
# mmap映射的时候不能映射空文件,因此咱们本身建立一个
create_file(file_name, 1024)
fd = os.open(file_name, os.O_RDWR)
with mmap.mmap(fd, 0) as m: # m.resize(1024) # 大小能够本身调整的
print("[进程1]PID:%d,PPID:%d" % (os.getpid(), os.getppid()))
m.write("进程1说:小明放学去撸串吗?\n".encode())
time.sleep(3)
print(m.readline().decode().strip())
exit(0)
if __name__ == '__main__':
main()
进程2:
import os
import time
import mmap
def create_file(file_name, size):
with open(file_name, "wb") as f:
f.seek(size - 1)
f.write(b"\0x00")
def main():
file_name = "temp.bin"
if not os.path.exists(file_name):
# mmap映射的时候不能映射空文件,因此咱们本身建立一个
create_file(file_name, 1024)
fd = os.open(file_name, os.O_RDWR)
with mmap.mmap(fd, 0) as m: # m.resize(1024) # 大小能够本身调整的
print("[进程2]PID:%d,PPID:%d" % (os.getpid(), os.getppid()))
time.sleep(1)
print(m.readline().decode().strip())
m.write("进程2说:为毛不去?\n".encode())
exit(0)
if __name__ == '__main__':
main()
输出图示:
代码实例:https://github.com/lotapp/BaseCode/tree/master/python/5.concurrent/Linux/进程通讯/6.signal
信号:它是一种异步的通知机制,用来提醒进程一个事件已经发生。当一个信号发送给一个进程,操做系统中断了进程正常的控制流程,此时,任何非原子操做都将被中断。若是进程定义了信号的处理函数,那么它将被执行,不然就执行默认的处理函数。
通常信号不太用于进程间通讯,经常使用就是发个信号把xxx进程干死。
先来个例子,等会讲理论:
Python里面通常用os.kill(pid,signalnum)
来发信号:eg:kill 9 pid
import os
import time
import signal
def main():
pid = os.fork()
if pid == 0:
print("[子进程]PID=%d,PPID=%d" % (os.getpid(), os.getppid()))
while True:
print("[子进程]孩子老卵,怎么滴吧~")
time.sleep(1)
elif pid > 0:
print("[父进程]PID=%d,PPID=%d" % (os.getpid(), os.getppid()))
time.sleep(3)
print("父进程耐心有限,准备杀了儿子")
# sigkill 至关于kill 9 pid
os.kill(pid, signal.SIGKILL) # 发信号
# 收尸
wpid, status = os.wait()
print("父进程收尸:子进程PID=%d,Status=%d" % (wpid, status))
if __name__ == '__main__':
main()
输出:
[父进程]PID=21841,PPID=5559 [子进程]PID=21842,PPID=21841 [子进程]孩子老卵,怎么滴吧~ [子进程]孩子老卵,怎么滴吧~ [子进程]孩子老卵,怎么滴吧~ 父进程耐心有限,准备杀了儿子 父进程收尸:子进程PID=21842,Status=9
扩展一下:
signal.pthread_kill(thread_id,signal.SIGKILL)) # 杀死线程
os.abort() # 给本身发异常终止信号
这边开始说说理论:
信号状态:
产生、传递等都是经过内核进行的,结合上面的例子画个图理解下:
未决信号集:没有被当前进程处理的信号集合(能够经过signal.sigpending()
获取set
集合)
阻塞信号集:要屏蔽的信号(不能被用户操做)
回顾一下上面说kill 9 pid
原理的知识:kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8 43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2 63) SIGRTMAX-1 64) SIGRTMAX
说下经常使用的几个信号:
sigkill
)是kill 9
sigint
)是Ctrl+C
终止进程sigquit
)是Ctrl+\
终止进程如今说说信号捕捉signal.signal(signalnum, handler)
handler处理函数
,除了自定义信号处理函数外也可使用系统提供的两种方式:
SIG_IGN
(忽略该信号)SIG_DFL
(系统默认操做)注意一点:SIGSTOP
和 SIGKILL
信号是不能被捕获、忽略和阻塞的(这个是系统预留的,若是连预留都没有能够想象确定木马横向)
PS:信号的优先级通常都是比较高的,每每进程收到信号后都会停下手上的事情先处理信号(死循环也同样歇菜)
来看一个例子:(处理singint,忽略sigquit)
import os
import time
import signal
def print_info(signalnum, frame):
print("信号:%d准备弄我,我是小强我怕谁?(%s)" % (signalnum, frame))
def main():
signal.signal(signal.SIGINT, print_info) # 处理Ctrl+C的终止命令(singint)
signal.signal(signal.SIGQUIT, signal.SIG_IGN) # 忽略Ctrl+\的终止命令(sigquit)
while True:
print("[PID:%d]我很坚强,不退出,等着信号来递达~" % os.getpid())
time.sleep(3) # 你要保证进程不会退出才能处理信号,不用担忧影响信号(优先级高)
if __name__ == '__main__':
main()
输出图示:(我休息3s,在3s内给程序发送了sigint
信号(Ctrl+C)就立马处理了)
扩展:
signal.pause()
,没必要使用死循环来轮询了os.killpg(pgid, sid)
进程组结束signal.siginterrupt(signal.SIGALRM, False)
防止系统调用被信号打断所设立(其实通常也不太用,出问题才用)通俗的讲就是,要是系统和你发同样的信号可能也就被处理了,加上这句就ok了,eg:
举个例子,有时候有些恶意程序蓄意破坏或者被所谓的安全软件误杀好比系统函数kill(-1)
【有权限的都杀了】
import signal
def print_info(signalnum, frame):
print("死前留言:我被信号%d弄死了,记得替我报仇啊!" % signalnum)
def main():
signal.signal(signal.SIGINT, print_info) # 处理Ctrl+C的终止命令(singint)
signal.signal(signal.SIGQUIT, print_info) # 处理Ctrl+\的终止命令(singquit)
signal.siginterrupt(signal.SIGINT, False)
signal.siginterrupt(signal.SIGQUIT, False)
signal.pause() # 设置一个进程到休眠状态直到接收一个信号
if __name__ == '__main__':
main()
输出:
dnt@MZY-PC:~/桌面/work/BaseCode/python/5.concurrent/Linux/进程通讯/6.signal python3 1.os_kill2.py ^C死前留言:我被信号2弄死了,记得替我报仇啊! dnt@MZY-PC:~/桌面/work/BaseCode/python/5.concurrent/Linux/进程通讯/6.signal python3 1.os_kill2.py ^\死前留言:我被信号3弄死了,记得替我报仇啊! dnt@MZY-PC:~/桌面/work/BaseCode/python/5.concurrent/Linux/进程通讯/6.signal
再说两个定时器就进下一个话题把,这个主要就是信号捕捉用得比较多,而后就是通常都是守护进程发信号
先验证一个概念:alarm闹钟不能被fork后的子进程继承
import os
import time
import signal
def main():
# 不受进程影响,每一个进程只能有一个定时器,再设置只是重置
signal.alarm(3) # 设置终止时间(3s),而后终止进程(sigaltirm)
pid = os.fork()
if pid == 0:
print("[子进程]PID=%d,PPID=%d" % (os.getpid(), os.getppid()))
for i in range(5):
print("[子进程]孩子老卵,怎么滴吧~")
time.sleep(1)
elif pid > 0:
print("[父进程]PID=%d,PPID=%d" % (os.getpid(), os.getppid()))
print("[遗言]PID=%d,PPID=%d" % (os.getpid(), os.getppid()))
if __name__ == '__main__':
main()
输出
[父进程]PID=9687,PPID=9063 [遗言]PID=9687,PPID=9063 [子进程]PID=9688,PPID=9687 [子进程]孩子老卵,怎么滴吧~ [子进程]孩子老卵,怎么滴吧~ [子进程]孩子老卵,怎么滴吧~ [子进程]孩子老卵,怎么滴吧~ [子进程]孩子老卵,怎么滴吧~ [遗言]PID=9688,PPID=1060
这个你能够本身验证:不受进程影响,每一个进程只能有一个定时器,再设置只是重置
其实好好看逆天的问题都会发现各类小技巧的,全部小技巧自我总结一下就会产生质变了
import signal
def main():
signal.alarm(1) # 设置终止时间(3s),而后终止进程(sigaltirm)
i = 0
while True:
print(i)
i += 1 # 别忘记,Python里面没有++哦~
if __name__ == '__main__':
main()
运行一下:time python3 xxx.py
运行一下:
time python3 xxx.py > temp
简单说下三个参数:
real
总共运行时间(real=user+sys+损耗时间)user
(用户代码真正运行时间)sys
(内核运行时间)【内核不运行,你系统也不正常了】其实就是减小了IO操做,性能方面就相差几倍!我这边只是一台老电脑,要是真在服务器下性能相差可能让你吓一跳
如今知道为何要realase发布而不用debug直接部署了吧(线上项目非必要状况,通常都会删除全部日记输出的)
signal.setitimer(which, seconds, interval=0.0)
which参数说明:
这个通常在守护进程中常常用,看个简单案例:
import time
import signal
def say_hai(signalnum, frame):
print("我会周期性执行哦~")
def main():
# 捕捉信号(在前面最好,否则容易漏捕获)
signal.signal(signal.SIGALRM, say_hai)
# 设置定时器,第一次1s后执行,之后都3s执行一次
signal.setitimer(signal.ITIMER_REAL, 1, 3)
# print(signal.getitimer(signal.ITIMER_REAL))
while True:
print("我在作其余事情")
time.sleep(1)
if __name__ == '__main__':
main()
输出:
我在作其余事情 我会周期性执行哦~ 我在作其余事情 我在作其余事情 我在作其余事情 我会周期性执行哦~ 我在作其余事情 我在作其余事情 我在作其余事情 我会周期性执行哦~ 我在作其余事情 我在作其余事情 我在作其余事情 ...
实例代码:"https://github.com/lotapp/BaseCode/tree/master/python/5.concurrent/Linux/进程守护
守护进程应用场景不少,好比程序上线后有个bug被不定时的触发,每次都致使系统爆卡或者退出,而程序员修复bug须要时间,可是线上项目又不能挂,这时候就可使用一个心跳检测的守护进程(查错也可使用守护进程)【为恶就不说了】
正式开始前,先来个伪案例:
模拟一个漏洞百出的程序
import os
import time
def main():
print("[PID:%d]进程运行中..." % os.getpid())
time.sleep(5)
os.abort() # 给本身发异常终止信号
if __name__ == '__main__':
main()
写个简单版本的守护进程:
import os
import time
import signal
def write_log(msg):
pass
def is_running(p_name):
"""是否在运行"""
try:
# grep -v grep 不显示grep自己,wc -l是计数用的
result = os.popen("ps ax | grep %s | grep -v grep" % p_name).readlines()
if len(result) > 0:
return True
else:
return False
except Exception as ex:
write_log(ex)
return False
def is_restart(p_script):
"""重启程序"""
try:
if os.system(p_script) == 0:
return True
else:
return False
except Exception as ex:
write_log(ex)
return False
def heartbeat(signalnum, frame):
"""心跳检查"""
p_name = "test.py"
p_script = "python3 ./test.py"
if not is_running(p_name):
print("程序(%s)已挂,准备重启" % p_name)
if not is_restart(p_script):
is_restart(p_script) # 再给一次机会
def main():
# 信号处理
signal.signal(signal.SIGALRM, heartbeat)
# 第一次1s后检查,之后每5s检查一次
signal.setitimer(signal.ITIMER_REAL, 1, 5)
while True:
time.sleep(5) # 不用担忧影响signal(优先级别高)
if __name__ == '__main__':
main()
输出:
程序(test.py)已挂,准备重启 [PID:7270]进程运行中... Aborted (core dumped) 程序(test.py)已挂,准备重启 [PID:7278]进程运行中... Aborted (core dumped) [PID:7284]进程运行中... .....
写了个伪牌子的,如今说说正规的,看看概念的东西:
特色:
讲正式流程前先复习一下上面说的进程组
和会话
须要扩充几点:
进程组:
会话:
稍微验证一下,而后步入正题:
import os
import time
def main():
pid = os.fork()
if pid == 0:
for i in range(7):
print("子进程:PID=%d,PPID=%d,PGrpID=%d" % (os.getpid(), os.getppid(), os.getpgrp()))
time.sleep(i)
elif pid > 0:
print("父进程:PID=%d,PPID=%d,PGrpID=%d" % (os.getpid(), os.getppid(), os.getpgrp()))
time.sleep(4)
print("遗言:PID=%d,PPID=%d,PGrpID=%d" % (os.getpid(), os.getppid(), os.getpgrp()))
if __name__ == '__main__':
main()
验证结果:父进程ID==进程组ID
,父进程挂了进程组依旧在
,顺便验证了下ps -ajx
的参数
先看看这个SessionID是啥:
import os
import time
def main():
print("进程:PID=%d,PPID=%d,PGrpID=%d" % (os.getpid(), os.getppid(), os.getpgrp()))
print(os.getsid(os.getpid()))
for i in range(1, 5):
time.sleep(i)
print("over")
if __name__ == '__main__':
main()
ps ajx
的参数如今全知道了:PPID PID PGID SID (你不加grep就能看到的)
验证一下SessionID的事情:
# 验证一下父进程不能建立会话ID
import os
def main():
pid = os.getpid()
print("进程:PPID=%d,PID=%d,GID=%d,SID=%d" % (pid, os.getppid(), os.getpgrp(),os.getsid(pid)))
os.setsid() # 父进程无法设置为会话ID的验证
if __name__ == '__main__':
main()
步入正轨:
建立守护进程的步骤:
umask(0)
重置一下,这样能够获取777权限)先简单弄个例子实现上面步骤:
import os
import time
from sys import stdin, stdout, stderr
def main():
# 【必须】1. fork子进程,父进程退出(子进程变成了孤儿)
pid = os.fork()
if pid > 0:
exit(0)
# 【必须】2. 子进程建立新会话(建立出新会话会丢弃原有的控制终端)
os.setsid()
# 3. 改变当前工做目录【为了减小bug】# 改为不会被删掉的目录,好比/
os.chdir("/home/dnt") # 我这边由于是用户建立的守护进程,就放它下面,用户删了,它也不必存在了
# 4. 重置文件掩码(获取777权限)
os.umask(0)
# 5. 关闭文件描述符(若是写日志也能够重定向一下)
os.close(stdin.fileno())
os.close(stdout.fileno())
os.close(stderr.fileno())
# 【必须】6. 本身的逻辑代码
while True:
time.sleep(1)
if __name__ == '__main__':
main()
运行效果:(直接后台走起了)
若是对Linux基础不熟,能够看看几年前说的LinuxBase:
Linux基础命令:http://www.cnblogs.com/dunitian/p/4822807.html
Linux系列其余文章:http://www.javashuo.com/article/p-dojeygnv-gw.html
若是对部署运行系列不是很熟,能够看以前写的小demo:
用Python三、NetCore、Shell分别开发一个Ubuntu版的定时提醒(附NetCore跨平台两种发布方式):http://www.javashuo.com/article/p-wsvejssb-q.html
若是对OOP不是很熟悉能够查看以前写的OOP文章:
Python3 与 C# 面向对象之~封装http://www.javashuo.com/article/p-purcxavn-d.html
Python3 与 C# 面向对象之~继承与多态http://www.javashuo.com/article/p-ticxscaz-cv.html
Python3 与 C# 面向对象之~异常相关http://www.javashuo.com/article/p-ronmnwhu-q.html
若是基础不牢固,能够看以前写的PythonBase:
Python3 与 C# 基础语法对比(Function专栏)http://www.javashuo.com/article/p-obgfrmjz-g.html
Python3 与 C# 扩展之~模块专栏http://www.javashuo.com/article/p-wtdazkqm-db.html
Python3 与 C# 扩展之~基础衍生http://www.javashuo.com/article/p-tmcacaan-d.html
Python3 与 C# 扩展之~基础拓展http://www.javashuo.com/article/p-wtppcuyx-g.html
如今正儿八经的来个简化版的守护进程:(你能够根据需求多加点信号处理)
import os
import time
import signal
from sys import stdin, stdout, stderr
class Daemon(object):
def __init__(self, p_name, p_script):
self.p_name = p_name
self.p_script = p_script
@staticmethod
def write_log(msg):
# 追加方式写
with open("info.log", "a+") as f:
f.write(msg)
f.write("\n")
def is_running(self, p_name):
"""是否在运行"""
try:
# grep -v grep 不显示grep自己,wc -l是计数用的
result = os.popen(
"ps ax | grep %s | grep -v grep" % p_name).readlines()
if len(result) > 0:
return True
else:
return False
except Exception as ex:
self.write_log(ex)
return False
def is_restart(self, p_script):
"""重启程序"""
try:
if os.system(p_script) == 0:
return True
else:
return False
except Exception as ex:
self.write_log(ex)
return False
def heartbeat(self, signalnum, frame):
"""心跳检查"""
if not self.is_running(self.p_name):
self.write_log("[%s]程序(%s)已挂,准备重启" % (time.strftime("%Y-%m-%d%X"),
self.p_name))
if not self.is_restart(self.p_script):
self.is_restart(self.p_script) # 再给一次机会
def run(self):
"""运行守护进程"""
pid = os.fork()
if pid > 0:
exit(0)
os.setsid() # 子进程建立新会话
os.chdir("/home/dnt") # 改变当前工做目录
os.umask(0) # 获取777权限
# 5. 关闭文件描述符
os.close(stdin.fileno())
os.close(stdout.fileno())
os.close(stderr.fileno())
# 【必须】6. 本身的逻辑代码
# 捕捉设置的定时器
signal.signal(signal.SIGALRM, self.heartbeat)
# 第一次2s后执行,之后5s执行一次
signal.setitimer(signal.ITIMER_REAL, 2, 5)
self.write_log("[%s]daeman running" % time.strftime("%Y-%m-%d%X"))
self.write_log("p_name:%s,p_script:%s" % (self.p_name, self.p_script))
while True:
time.sleep(5) # 不用担忧影响signal(优先级别高)
def main():
try:
pro = Daemon("test.py", "python3 ~/demo/test.py")
pro.run()
except Exception as ex:
Daemon.write_log(ex)
if __name__ == '__main__':
main()
运行效果:(关闭文件描述符后就不要printf了)
扩展说明,若是你要文件描述符重定向的话能够这么写:
with open("in.log", "a+") as f:
os.dup2(f.fileno(), sys.stdin.fileno())
with open("out.log", "a+") as f:
os.dup2(f.fileno(), sys.stdout.fileno())
with open("err.log", "a+") as f:
os.dup2(f.fileno(), sys.stderr.fileno())
以后你printf就自动到指定的文件了
Socket,在讲基础最后一个系列~网络编程的时候会讲,不急,并且进程间通讯不须要这么‘重量级’
的
线程相关打算和代码一块儿讲,有机会也能够单独拉出来讲一个结尾篇
业余拓展:
Other:
Linux下0、一、2号进程 https://blog.csdn.net/gatieme/article/details/51484562 https://blog.csdn.net/gatieme/article/details/51532804 https://blog.csdn.net/gatieme/article/details/51566690 Linux 的启动流程 http://www.ruanyifeng.com/blog/2013/08/linux_boot_process.html http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-commands.html http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-part-two.html 孤儿进程与僵尸进程 https://www.cnblogs.com/Anker/p/3271773.html https://blog.csdn.net/believe_s/article/details/77040494 Python2 OS模块之进程管理 https://www.cnblogs.com/now-fighting/p/3534185.html 缓冲区的我的理解 https://blog.csdn.net/lina_acm/article/details/51865543 深刻Python多进程编程基础 https://zhuanlan.zhihu.com/p/37370577 https://zhuanlan.zhihu.com/p/37370601 python多进程实现进程间通讯实例 https://www.jb51.net/article/129016.htm PIPE2参考: https://bugs.python.org/file22147/posix_pipe2.diff https://stackoverflow.com/questions/30087506/event-driven-system-call-in-python https://stackoverflow.com/questions/5308080/python-socket-accept-nonblocking/5308168 FIFO参考: https://blog.csdn.net/an_tang/article/details/68951819 https://blog.csdn.net/firefoxbug/article/details/8137762 Python之mmap内存映射模块(大文本处理)说明 https://www.cnblogs.com/zhoujinyi/p/6062907.html python 基于mmap模块的jsonmmap实现本地多进程内存共享 https://www.cnblogs.com/dacainiao/p/5914114.html 若是把一个事务可看做是一个程序,它要么完整的被执行,要么彻底不执行。这种特性就叫原子性。 https://blog.csdn.net/Android_Mrchen/article/details/77866490 事务四大特征:原子性,一致性,隔离性和持久性 https://blog.csdn.net/u014079773/article/details/52808193 python 、mmap 实现内存数据共享 https://www.jianshu.com/p/c3afc0f02560 http://www.cnblogs.com/zhoujinyi/p/6062907.html https://blog.csdn.net/zhaohongyan6/article/details/71158522 Python信号相关: https://my.oschina.net/guol/blog/136036 Linux--进程组、会话、守护进程 https://www.cnblogs.com/forstudy/archive/2012/04/03/2427683.html