先写几个问号来概况下今天准备说的内容:(谜底自己解开,文中都有)
Ctrl+C
终止进程的本质吗?你知道 Kill-9pid
的真正含义吗?僵尸进程
和 孤儿进程
的悲催生产史吗? 孤儿找干爹
, 僵尸送往生
想知道不?李代桃僵
吗? ps aux|grep xxx
的背后到底隐藏了什么?密密私语
等着你来查看哦~上回说道:1.并发编程~先导篇(上)
代码实例: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()
b'\x00'
# 老规矩,开始之前,扩充一个小知识点:(空字符串和'\0'区别)
a = "" # 空字符串 (Python里面没有char类型)
b = "\x00" # `\0` 的二进制写法
c = "\0"
print(a)
print(b)
print(c)
print(len(a))
print(len(b))
print(len(c))
0
1
1
看个简单的案例快速熟悉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: kill9pid
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
集合)
阻塞信号集:要屏蔽的信号(不能被用户操作)
回顾一下上面说 kill9pid
原理的知识: 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
)是 kill9
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()
进程:PPID=3301,PID=2588,GID=3301,SID=3301
---------------------------------------------------------------------------
PermissionError Traceback (most recent call last)
<ipython-input-1-375f70009fcf> in <module>()
8
9 if __name__ == '__main__':
---> 10 main()
<ipython-input-1-375f70009fcf> in main()
4 pid = os.getpid()
5 print("进程:PPID=%d,PID=%d,GID=%d,SID=%d" % (pid, os.getppid(), os.getpgrp(),os.getsid(pid)))
----> 6 os.setsid() # 父进程没法设置为会话ID的验证
7
8
PermissionError: [Errno 1] Operation not permitted
步入正轨:
创建守护进程的步骤:
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系列其他文章:https://www.cnblogs.com/dunitian/p/4822808.html#linux
如果对部署运行系列不是很熟,可以看之前写的小demo:
用Python3、NetCore、Shell分别开发一个Ubuntu版的定时提醒(附NetCore跨平台两种发布方式):https://www.cnblogs.com/dotnetcrazy/p/9111200.html
如果对OOP不是很熟悉可以查看之前写的OOP文章:
Python3 与 C# 面向对象之~封装https://www.cnblogs.com/dotnetcrazy/p/9202988.html
Python3 与 C# 面向对象之~继承与多态https://www.cnblogs.com/dotnetcrazy/p/9219226.html
Python3 与 C# 面向对象之~异常相关https://www.cnblogs.com/dotnetcrazy/p/9219751.html
如果基础不牢固,可以看之前写的PythonBase:
Python3 与 C# 基础语法对比(Function专栏)https://www.cnblogs.com/dotnetcrazy/p/9175950.html
Python3 与 C# 扩展之~模块专栏https://www.cnblogs.com/dotnetcrazy/p/9253087.html
Python3 与 C# 扩展之~基础衍生https://www.cnblogs.com/dotnetcrazy/p/9278573.html
Python3 与 C# 扩展之~基础拓展https://www.cnblogs.com/dotnetcrazy/p/9333792.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,在讲基础最后一个系列~网络编程的时候会讲,不急,而且进程间通信不需要这么 ‘重量级’
的
线程相关打算和代码一起讲,有机会也可以单独拉出来说一个结尾篇
业余拓展:
官方文档大全
进程间通信和网络
os - 其他操作系统接口
mmap - 内存映射文件支持
signal - 设置异步事件的处理程序
Other:
Linux下0、1、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