zoukankan      html  css  js  c++  java
  • signal,blinker:信号(看我脸色行事)

    signal

    什么是信号(signal)?

    信号在linux中被用来进行进程间的通信和异步处理,简单地可以理解会为回调函数,当发送一个信号时,会触发相应的操作。python中的signal模块便是用来处理信号的,需要注意的是,这个模块不能在Windows上使用,因为Windows内核对信号的支持不是很好,这里我们使用Linux进行操作。

    python的signal模块所支持的信号

    python中支持的信号很多,基本和linux内置的信号是一致的,但是常用的如下

    • SIGHUP:连接挂断

      本信号在用户终端连接结束时发出。我们通常连接linux的时候,系统会分配给用户一个终端(session)。在这个终端运行的所有程序,包括前台进程组和后台进程组,都属于这个session。当用户退出linux时,无论前台还是后台,凡是有对终端输出的进程都会收到SIGHUP信号。这个信号的默认操作为终止进程,因此会向终端输出内容的那些进程就会终止。不过有的进程可以捕捉这个信号并忽略它。比如wget,这样就算退出了linux登录,wget依旧能继续下载

    • SIGILL:非法指令

      执行了非法指令,通常是因为可执行文件本身出现错误,或者执行数据段,堆栈溢出时也可能产生这个信号

    • SIGINT:终止进程(ctrl c)

      程序终止(interrupt)信号,通常在用户按下ctrl c的时候发出,用于通知前台进程组终止进程

    • SIGTSTP:暂停进程(ctrl z)

      停止进程的运行, 但该信号可以被处理和忽略。通常在用户按下ctrl z的时候触发

    • SIGKILL:杀死进程(此信号不能被捕获或忽略)

      用来立即结束程序的运行,本信号无法被阻塞、处理和忽略。如果管理员发现某个进程终止不了,可尝试发送这个信号。

    • SIGQUIT:终端退出

      和SIGINT类似,但通常是由ctrl /来通知,进程在因收到SIGQUIT退出时会产生core文件,在这个意义上类似于一个程序错误信号

    • SIGTERM:杀死进程(可以被捕获或忽略)

      程序结束(terminate)信号,与SIGKILL不同的是,该信号可以被阻塞、处理、忽略。通常用来要求程序自己正常退出。比如shell中,kill 进程id会产生这个信号。如果这个无法终止,我们才会尝试SIGKILL,相当于kill -9 进程id

    • SIGALRM:闹钟信号,由signal.alarm()发起

      时钟定时信号, 计算的是实际的时间或时钟时间. alarm函数使用该信号.

    • SIGCONT:继续执行暂停的进程

      让一个停止(stopped)的进程继续执行. 本信号不能被阻塞. 可以用一个handler来让程序在由stopped状态变为继续执行时完成特定的工作

    除此之外还有很多其他信号,这里不一一列举了,有兴趣的话可以在网上搜索,linux 信号

    signal模块所支持的函数

    alarm(time),time为int,表示多少秒,在时间过后,向进程自身发送SIGALRM信号

    import signal
    import time
    
    signal.alarm(3)  # 3s后会向进程自身发出一个SIGALRM信号,同时终止程序
    while 1:
        time.sleep(1)
        print(".......")
    """
    .......
    .......
    Alarm clock
    """
    

    pause(),让进程暂停,以等待信号(什么信号都行)。因此该函数相当于阻塞进程进行,接收到信号后继续前进。

    import signal
    
    signal.pause()  # 程序会卡在这里
    print(123)
    

    signal(signalnum, handler)

    这是一个非常重要的函数,用来注册信号的。signalnum是要注册的信号量,handler是该信号量所对应的处理器,一般是一个可以调用的函数,必须要接收两个参数,分别是信号量(signalnum)、当前程序运行的堆栈(frame),这两个参数会在触发信号时自动传入。
    那如果我们想忽略信号的话,就是直接在handler的函数体中写一个pass吗?这样是可以的,但是signal为我们提供了两个常量,之前没有介绍。
    1.signal.SIG_DFL:表示接收到信号后,程序按照默认行为执行;
    2.signal.SIG_IGN:表示接收到信号后,程序忽略该信号,继续自身运行。
    
    但是需要注意的是:
    1.该方法是有返回值的,返回之前原有的信号处理函数
    2.该方法只能在主线程中注册,如果在子线程中注册, 会引发一个ValueError
    
    # -*- coding:utf-8 -*-
    # @Author: WanMingZhu
    # @Date: 2019/8/30 17:12
    import signal
    import traceback
    
    
    def handler(signalnum, frame):
        print(signalnum)
        print(frame)
        traceback.print_stack(frame)  # 打印栈帧的方法
    
    
    # 注册一个signal.SIGALRM信号
    signal.signal(signal.SIGALRM, handler)
    
    # 如何发送信号
    # 可以使用os.kill
    import os
    # 向当前进程发送信号
    os.kill(os.getpid(), signal.SIGALRM)
    
    """
    输出结果:
    14
    <frame at 0x7fda528cb9f8, file '1.py', line 19, code <module>>
      File "1.py", line 19, in <module>
        os.kill(os.getpid(), signal.SIGALRM)
    """
    

    blinker

    不同于signal模块,blink是第三方模块。个人觉得blink使用起来要比signal简单许多,而且这个模块可以在Windows下使用

    # -*- coding:utf-8 -*-
    # @Author: WanMingZhu
    # @Date: 2019/8/30 17:12
    from blinker import Namespace
    
    # 1.定义信号
    namespace = Namespace()
    fire = namespace.signal("fire")
    
    # 2.监听信号
    # 首先定义一个回调函数,里面至少要接收一个参数
    def open_fire(sender):
        print(sender)
        print("open_fire")
    # 监听
    fire.connect(open_fire)
    
    
    # 3.发送信号
    # send里面的参数则会传给open_fire
    fire.send("xxx")
    
    """
    输出结果:
    xxx
    open_fire
    """
    

    总结一下:步骤就三步

    • 1.定义信号

      namespace = NameSpace()
      signal = namespace.signal("signal")
      
    • 2.监听信号

      创建需要至少要接收一个参数的函数,然后通过signal.connect进行注册

    • 3.发送信号

      调用signal.send("message")方法,然后会触发绑定的函数,同时message也会传进去,如果不传则为None
      如果回调函数里面只有一个参数,又不想传的话,直接send即可,会自动将None传进去
      但是如果回调函数要接收多个参数,那么第一个sender必须通过位置参数指定,其余要通过关键字参数指定
      
    from blinker import Namespace
    
    # 1.定义信号
    namespace = Namespace()
    fire = namespace.signal("fire")
    
    
    def open_fire(sender, a, b, c):
        print(sender, a, b, c)
        print("open fire`````")
    
    
    fire.connect(open_fire)
    
    # 回调至少接受一个参数,且必须通过位置参数指定
    # 如果不传则相当于传了一个None
    # 如果有多个参数,那么剩余的参数要通过关键字参数的方式来传递
    try:
        fire.send("xxx", 1, 2, 3)
    except TypeError as e:
        print(e)  # send() accepts only one positional argument, 4 given
    
    fire.send("xxx", a=1, b=2, c=3)
    """
    xxx 1 2 3
    open fire`````
    """
    
    # 第一个参数不指定,则默认传了一个None进去
    fire.send(a=1, b=2, c=3)
    """
    None 1 2 3
    open fire`````
    """
    
  • 相关阅读:
    nginx 配置文件详解
    nginx的location匹配规则
    mysql常用函数
    jquery封装的ajax请求
    docker
    in与exists和not in 与 not exists的区别
    mysql授权
    线程池
    springboot+rediscluster
    常用网址
  • 原文地址:https://www.cnblogs.com/traditional/p/11446232.html
Copyright © 2011-2022 走看看