zoukankan      html  css  js  c++  java
  • atexit注册的函数是在main函数之后执行?

    跟atexit函数相识已久,man手册里对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函数”?

    先上一段简单代码:

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <unistd.h>
     4 #include <pthread.h>
     5 
     6 void bye(void)
     7 {
     8     printf("before bye
    ");
     9     sleep(10);
    10     printf("after bye
    ");
    11 }
    12 
    13 void *do_thread(void)
    14 {
    15     while(1)
    16     {
    17         printf("--------------I'm thread!
    ");
    18         sleep(1);
    19     }
    20 }
    21 
    22 int main(void)
    23 {
    24     pthread_t pid_t;
    25     
    26     atexit(bye);
    27     pthread_create(&pid_t, NULL, (void *)do_thread, NULL);
    28 
    29     exit(EXIT_SUCCESS);
    30 }

    上面的程序先用atexit注册一个程序正常退出后的执行函数,再创建一线程用来不断输出信息,然后主线程执行exit;

    运行程序会输出什么呢?

    我一开始的猜测运行结果应该是:

    before bye

    after bye

    或者是:

    --------------I'm thread!

    before bye

    after bye

    因为在我的理解中,bye()是在退出main函数之后执行,那时候,main函数里创建的线程什么的应该都不复存在了,代码会清清静静地执行bye()函数。事实证明,我太想当然了。

    上面程序实际运行结果是:

     1 before bye
     2 --------------I'm thread!
     3 --------------I'm thread!
     4 --------------I'm thread!
     5 --------------I'm thread!
     6 --------------I'm thread!
     7 --------------I'm thread!
     8 --------------I'm thread!
     9 --------------I'm thread!
    10 --------------I'm thread!
    11 --------------I'm thread!
    12 after bye

    为什么在执行bye()的时候线程还在呢?

    来看一下exit()函数的源码:

      1 /* Copyright (C) 1991,95,96,97,99,2001,2002,2005,2009
      2    Free Software Foundation, Inc.
      3    This file is part of the GNU C Library.
      4 
      5    The GNU C Library is free software; you can redistribute it and/or
      6    modify it under the terms of the GNU Lesser General Public
      7    License as published by the Free Software Foundation; either
      8    version 2.1 of the License, or (at your option) any later version.
      9 
     10    The GNU C Library is distributed in the hope that it will be useful,
     11    but WITHOUT ANY WARRANTY; without even the implied warranty of
     12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13    Lesser General Public License for more details.
     14 
     15    You should have received a copy of the GNU Lesser General Public
     16    License along with the GNU C Library; if not, see
     17    <http://www.gnu.org/licenses/>.  */
     18 
     19 #include <stdio.h>
     20 #include <stdlib.h>
     21 #include <unistd.h>
     22 #include <sysdep.h>
     23 #include "exit.h"
     24 
     25 #include "set-hooks.h"
     26 DEFINE_HOOK (__libc_atexit, (void))
     27 
     28 
     29 /* Call all functions registered with `atexit' and `on_exit',
     30    in the reverse of the order in which they were registered
     31    perform stdio cleanup, and terminate program execution with STATUS.  */
     32 void
     33 attribute_hidden
     34 __run_exit_handlers (int status, struct exit_function_list **listp,
     35              bool run_list_atexit)
     36 {
     37   /* We do it this way to handle recursive calls to exit () made by
     38      the functions registered with `atexit' and `on_exit'. We call
     39      everyone on the list and use the status value in the last
     40      exit (). */
     41   while (*listp != NULL)
     42     {
     43       struct exit_function_list *cur = *listp;
     44 
     45       while (cur->idx > 0)
     46     {
     47       const struct exit_function *const f =
     48         &cur->fns[--cur->idx];
     49       switch (f->flavor)
     50         {
     51           void (*atfct) (void);
     52           void (*onfct) (int status, void *arg);
     53           void (*cxafct) (void *arg, int status);
     54 
     55         case ef_free:
     56         case ef_us:
     57           break;
     58         case ef_on:
     59           onfct = f->func.on.fn;
     60 #ifdef PTR_DEMANGLE
     61           PTR_DEMANGLE (onfct);
     62 #endif
     63           onfct (status, f->func.on.arg);
     64           break;
     65         case ef_at:
     66           atfct = f->func.at;
     67 #ifdef PTR_DEMANGLE
     68           PTR_DEMANGLE (atfct);
     69 #endif
     70           atfct ();
     71           break;
     72         case ef_cxa:
     73           cxafct = f->func.cxa.fn;
     74 #ifdef PTR_DEMANGLE
     75           PTR_DEMANGLE (cxafct);
     76 #endif
     77           cxafct (f->func.cxa.arg, status);
     78           break;
     79         }
     80     }
     81 
     82       *listp = cur->next;
     83       if (*listp != NULL)
     84     /* Don't free the last element in the chain, this is the statically
     85        allocate element.  */
     86     free (cur);
     87     }
     88 
     89   if (run_list_atexit)
     90     RUN_HOOK (__libc_atexit, ());
     91 
     92   _exit (status);
     93 }
     94 
     95 
     96 void
     97 exit (int status)
     98 {
     99   __run_exit_handlers (status, &__exit_funcs, true);
    100 }
    101 libc_hidden_def (exit)

    从上面的源码可以看出:exit()先是执行atexit注册的函数,然后再执行_exit函数,_exit会关闭进程所有的文件描述符,清理内存以及其他一些内核清理函数。所以当exit()执行到_exit()的时候,之前创建的线程才会停止运行。之前我脑海里存在的“退出main函数”的概念还是太抽象了,其背后存在的其实是一个动作流,会执行atexit注册的函数,刷新流(stdin, stdout, stderr),把文件缓冲区的内容写回文件,关闭进程所有的文件描述符,清理内存以及其他一些内核清理函数。exit()和_exit()源码有待好好研究。

    再次回首篇头那一段atexit的解释,别有一番深意。

  • 相关阅读:
    scp命令详解
    linux下不同服务器间数据传输(rcp,scp,rsync,ftp,sftp,lftp,wget,curl)
    详解代理自动配置 PAC
    linux卸载
    VMware里克隆出来的CentOS Linux device eth0 does not seem to be present, delaying initialization
    CentOS系统更换软件安装源yum
    kali开启禁止或删除ssh 开机启动
    7天玩转 ASP.NET MVC
    C#高级编程42章 MVC
    C#高级编程(32章)ADO.net
  • 原文地址:https://www.cnblogs.com/tanghuimin0713/p/3572276.html
Copyright © 2011-2022 走看看