Yifei Kong

Nov 13, 2018

Nov 12, 2018

Oct 15, 2018

使用 prctl 在父进程退出的时候安全退出子进程

在 Linux 中, 当子进程退出的时候, 父进程可以收到信号, 但是当父进程退出的时候, 子进程并不会受到信号. 这样就造成了在父进程崩溃的时候, 子进程并不能同时退出, 而是一直会在后台运行, 比如下面的例子:

import os
import time

def loop_print():
    import time
    while True:
        print('child alive, %s' % time.time())
        time.sleep(1)

try:
    pid = os.fork()
except OSError:
    pass

if pid != 0:  # parent
    print('parent sleep for 2')
    time.sleep(2)
    print …

Oct 15, 2018

吐槽一下 Python 混乱的 threading 和 multiprocessing

最近要写一个库往 influxdb 中打点, 因为要被很多程序使用, 而又要创建新的进程, 为了避免引起使用方的异常, 简单深入了解了下 Python 的并发控制, 这才发现标准库真是坑. 之前没过多考虑过, 只是凭感觉在 CPU 密集的时候使用 multiprocessing, 而默认使用 threading, 其实两个还是有很多不一样的, 除了都是并发执行以外还有很大的不同. Python 中试图用 threading 和 multiprocessing 实现类似的接口来统一两方面, 结果导致更混乱了. 本文探讨几个坑.

在多线程环境中 fork

首先不谈 Python, 我们思考一下, 在多线程环境下如果执行 fork 会怎样? 在新的进程中, 会不会所有线程都在运行? 答案是否定的, 在 fork 之后, 只有执行 fork 的线程在运行, 而其他线程都不会运行. 这是 POSIX 标准规定的:

A process …

Oct 13, 2018

Python 高性能请求库 aiohttp 的基本用法

aiohttp 是 Python 异步编程最常用的一个 web 请求库了, 依托于 asyncio, 性能非常吓人. 下面列举几个常见的用法:

最基础: 并发下载网页

import aiohttp
import asyncio

async def fetch(session, url):
    async with session.get(url) as response:
        return await response.text()

async def main():
    urls = [
            'http://python.org',
            'https://google.com',
            'http://yifei.me'
        ]
    tasks = []
    async with aiohttp.ClientSession …

Sep 27, 2018

requests cookies 为空的一个坑

有时候,requests 返回的 cookies 会为空,原因是链接发生了 301/302 跳转,而 cookies 是跟着第一个响应返回的,第二个响应没有返回 Set-Cookie header。所以直接读取 r.cookies 是空的,而在 session.cookies 中是有数据的。

解决方法是直接读 s.cookies。

s = requests.Session()
r = s.get('http://httpbin.org/cookies/set?foo=bar')
cookies = requests.utils.dict_from_cookiejar(s.cookies)
s.cookies.clear()

不过需要注意的是如果在多线程环境中使用 session …

Jul 19, 2018

Python 中的 GC(垃圾回收)

引用计数(reference counting)

CPython 中默认使用的垃圾回收算法是 Reference Counting。也就是对每个元素标记有多少个其他元素引用了它,当引用数降到零的时候就删除。

  1. 当对象增加一个引用,比如赋值给变量,属性或者传入一个方法,引用计数执行加1运算。
  2. 当对象减少一个引用,比如变量离开作用域,属性被赋值为另一个对象引用,属性所在的对象被回收或者之前传入参数的方法返回,引用计数执行减1操作。
  3. 当引用计数变为0,代表该对象不被引用,可以标记成垃圾进行回收。

为了解决循环引用的问题,CPython 使用了 Cyclic GC,遍历所有的环,并且把每一个元素的引用减一,来检测每一个引用环是不是循环应用。

标记删除(Mark and Sweep)

  1. 从某一个已知的还活着的对象开始,便利对象,如果经过了某个对象就认为是活着的
  2. 如果没有被标记的就删除

避免了循环引用的问题

实际的处理过程

Pluggable Generational Incremental

参考资料:

  1. https://www.youtube.com/watch?v=iHVs_HkjdmI …

Jul 18, 2018

在 Python 中优雅地处理 SIGTERM 信号

昨天写了一个服务,在本地运行很好,使用 Ctrl-C 结束运行之后会清理资源,然后取消注册,然而放到 Docker 中跑之后发现结束之后资源没有释放。查了查发现原来是下面是几个因素造成的:

  1. Ctrl-C 发送的是 SIGINT 信号,Python 会转化成 KeyboardInterrupt 异常,而我的资源是在 finally 释放资源,所以使用 Ctrl-C 可以优雅地退出
  2. Python 中对其他的信号(比如 SIGTERM、SIGHUP)都不会处理,而是直接退出
  3. Docker 在推出的时候默认发送的是 SIGTERM 信号

所以在 docker stop 的时候服务并不能优雅的推出。

解决方法

使用 atexit 模块是不可以的,atexit 不会处理 SIGTERM。需要使用 signal 模块来,在网上找到了一份源码 …

Jul 09, 2018

父进程退出后如何退出子进程

我们知道当子进程推出的时候,父进程会收到 SIGCHLD 信号,从而可以采取相应的操作。但是当父进程退出的时候,系统会把子进程的父进程更改为pid=0的 init 进程,而且子进程不会收到任何信号。而我们经常想在父进程退出的时候,让子进程也推出。在 Python 中可以有如下几种做法。

设置子进程为 daemon

这里的 daemon 和系统的守护进程没有任何关系,是 quit_when_parent_dies 的意思。也就是当父进程退出的时候,会自动尝试关闭 daemon=True 的子进程。

p = multiprocessing.Process(target=foo)
p.daemon = True
p.start()

官方文档

在子进程中设置 PDEATHSIG

在 Linux 中,进程可以要求内核在父进程退出的时候给自己发信号。使用系统调用 prctl。

prctl(PR_SET_PDEATHSIG …

Jun 22, 2018

Next → Page 1 of 6