atexit注册的函数是在main函数之后执行?
2014-02-27 20:06
405 查看
跟atexit函数相识已久,man手册里对atexit的解释是这么一段:
乍一看,就形成了这样的印象:“哦,atexit函数就是来注册一个函数A,使main函数退出后,要执行一下函数A,进程才会彻底over”。
直到工作中遇到一个段错的bug,日志中发现,进程在执行atexit注册过的函数时,main函数里的线程依然在快活地运行,这种现象颠覆了我以往的认知,让我不得不重新思考,atexit注册的函数到底什么时候执行?何为“退出main函数”?
先上一段简单代码:
上面的程序先用atexit注册一个程序正常退出后的执行函数,再创建一线程用来不断输出信息,然后主线程执行exit;
运行程序会输出什么呢?
我一开始的猜测运行结果应该是:
before bye
after bye
或者是:
--------------I'm thread!
before bye
after bye
因为在我的理解中,bye()是在退出main函数之后执行,那时候,main函数里创建的线程什么的应该都不复存在了,代码会清清静静地执行bye()函数。事实证明,我太想当然了。
上面程序实际运行结果是:
为什么在执行bye()的时候线程还在呢?
来看一下exit()函数的源码:
从上面的源码可以看出:exit()先是执行atexit注册的函数,然后再执行_exit函数,_exit会关闭进程所有的文件描述符,清理内存以及其他一些内核清理函数。所以当exit()执行到_exit()的时候,之前创建的线程才会停止运行。之前我脑海里存在的“退出main函数”的概念还是太抽象了,其背后存在的其实是一个动作流,会执行atexit注册的函数,刷新流(stdin, stdout, stderr),把文件缓冲区的内容写回文件,关闭进程所有的文件描述符,清理内存以及其他一些内核清理函数。exit()和_exit()源码有待好好研究。
再次回首篇头那一段atexit的解释,别有一番深意。
The atexit() function registers the given function to be called at normal process termination, either via exit(3) or via return from the program’s main(). Functions so registered are called in the reverse order of their registration; no arguments are passed.
乍一看,就形成了这样的印象:“哦,atexit函数就是来注册一个函数A,使main函数退出后,要执行一下函数A,进程才会彻底over”。
直到工作中遇到一个段错的bug,日志中发现,进程在执行atexit注册过的函数时,main函数里的线程依然在快活地运行,这种现象颠覆了我以往的认知,让我不得不重新思考,atexit注册的函数到底什么时候执行?何为“退出main函数”?
先上一段简单代码:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> void bye(void) { printf("before bye\n"); sleep(10); printf("after bye\n"); } void *do_thread(void) { while(1) { printf("--------------I'm thread!\n"); sleep(1); } } int main(void) { pthread_t pid_t; atexit(bye); pthread_create(&pid_t, NULL, (void *)do_thread, NULL); exit(EXIT_SUCCESS); }
上面的程序先用atexit注册一个程序正常退出后的执行函数,再创建一线程用来不断输出信息,然后主线程执行exit;
运行程序会输出什么呢?
我一开始的猜测运行结果应该是:
before bye
after bye
或者是:
--------------I'm thread!
before bye
after bye
因为在我的理解中,bye()是在退出main函数之后执行,那时候,main函数里创建的线程什么的应该都不复存在了,代码会清清静静地执行bye()函数。事实证明,我太想当然了。
上面程序实际运行结果是:
before bye --------------I'm thread! --------------I'm thread! --------------I'm thread! --------------I'm thread! --------------I'm thread! --------------I'm thread! --------------I'm thread! --------------I'm thread! --------------I'm thread! --------------I'm thread! after bye
为什么在执行bye()的时候线程还在呢?
来看一下exit()函数的源码:
/* Copyright (C) 1991,95,96,97,99,2001,2002,2005,2009 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, see <http://www.gnu.org/licenses/>. */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sysdep.h> #include "exit.h" #include "set-hooks.h" DEFINE_HOOK (__libc_atexit, (void)) /* Call all functions registered with `atexit' and `on_exit', in the reverse of the order in which they were registered perform stdio cleanup, and terminate program execution with STATUS. */ void attribute_hidden __run_exit_handlers (int status, struct exit_function_list **listp, bool run_list_atexit) { /* We do it this way to handle recursive calls to exit () made by the functions registered with `atexit' and `on_exit'. We call everyone on the list and use the status value in the last exit (). */ while (*listp != NULL) { struct exit_function_list *cur = *listp; while (cur->idx > 0) { const struct exit_function *const f = &cur->fns[--cur->idx]; switch (f->flavor) { void (*atfct) (void); void (*onfct) (int status, void *arg); void (*cxafct) (void *arg, int status); case ef_free: case ef_us: break; case ef_on: onfct = f->func.on.fn; #ifdef PTR_DEMANGLE PTR_DEMANGLE (onfct); #endif onfct (status, f->func.on.arg); break; case ef_at: atfct = f->func.at; #ifdef PTR_DEMANGLE PTR_DEMANGLE (atfct); #endif atfct (); break; case ef_cxa: cxafct = f->func.cxa.fn; #ifdef PTR_DEMANGLE PTR_DEMANGLE (cxafct); #endif cxafct (f->func.cxa.arg, status); break; } } *listp = cur->next; if (*listp != NULL) /* Don't free the last element in the chain, this is the statically allocate element. */ free (cur); } if (run_list_atexit) RUN_HOOK (__libc_atexit, ()); _exit (status); } void exit (int status) { __run_exit_handlers (status, &__exit_funcs, true); } libc_hidden_def (exit)
从上面的源码可以看出:exit()先是执行atexit注册的函数,然后再执行_exit函数,_exit会关闭进程所有的文件描述符,清理内存以及其他一些内核清理函数。所以当exit()执行到_exit()的时候,之前创建的线程才会停止运行。之前我脑海里存在的“退出main函数”的概念还是太抽象了,其背后存在的其实是一个动作流,会执行atexit注册的函数,刷新流(stdin, stdout, stderr),把文件缓冲区的内容写回文件,关闭进程所有的文件描述符,清理内存以及其他一些内核清理函数。exit()和_exit()源码有待好好研究。
再次回首篇头那一段atexit的解释,别有一番深意。
相关文章推荐
- atexit()函数(使main函数之后可以执行其他函数)
- atexit 在函数正常退出时执行的函数注册
- C++面试题: main函数执行完之后还会调用其他的函数吗?
- mian函数之前执行函数,和main函数之后执行函数
- C++面试题之 main函数执行完之后还会调用其他的函数吗?
- 如何使得OnInitDialog之后执行某个函数
- main函数之前--真正的函数执行入口或开始
- 让自己的函数在main函数之前执行
- atexit注册终止函数
- atexit(在main函数执行完毕后,是否可能再执行一段代码?)
- main主函数执行完之后再执行其他代码的方法
- 在main函数执行之前和执行之后执行的方法
- C语言中如何在main函数开始前执行函数
- atexit注册进程终止处理函数
- 如何让vuejs中ready函数加载完之后执行某个函数?
- 让软件在退出的时候自动执行函数 -- atexit()
- atexit()函数来注册程序正常终止时被调用的函数
- javaScript学习笔记——如何在加载完某个标签之后执行一个函数
- jquery页面加载页面之后就执行的函数及区别
- C在return之后执行函数