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`````
    """
    
  • 相关阅读:
    LeetCode(111) Minimum Depth of Binary Tree
    LeetCode(108) Convert Sorted Array to Binary Search Tree
    LeetCode(106) Construct Binary Tree from Inorder and Postorder Traversal
    LeetCode(105) Construct Binary Tree from Preorder and Inorder Traversal
    LeetCode(99) Recover Binary Search Tree
    【Android】通过经纬度查询城市信息
    【Android】自定义View
    【OpenStack Cinder】Cinder安装时遇到的一些坑
    【积淀】半夜突然有点想法
    【Android】 HttpClient 发送REST请求
  • 原文地址:https://www.cnblogs.com/traditional/p/11446232.html
Copyright © 2011-2022 走看看