浏览量 1 Q:以下代码中的atexit()方法并没有被调用,知道为什么吗? stdlib.h> void func(void) { printf("\n Clean up function called \n"); } int main(void) { //int atexit atexit()注册的函数类型应为不接受任何参数的void函数,exit调用这些注册函数的顺序与它们 登记时候的顺序相反。同一个函数如若登记多次,则也会被调用多次。 */ atexit(func); _exit(0); } A:这是因为使用了 _exit() 方法。此方法并没有调用清除数据相关的方法,比如 atexit()等。
方法就是用atexit()函数来注册程序正常终止时要被调用的函数。 atexit()函数的参数是一个函数指针,函数指针指向一个没有参数也没有返回值的函数。 atexit()的函数原型是:#include <cstdlib>intatexit(void(*func)(void));atexit()成功时返回零,失败时返回非零。 在一个程序中至少可以用atexit()注册32个处理函数(你至少可以32次,这依赖于你的编译器),这些处理函数的调用顺序与其注册的顺序相反,也即最先注册的最后调用,最后注册的最先调用。 这里需要纠正一下网上很多人的错误说法,他们说atexit()最多可以被调用32次,而实际上是atexit最少可以被调用32次。 函数说明:atexit()用来设置一个程序正常结束前调用的函数. \n");}main(){ atexit (my_exit); exit(0);} 执行:before exit()!
glibc exit 之前写过一篇介绍 linux 进程环境的文章(《 [apue] 进程环境那些事儿》),其中提到了 glibc exit 会主动调用 atexit 注册的处理器,且有以下特性: LIFO ,先进后出的顺序 注册几次调用几次 atexit 处理器中再次调用 exit 能完成剩余处理器的调用 atexit 处理器中再次注册的 atexit 处理器能被调用 下面带着这些问题,来看 glibc * arg) :__on_exit 注册的函数,与 atexit 的不同之处仅在于回调时多了一个 status 参数 void (*cxa) (void *arg, int status) :__internal_atexit 也是通过调用 __cxa_atexit 实现的: int atexit (void (*func) (void)) { return __cxa_atexit ((void (*) (void ); ret = atexit (bye); if (ret !
这个时候,我们就可以使用Python自带的atexit这个模块了。 它的使用方法非常简单: import atexit @atexit.register def clean(): print('清理环境相关的代码') setup() test() 这样一来, 如下图所示: atexit使用中有下面几个注意事项: 你可以注册多个退出函数,他们会按照注册时间从晚到早以此执行。 例如: import atexit @atexit.register def clean_1(): ... @atexit.register def clean_2(): ... 会先运行clean_2()后运行clean_1() 如果clean()函数有参数,那么你可以不用装饰器,而是直接调用atexit.register(clean_1, 参数1, 参数2, 参数3='xxx
纯文本帮助 windows环境下,运行: python -m pydoc atexit Linux环境下直接运行: pydoc atexit 上面的命令会在控制台生成atexit的纯文本帮助信息。 Windows环境下: python -m pydoc -w atexit //在当前目录创建atexit.html python -m pydoc -p 5000 //启动一个Web服务器监听 http://localhost:5000/ Linux环境下: pydoc -w atexit pydoc -p 5000 交互式帮助 pydoc还未__builtins__添加了一个函数help() >>>help(atexit) Help on module atexit: NAME atexit
: 一个进程可以登记多达32个函数,这些函数将由exit自动调用,通常这32个函数被称为终止处理程序,并调用atexit函数来登记这些函数,atexit的参数是一个函数地址,当调用此函数时无须传递任何参数 ,该函数也不能返回值,atexit函数称为终止处理程序注册程序,注册完成以后,当函数终止是exit()函数会主动的调用前面注册的各个函数,但是exit函数调用这些函数的顺序于这些函数登记的顺序是相反的, atexit() 用于注册终止函数(即main执行结束后调用的函数),其原型为: int atexit(void (*function)(void)); 很多时候我们需要在程序退出的时候做一些诸如释放资源的操作 方法就是用atexit()函数来注册程序正常终止时要被调用的函数。 atexit()函数的参数是一个函数指针,函数指针指向一个没有参数也没有返回值的函数。 exit()函数运行时首先会执行由atexit()函数登记的函数,然后会做一些自身的清理工作,同时刷新所有输出流、关闭所有打开的流并且关闭通过标准I/O函数tmpfile()创建的临时文件。
正确处理SystemExit的最佳实践️ 3.1 使用atexit模块执行清理操作 在处理系统退出时,使用Python的 atexit 模块可以注册退出钩子,在程序退出时执行某些操作,而无需直接捕获 SystemExit import atexit def cleanup(): print("程序退出前执行清理操作...") atexit.register(cleanup) print("程序正在运行...") sys.exit(0) 在上面的代码中,atexit.register() 注册了一个清理函数,在程序正常退出之前会被执行 为了解决这个问题,可以结合 atexit 和 join() 来确保所有线程安全退出。 无论是通过捕获退出状态码、使用 atexit 进行清理,还是正确管理多线程,掌握这些技术将让你的程序在退出时更加稳定和可靠。
E3代理含义: 明白其上面的 atexit函数的原理,那么现在看看其E3内部的实现 ? E3内部其实是将E2函数注册进了atexit函数,当结束的时候则会调用E2 那么现在看看E2 E2函数内部: ? 答: 因为atexit的参数的c约定回调,而析构是thiscall,调用约定,所以内部必须包含一层才可以. 函数正常运行而注册的(atexit和ininterm类似,一个从前往后,一个从后往前) 6.E2是E3内部给atexit函数注册的回调,这样在析构的时候则调用E2即可. 7.E2函数内部是真正的调用析构的 实战中反汇编查找全局对象 既然我们知道了atexit函数会调用析构,那么我们在IDA中搜索atexit函数,看看谁引用了它,则可以把全局对象一网打尽. ? ?
atexit如果注册成功返回0,否则返回负数。 atexit_b如果注册成功返回0,否则返回负数。 #define ATEXIT_FN_EMPTY 0 #define ATEXIT_FN_STD 1 #define ATEXIT_FN_CXA 2 #define ATEXIT_FN_BLK static struct atexit *__atexit; /* points to head of LIFO stack */ struct atexit是一个链表和数组的结合体。 struct atexit的存储结构 从数据结构的定义以及atexit函数的描述和上面的图形我们应该可以很容易的去实现那三个注册函数。大家可以去阅读上面三个函数的实现,这里就不再列出了。
stdlib.h> void exit(int status); void _Exit(int status); #include <unistd.h> void _exit(int status); 3 atexit atexit()注册的函数类型应为不接受任何参数的void函数。 exit调用这些注册函数的顺序与它们 登记时候的顺序相反。同一个函数如若登记多次,则也会被调用多次。 #include "apue.h" static void my_exit1(void); static void my_exit2(void); int main(void) { if (atexit = 0) err_sys("can't register my_exit2"); if (atexit(my_exit1) ! = 0) err_sys("can't register my_exit1"); if (atexit(my_exit1) !
(It is possible for one of these func‐ tions to use atexit(3) or on_exit(3) to register an additional If a function has been registered multiple times using atexit(3) or on_exit(3), then it is called 在atexit(3)和on_exit(3)上注册的所有函数均以与注册相反的顺序调用。 (这些功能之一可能 使用atexit(3)或on_exit(3)来注册要在退出处理期间执行的其他功能的说明;新注册已添加到 如果这些函数之一未返回(例如,它调用_exit(2 如果一个 已使用atexit(3)或on_exit(3)多次注册该函数,然后调用该函数的次数与已注册的次数相同。 所有打开的stdio(3)流都被刷新并关闭。
main函数之后执行的函数 1、全局对象的析构函数会在main函数之后执行; 2、用atexit注册的函数也会在main之后执行。 atexit函数 原形: int atexit(void (*func)(void)); atexit 函数可以“注册”一个函数,使这个函数将在main函数正常终止时被调用,当程序异常终止时,通过它注册的函数并不会被调用 main()同样也是一个函数,在结束时,按出栈的顺序调用使用atexit函数注册的,所以说,函数atexit是注册的函数和函数入栈出栈一样,是先进后出的,先注册的后执行。 通过atexit可以注册回调清理函数。可以在这些函数中加入一些清理工作,比如内存释放、关闭打开的文件、关闭socket描述符、释放锁等等。 注册的函数的执行顺序:先注册的后执行 atexit( fn0 ); atexit( fn1 ); atexit( fn2 ); atexit( fn3 );
import sys import readline import rlcompleter import atexit import os readline.parse_and_bind('tab: complete os.environ['HOME'], '.pythonhistory') try: readline.read_history_file(histfile) except IOError: pass atexit.register
在用户宿主目录建立 .pythonstartup 文件内容如下: # python startup file import readline import rlcompleter import atexit HOME'], '.pythonhistory') try: readline.read_history_file(histfile) except IOError: pass atexit.register
标准库 atexit 就是解决这个问题的。 使用 atexit.register 注册一个函数, 该函数在在脚本运行完后立马执行,无论当前脚本是否报错、抛异常、被 kill,可用于脚本执行结束时测量一些基准数据,比如运行了多长时间: import atexit import time start_time = time.time() def shutdown(): print("Execution took: {0} seconds" .format(time.time() - start_time)) atexit.register(shutdown) time.sleep(3) print(1/0) 运行结果如下: File print(1/0) ZeroDivisionError: division by zero Execution took: 3.0044968128204346 seconds 可以使用 atexit.register
output # Original Size: 1022 # Compressed Size: 423 # Decompressed Size: 1022 06.注册Shutdown函数 有可模块叫atexit 假如你想在脚本执行结束时测量一些基准数据,比如运行了多长时间: import atexit import time import math def microtime(get_as_float=False (shutdown) # Execution took: 0.297000 1387135607 seconds # Error in atexit. _run_exitfuncs: # Traceback (most recent call last): # File "C:\Python27\lib\atexit.py", line 24, in 当你使用atexit.register()时,你的代码都将执行,不论脚本因为什么原因停止运行。 ?
首先在家目录下创建一个隐藏文件,vi ~/.pythonstartup,内容如下: # python startup file import readline import rlcompleter import atexit os.environ['HOME'],'.pythonhistory') try: readline.read_history_file(histfile) except IOError: pass atexit.register
/usr/bin/env python # python startup file import sys import readline import rlcompleter import atexit os.environ['HOME'], '.pythonhistory') try: readline.read_history_file(histfile) except IOError: pass atexit.register
3、使用atexit注册进程终止处理函数,我们使用man手册来查看它的介绍: int atexit(void(*func)(void)); 注意:atexit()注册的函数类型应为不接受任何参数的void 函数,atexit的参数是一个函数地址(或者说是一个函数指针),当调用此函数(指的是atexit的参数 )时无须传递任何参数,该函数也不能返回值,atexit函数称为终止处理程序注册程序,注册完成以后。 \n"); // 当进程被正常终止时,系统会自动调用这里注册的func1执行 atexit(func2); atexit(func1); return 0; } 注:这里还有一点要注意的地方就是 return、exit和_exit的区别:return和exit效果一样,都是会执行进程终止处理函数,但是用_exit终止进程时并不执行atexit注册的进程终止处理函数(类似于单片机的中断)。 \n"); // 当进程被正常终止时,系统会自动调用这里注册的func1执行 atexit(func2); atexit(func1); printf("i like the rtos\n");
/usr/bin/env python import sys import readline import rlcompleter import atexit import os #tab completion os.environ['HOME'],'.pythonhistory') try: readline.read_history_file(histfile) except IOError: pass atexit.register