5 exit linux_exit linux - CSDN
  • exit()就是退出,传入的参数是程序退出时的状态码,0表示正常退出,其他表示非正常退出,一般都用-1或者1,标准C里有EXIT_SUCCESS和EXIT_FAILURE两个宏,用exit(EXIT_SUCCESS);可读性比较好一点。 作为系统调用而言...

    exit()就是退出,传入的参数是程序退出时的状态码,0表示正常退出,其他表示非正常退出,一般都用-1或者1,标准C里有EXIT_SUCCESS和EXIT_FAILURE两个宏,用exit(EXIT_SUCCESS);可读性比较好一点。
    1、异同:
    作为系统调用而言,_exit和exit是一对孪生兄弟,它们究竟相似到什么程度,我们可以从Linux的源码中找到答案:
    这里写图片描述

    _NR“是在Linux的源码中为每个系统调用加上的前缀,请注意第一个exit前有2条下划线,第二个exit前只有1条下划线。
    这时随便一个懂得C语言并且头脑清醒的人都会说,_exit和exit没有任何区别,但我们还要讲一下这两者之间的区别,这种区别主要体现在它们在函数库中的定义。_exit在Linux函数库中的原型是:
    这里写图片描述

    exit()函数定义在stdlib.h中,而_exit()定义在unistd.h中
    _exit()函数的作用最为简单:直接使进程停止运行,清除其使用的内存空间,并销毁其在内核中的各种数据结构;exit() 函数则在这些基础上作了一些包装,在执行退出之前加了若干道工序,也是因为这个原因,有些人认为exit已经不能算是纯粹的系统调用。
    exit()函数与_exit()函数最大的区别就在于exit()函数在调用exit系统调用之前要检查文件的打开情况,把文件缓冲区中的内容写回文件,就是”清理I/O缓冲”。

    2、exit()在结束调用它的进程之前,要进行如下步骤:
    1.调用atexit()注册的函数(出口函数);按ATEXIT注册时相反的顺序调用所有由它注册的函数,这使得我们可以指定在程序终止时执行自己的清理动作.例如,保存程序状态信息于某个文件,解开对共享数据库上的锁等.

    2.cleanup();关闭所有打开的流,这将导致写所有被缓冲的输出,删除用TMPFILE函数建立的所有临时文件.

    3.最后调用_exit()函数终止进程。

    2、_exit做3件事(man):
    1,Any open file descriptors belonging to the process are closed
    2,any children of the process are inherited by process 1, init
    3,the process’s parent is sent a SIGCHLD signal

    exit执行完清理工作后就调用_exit来终止进程。

    在Linux的标准函数库中,有一套称作”高级I/O”的函数,我们熟知的printf()、fopen()、fread()、fwrite()都在此 列,它们也被称作”缓冲I/O(buffered I/O)”,其特征是对应每一个打开的文件,在内存中都有一片缓冲区,每次读文件时,会多读出若干条记录,这样下次读文件时就可以直接从内存的缓冲区中读取,每次写文件的时候,也仅仅是写入内存中的缓冲区,等满足了一定的条件(达到一定数量,或遇到特定字符,如换行符和文件结束符EOF), 再将缓冲区中的 内容一次性写入文件,这样就大大增加了文件读写的速度,但也为我们编程带来了一点点麻烦。如果有一些数据,我们认为已经写入了文件,实际上因为没有满足特 定的条件,它们还只是保存在缓冲区内,这时我们用_exit()函数直接将进程关闭,缓冲区中的数据就会丢失,反之,如果想保证数据的完整性,就一定要使用exit()函数。

    简单的说,exit函数将终止调用进程。在退出程序之前,所有文件关闭,缓冲输出内容将刷新定义,并调用所有已刷新的“出口函数”(由atexit定义)。
    _exit:该函数是由Posix定义的,不会运行exit handler和signal handler,在UNIX系统中不会flush标准I/O流。
    简单的说,_exit终止调用进程,但不关闭文件,不清除输出缓存,也不调用出口函数。
    共同:
    不管进程是如何终止的,内核都会关闭进程打开的所有file descriptors,释放进程使用的memory!

    更详细的介绍:
    Calling exit()
    The exit() function causes normal program termination.
    The exit() function performs the following functions:
    1. All functions registered by the Standard C atexit() function are called in the reverse
    order of registration. If any of these functions calls exit(), the results are not portable.
    2. All open output streams are flushed (data written out) and the streams are closed.
    3. All files created by tmpfile() are deleted.
    4. The _exit() function is called.

    Calling _exit()
    The _exit() function performs operating system-specific program termination functions.
    These include:
    1. All open file descriptors and directory streams are closed.
    2. If the parent process is executing a wait() or waitpid(), the parent wakes up and
    status is made available.
    3. If the parent is not executing a wait() or waitpid(), the status is saved for return to
    the parent on a subsequent wait() or waitpid().
    4. Children of the terminated process are assigned a new parent process ID. Note: the
    termination of a parent does not directly terminate its children.
    5. If the implementation supports the SIGCHLD signal, a SIGCHLD is sent to the parent.
    6. Several job control signals are sent.

    为何在一个fork的子进程分支中使用_exit函数而不使用exit函数?
    ‘exit()’与‘_exit()’有不少区别在使用‘fork()’,特别是‘vfork()’时变得很
    突出。
    ‘exit()’与‘_exit()’的基本区别在于前一个调用实施与调用库里用户状态结构(user-mode constructs)有关的清除工作(clean-up),而且调用用户自定义的清除程序 (自定义清除程序由atexit函数定义,可定义多次,并以倒序执行),相对应,_exit函数只为进程实施内核清除工作。

    在由‘fork()’创建的子进程分支里,正常情况下使用‘exit()’是不正确的,这是 因为使用它会导致标准输入输出(stdio: Standard Input Output)的缓冲区被清空两次,而且临时文件被出乎意料的删除(临时文件由tmpfile函数创建在系统临时目录下,文件名由系统随机生成)。在C++程序中情况会更糟,因为静态目标(static objects)的析构函数(destructors)可以被错误地执行。(还有一些特殊情况,比如守护程序,它们的父进程需要调用‘_exit()’而不是子进程;适用于绝大多数情况的基本规则是,‘exit()’在每一次进入‘main’函数后只调用一次。)

    展开全文
  • Linuxexit命令

    千次阅读 2019-04-30 10:39:18
    Linuxexit命令

    首先给大家分享一个巨牛巨牛的人工智能教程,是我无意中发现的。教程不仅零基础,通俗易懂,而且非常风趣幽默,还时不时有内涵段子,像看小说一样,哈哈~我正在学习中,觉得太牛了,所以分享给大家!点这里可以跳转到教程

                    用途说明
    exit命令用于退出当前shell,在shell脚本中可以终止当前脚本执行
     
    常用参数
    格式:exit n
    退出。设置退出码为n
     
    格式:exit
    退出。退出码不变,即为最后一个命令的退出码
     
    格式:$?
    上一个命令的退出码

    退出码(exit status或exit code)的约定
    0表示成功(Zero - Success)
    非0表示失败(Non-Zero  - Failure)
    2表示用法不当(Incorrect Usage)
    127表示命令没有找到(Command Not Found)
    126表示不是可执行的(Not an executable)
    >=128 信号产生


    原贴地址:http://blog.163.com/bobile45@126/blog/static/96061992201311712658570/
               

    浏览人工智能教程

    展开全文
  • linux c语言return 和 exit()的区别

    千次阅读 2018-08-20 16:29:14
    return 和 exit()的区别: exit函数在头文件stdlib.h中。 exit(0):正常运行程序并退出程序; exit(1):非正常运行导致退出程序; return():返回函数,若在main主函数中,则会退出函数并返回一值,可以写...

    return 和 exit()的区别:

    exit函数在头文件stdlib.h中。

    exit(0):正常运行程序并退出程序;

    exit(1):非正常运行导致退出程序;

    return():返回函数,若在main主函数中,则会退出函数并返回一值,可以写为return(0),或return 0。

    具体:

    1. return返回函数值,是关键字;exit是一个函数。

    2. return是语言级别的,它表示了调用堆栈的返回;而exit是系统调用级别的,它表示了一个进程的结束。

    3. return是函数的退出(返回);exit是进程的退出。

    4. return是C语言提供的,exit是操作系统提供的(或者函数库中给出的)。

    5. return用于结束一个函数的执行,将函数的执行信息传出个其他调用函数使用;exit函数是退出应用程序,删除进程使用的内存空间,并将应用程序的一个状态返回给OS,这个状态标识了应用程序的一些运行信息,这个信息和机器和操作系统有关,一般是 0 为正常退出,非0 为非正常退出。

    6. 【重要】非主函数中调用return和exit效果很明显,但是在main函数中调用return和exit的现象就很模糊,多数情况下现象都是一致的。【在子函数调用exit会退出进程,但是return只是返回;在主函数exit和return都会退出进程。】

    展开全文
  • 日期 内核版本 架构 作者 GitHub CSDN 2016-05-12 Linux-4.6 ...Linux进程的退出linux下进程退出的方式正常退出 从main函数返回return 调用exit 调用_exit 异常退出 调用abort 由信号终止 _exit
    日期 内核版本 架构 作者 GitHub CSDN
    2016-05-12 Linux-4.6 X86 & arm gatieme LinuxDeviceDrivers Linux进程管理与调度

    Linux进程的退出


    linux下进程退出的方式


    正常退出

    • 从main函数返回return

    • 调用exit

    • 调用_exit

    异常退出

    • 调用abort

    • 由信号终止

    _exit, exit和_Exit的区别和联系


    _exit是linux系统调用,关闭所有文件描述符,然后退出进程。

    exit是c语言的库函数,他最终调用_exit。在此之前,先清洗标准输出的缓存,调用用atexit注册的函数等, 在c语言的main函数中调用return就等价于调用exit。

    _Exit是c语言的库函数,自c99后加入,等价于_exit,即可以认为它直接调用_Exit。

    基本来说,_Exit(或 _exit,建议使用大写版本)是为 fork 之后的子进程准备的特殊 API。功能见POSIX 标准:_Exit,讨论见 c - how to exit a child process

    由fork()函数创建的子进程分支里,正常情况下使用函数exit()是不正确的,这是因为使用它会导致标准输入输出的缓冲区被清空两次,而且临时文件可能被意外删除。”

    因为在 fork 之后,exec 之前,很多资源还是共享的(如某些文件描述符),如果使用 exit 会关闭这些资源,导致某些非预期的副作用(如删除临时文件等)。

    「刷新」是对应 flush,意思是把内容从内存缓存写出到文件里,而不仅仅是清空(所以常见的对 stdin 调用 flush 的方法是耍流氓而已)。如果在 fork 的时候父进程内存有缓冲内容,则这个缓冲会带到子进程,并且两个进程会分别 flush (写出)一次,造成数据重复。参见c - How does fork() work with buffered streams like stdout?

    进程退出的系统调用


    _exit和exit_group系统调用


    _exit系统调用

    进程退出由exit系统调用来完成, 这使得内核有机会将该进程所使用的资源释放回系统中

    进程终止时,一般是调用exit库函数(无论是程序员显式调用还是编译器自动地把exit库函数插入到main函数的最后一条语句之后)来释放进程所拥有的资源。

    exit系统调用的入口点是sys_exit()函数, 需要一个错误码作为参数, 以便退出进程。

    其定义是体系结构无关的, 见kernel/exit.c

    而我们用户空间的多线程应用程序, 对应内核中就有多个进程, 这些进程共享虚拟地址空间和资源, 他们有各自的进程id(pid), 但是他们的组进程id(tpid)是相同的, 都等于组长(领头进程)的pid

    在linux内核中对线程并没有做特殊的处理,还是由task_struct来管理。所以从内核的角度看, 用户态的线程本质上还是一个进程。对于同一个进程(用户态角度)中不同的线程其tgid是相同的,但是pid各不相同。 主线程即group_leader(主线程会创建其他所有的子线程)。如果是单线程进程(用户态角度),它的pid等于tgid。

    这个信息我们已经讨论过很多次了

    参见

    Linux进程ID号–Linux进程的管理与调度(三)

    Linux进程描述符task_struct结构体详解–Linux进程的管理与调度(一)

    为什么还需要exit_group

    我们如果了解linux的线程实现机制的话, 会知道所有的线程是属于一个线程组的, 同时即使不是线程, linux也允许多个进程组成进程组, 多个进程组组成一个会话, 因此我们本质上了解到不管是多线程, 还是进程组起本质都是多个进程组成的一个集合, 那么我们的应用程序在退出的时候, 自然希望一次性的退出组内所有的进程。

    因此exit_group就诞生了

    group_exit函数会杀死属于当前进程所在线程组的所有进程。它接受进程终止代号作为参数,进程终止代号可能是系统调用exit_group(正常结束)指定的一个值,也可能是内核提供的一个错误码(异常结束)。

    因此C语言的库函数exit使用系统调用exit_group来终止整个线程组,库函数pthread_exit使用系统调用_exit来终止某一个线程

    _exit和exit_group这两个系统调用在Linux内核中的入口点函数分别为sys_exit和sys_exit_group。

    系统调用声明


    声明见include/linux/syscalls.h, line 326

    asmlinkage long sys_exit(int error_code);
    asmlinkage long sys_exit_group(int error_code);
    
    asmlinkage long sys_wait4(pid_t pid, int __user *stat_addr,
                                    int options, struct rusage __user *ru);
    asmlinkage long sys_waitid(int which, pid_t pid,
                               struct siginfo __user *infop,
                               int options, struct rusage __user *ru);
    asmlinkage long sys_waitpid(pid_t pid, int __user *stat_addr, int options);

    系统调用号


    其系统调用号是一个体系结构相关的定义, 但是多数体系结构的定义如下, 在include/uapi/asm-generic/unistd.h, line 294文件中

    /* kernel/exit.c */
    #define __NR_exit 93
    __SYSCALL(__NR_exit, sys_exit)
    #define __NR_exit_group 94
    __SYSCALL(__NR_exit_group, sys_exit_group)
    #define __NR_waitid 95
    __SC_COMP(__NR_waitid, sys_waitid, compat_sys_waitid)

    只有少数体系结构, 重新定义了系统调用号

    体系 定义
    xtensa rch/xtensa/include/uapi/asm/unistd.h, line 267
    arm64 rch/arm64/include/asm/unistd32.h, line 27
    通用 include/uapi/asm-generic/unistd.h, line 294

    系统调用实现


    然后系统调用的实现在kernel/exit.c

    SYSCALL_DEFINE1(exit, int, error_code)
    {
            do_exit((error_code&0xff)<<8);
    }
    
    
    /*
     * this kills every thread in the thread group. Note that any externally
     * wait4()-ing process will get the correct exit code - even if this
     * thread is not the thread group leader.
     */
    SYSCALL_DEFINE1(exit_group, int, error_code)
    {
            do_group_exit((error_code & 0xff) << 8);
            /* NOTREACHED */
            return 0;
    }

    do_exit_group流程


    do_group_exit()函数杀死属于current线程组的所有进程。它接受进程终止代码作为参数,进程终止代号可能是系统调用exit_group()指定的一个值,也可能是内核提供的一个错误代号。
    该函数执行下述操作

    1. 检查退出进程的SIGNAL_GROUP_EXIT标志是否不为0,如果不为0,说明内核已经开始为线性组执行退出的过程。在这种情况下,就把存放在current->signal->group_exit_code的值当作退出码,然后跳转到第4步。

    2. 否则,设置进程的SIGNAL_GROUP_EXIT标志并把终止代号放到current->signal->group_exit_code字段。

    3. 调用zap_other_threads()函数杀死current线程组中的其它进程。为了完成这个步骤,函数扫描与current->tgid对应的PIDTYPE_TGID类型的散列表中的每PID链表,向表中所有不同于current的进程发送SIGKILL信号,结果,所有这样的进程都将执行do_exit()函数,从而被杀死。

    4. 调用do_exit()函数,把进程的终止代码传递给它。正如我们将在下面看到的,do_exit()杀死进程而且不再返回。

    /*
     * Take down every thread in the group.  This is called by fatal signals
     * as well as by sys_exit_group (below).
     */
    void
    do_group_exit(int exit_code)
    {
        struct signal_struct *sig = current->signal;
    
        BUG_ON(exit_code & 0x80); /* core dumps don't get here */
        /*
            检查current->sig->flags的SIGNAL_GROUP_EXIT标志是否置位
            或者current->sig->group_exit_task是否不为NULL
        */
        if (signal_group_exit(sig))
            exit_code = sig->group_exit_code;   /*  group_exit_code存放的是线程组终止代码  */
        else if (!thread_group_empty(current)) {    /*  检查线程组链表是否不为空  */
            struct sighand_struct *const sighand = current->sighand;
    
            spin_lock_irq(&sighand->siglock);
            if (signal_group_exit(sig))
                /* Another thread got here before we took the lock.  */
                exit_code = sig->group_exit_code;
            else {
                sig->group_exit_code = exit_code;
                sig->flags = SIGNAL_GROUP_EXIT;
                zap_other_threads(current);     /*  遍历整个线程组链表,并杀死其中的每个线程  */
            }
            spin_unlock_irq(&sighand->siglock);
        }
    
        do_exit(exit_code);
        /* NOTREACHED */
    }

    do_exit流程


    进程终止所要完成的任务都是由do_exit函数来处理。

    该函数定义在kernel/exit.c

    触发task_exit_nb通知链实例的处理函数

    profile_task_exit(tsk);
    

    该函数会定义在触发kernel/profile.c

    void profile_task_exit(struct task_struct *task)
    {
        blocking_notifier_call_chain(&task_exit_notifier, 0, task);
    }

    会触发task_exit_notifier通知, 从而触发对应的处理函数

    其中task_exit_notifier被定义如下

    //  http://lxr.free-electrons.com/source/kernel/profile.c?v=4.6#L134
    static BLOCKING_NOTIFIER_HEAD(task_exit_notifier);
    
    
    // http://lxr.free-electrons.com/source/include/linux/notifier.h?v=4.6#L111
    #define BLOCKING_NOTIFIER_INIT(name) {                      \
                    .rwsem = __RWSEM_INITIALIZER((name).rwsem),     \
                    .head = NULL }
    
    // http://lxr.free-electrons.com/source/include/linux/rwsem.h?v4.6#L74
    #define __RWSEM_INITIALIZER(name)                               \
            { .count = RWSEM_UNLOCKED_VALUE,                        \
              .wait_list = LIST_HEAD_INIT((name).wait_list),        \
              .wait_lock = __RAW_SPIN_LOCK_UNLOCKED(name.wait_lock) \
              __RWSEM_OPT_INIT(name)                                \
              __RWSEM_DEP_MAP_INIT(name) }
    
    

    检查进程的blk_plug是否为空

    保证task_struct中的plug字段是空的,或者plug字段指向的队列是空的。plug字段的意义是stack plugging

    //  http://lxr.free-electrons.com/source/include/linux/blkdev.h?v=4.6#L1095
    WARN_ON(blk_needs_flush_plug(tsk));

    其中blk_needs_flush_plug函数定义在include/linux/blkdev.h, 如下

    static inline bool blk_needs_flush_plug(struct task_struct *tsk)
    {
        struct blk_plug *plug = tsk->plug;
    
        return plug &&
            (!list_empty(&plug->list) ||
            !list_empty(&plug->mq_list) ||
            !list_empty(&plug->cb_list));
    }

    OOPS消息


    中断上下文不能执行do_exit函数, 也不能终止PID为0的进程。

    if (unlikely(in_interrupt()))
        panic("Aiee, killing interrupt handler!");
    if (unlikely(!tsk->pid))
        panic("Attempted to kill the idle task!");

    设定进程可以使用的虚拟地址的上限(用户空间)


    /*
     * If do_exit is called because this processes oopsed, it's possible
     * that get_fs() was left as KERNEL_DS, so reset it to USER_DS before
     * continuing. Amongst other possible reasons, this is to prevent
     * mm_release()->clear_child_tid() from writing to a user-controlled
     * kernel address.
     *
     * 设定进程可以使用的虚拟地址的上限(用户空间)
     * http://lxr.free-electrons.com/ident?v=4.6;i=set_fs
     */
    set_fs(USER_DS);
    
    这个是一个体系结构相关的代码, 其定义如下
    

    其定义在arch/对应体系/include/asm/uaccess.h中

    体系 定义
    arm arch/arm/include/asm/uaccess.h, line 99
    arm64 arch/arm64/include/asm/uaccess.h, line 66
    x86 arch/x86/include/asm/uaccess.h, line 32
    通用 include/asm-generic/uaccess.h, line 28

    arm64的定义如下

    static inline void set_fs(mm_segment_t fs)
    {
        current_thread_info()->addr_limit = fs;
    
        /*
         * Enable/disable UAO so that copy_to_user() etc can access
         * kernel memory with the unprivileged instructions.
        */
        if (IS_ENABLED(CONFIG_ARM64_UAO) && fs == KERNEL_DS)
            asm(ALTERNATIVE("nop", SET_PSTATE_UAO(1), ARM64_HAS_UAO));
        else
            asm(ALTERNATIVE("nop", SET_PSTATE_UAO(0), ARM64_HAS_UAO,
            CONFIG_ARM64_UAO));
    }

    检查进病设置进程程PF_EXITING


    首先是检查PF_EXITING标识, 此标识表示进程正在退出, 

    如果此标识已被设置, 则进一步设置PF_EXITPIDONE标识, 并将进程的状态设置为不可中断状态TASK_UNINTERRUPTIBLE, 并进程一次进程调度

        /*current->flags的PF_EXITING标志表示进程正在被删除  */
        if (unlikely(tsk->flags & PF_EXITING)) {  /*  检查PF_EXITING标志是否未被设置  */
            pr_alert("Fixing recursive fault but reboot is needed!\n");
            /*
             * We can do this unlocked here. The futex code uses
             * this flag just to verify whether the pi state
             * cleanup has been done or not. In the worst case it
             * loops once more. We pretend that the cleanup was
             * done as there is no way to return. Either the
             * OWNER_DIED bit is set by now or we push the blocked
             * task into the wait for ever nirwana as well.
             */
            /*  设置进程标识为PF_EXITPIDONE*/
            tsk->flags |= PF_EXITPIDONE;
            /*  设置进程状态为不可中断的等待状态 */
            set_current_state(TASK_UNINTERRUPTIBLE);
            /*  调度其它进程  */
            schedule();
        }

    如果此标识未被设置, 则通过exit_signals来设置

        /*
            tsk->flags |= PF_EXITING;
            http://lxr.free-electrons.com/source/kernel/signal.c#L2383
        */
        exit_signals(tsk);  /* sets tsk->flags PF_EXITING  设置PF_EXITING标志

    内存屏障


        /*
         * tsk->flags are checked in the futex code to protect against
         * an exiting task cleaning up the robust pi futexes.
         */
        /*  内存屏障,用于确保在它之后的操作开始执行之前,它之前的操作已经完成  */
        smp_mb();
        /*  一直等待,直到获得current->pi_lock自旋锁  */
        raw_spin_unlock_wait(&tsk->pi_lock);

    同步进程的mm的rss_stat

        /* sync mm's RSS info before statistics gathering */
        if (tsk->mm)
            sync_mm_rss(tsk->mm);

    获取current->mm->rss_stat.count[member]计数


    
        /*
            cct_update_integrals - update mm integral fields in task_struct
            更新进程的运行时间, 获取current->mm->rss_stat.count[member]计数 
            http://lxr.free-electrons.com/source/kernel/tsacct.c?v=4.6#L152
        */
        acct_update_integrals(tsk);

    函数的实现如下, 参见 http://lxr.free-electrons.com/source/kernel/tsacct.c?v=4.6#L156

    void acct_update_integrals(struct task_struct *tsk)
    {
        cputime_t utime, stime;
        unsigned long flags;
    
        local_irq_save(flags);
        task_cputime(tsk, &utime, &stime);
        __acct_update_integrals(tsk, utime, stime);
        local_irq_restore(flags);
    }

    其中task_cputime获取了进程的cpu时间
    __acct_update_integr定义如下

    参照http://lxr.free-electrons.com/source/kernel/tsacct.c#L125

    static void __acct_update_integrals(struct task_struct *tsk,
                        cputime_t utime, cputime_t stime)
    {
        cputime_t time, dtime;
        u64 delta;
    
        if (!likely(tsk->mm))
            return;
    
        time = stime + utime;
        dtime = time - tsk->acct_timexpd;
        /* Avoid division: cputime_t is often in nanoseconds already. */
        delta = cputime_to_nsecs(dtime);
    
        if (delta < TICK_NSEC)
            return;
    
        tsk->acct_timexpd = time;
        /*
         * Divide by 1024 to avoid overflow, and to avoid division.
         * The final unit reported to userspace is Mbyte-usecs,
         * the rest of the math is done in xacct_add_tsk.
         */
        tsk->acct_rss_mem1 += delta * get_mm_rss(tsk->mm) >> 10;
        tsk->acct_vm_mem1 += delta * tsk->mm->total_vm >> 10;
    }

    清除定时器


        group_dead = atomic_dec_and_test(&tsk->signal->live);
        if (group_dead) {
            hrtimer_cancel(&tsk->signal->real_timer);
            exit_itimers(tsk->signal);
            if (tsk->mm)
                setmax_mm_hiwater_rss(&tsk->signal->maxrss, tsk->mm);
        }

    收集进程会计信息


        acct_collect(code, group_dead);

    审计


        if (group_dead)
            tty_audit_exit();   //记录审计事件
        audit_free(tsk);    //  释放struct audit_context结构体

    释放进程占用的资源


    释放线性区描述符和页表

        /*  释放存储空间
        放弃进程占用的mm,如果没有其他进程使用该mm,则释放它。
         */
        exit_mm(tsk);

    输出进程会计信息

        if (group_dead)
            acct_process();
        trace_sched_process_exit(tsk);

    释放用户空间的“信号量”

    exit_sem(tsk);   /*  释放用户空间的“信号量”  */

    遍历current->sysvsem.undo_list链表,并清除进程所涉及的每个IPC信号量的操作痕迹

    释放锁

    exit_shm(tsk);  /* 释放锁  */

    释放文件对象相关资源

    exit_files(tsk); /*  释放已经打开的文件   */
    ````
    
    **释放struct fs_struct结构体**
    
    
    
    
    
    <div class="se-preview-section-delimiter"></div>
    
    ```c
    exit_fs(tsk);   /*  释放用于表示工作目录等结构  */

    脱离控制终端

        if (group_dead)
            disassociate_ctty(1);

    释放命名空间

    exit_task_namespaces(tsk);  /*  释放命名空间  */
    exit_task_work(tsk);

    释放task_struct中的thread_struct结构

        exit_thread();      /*     */

    触发thread_notify_head链表中所有通知链实例的处理函数,用于处理struct thread_info结构体

    Performance Event功能相关资源的释放

    perf_event_exit_task(tsk);

    Performance Event功能相关资源的释放

    cgroup_exit(tsk);

    注销断点

        /*
         * FIXME: do that only when needed, using sched_exit tracepoint
         */
        flush_ptrace_hw_breakpoint(tsk);

    更新所有子进程的父进程

        exit_notify(tsk, group_dead);

    进程事件连接器(通过它来报告进程fork、exec、exit以及进程用户ID与组ID的变化)

        proc_exit_connector(tsk);

    用于NUMA,当引用计数为0时,释放struct mempolicy结构体所占用的内存

    #ifdef CONFIG_NUMA
        task_lock(tsk);
        mpol_put(tsk->mempolicy);
        tsk->mempolicy = NULL;
        task_unlock(tsk);
    #endif

    释放struct futex_pi_state结构体所占用的内存

    #ifdef CONFIG_FUTEX
        if (unlikely(current->pi_state_cache))
            kfree(current->pi_state_cache);
    #endif

    释放struct io_context结构体所占用的内存

        if (tsk->io_context)
            exit_io_context(tsk);

    释放与进程描述符splice_pipe字段相关的资源

        if (tsk->splice_pipe)
            free_pipe_info(tsk->splice_pipe);
        if (tsk->task_frag.page)
            put_page(tsk->task_frag.page);

    检查有多少未使用的进程内核栈


        check_stack_usage();

    调度其它进程


        /* causes final put_task_struct in finish_task_switch(). */
        tsk->state = TASK_DEAD;
        tsk->flags |= PF_NOFREEZE;      /* tell freezer to ignore us */
        /*
            重新调度,因为该进程已经被设置成了僵死状态,因此永远都不会再把它调度回来运行了,也就实现了do_exit不会有返回的目标    */
        schedule();

    在设置了进程状态为TASK_DEAD后, 进程进入僵死状态, 进程已经无法被再次调度, 因为对应用程序或者用户空间来说此进程已经死了, 但是尽管进程已经不能再被调度,但系统还是保留了它的进程描述符,这样做是为了让系统有办法在进程终止后仍能获得它的信息。

    在父进程获得已终止子进程的信息后,子进程的task_struct结构体才被释放(包括此进程的内核栈)。

    展开全文
  • linux的退出函数exit( )、_exit( )

    千次阅读 2018-07-30 14:44:00
    1、void exit(int status); 该函数的功能是,使调用本函数的进程正常终止,然后把形参的值status&amp;0377(八进制)返回给父进程,父进程可以通过wait“函数族”来获取这个返回值。 所有通过atexit( )和 on_...
  • Linuxexit(0)&exit(1)&return

    千次阅读 2018-08-20 18:41:26
    exit()与return的区别: exit(0):正常运行程序并退出程序,调用时程序运行正常结束; exit(1):非正常运行导致退出程序,调用时程序运行非正常结束; return():返回函数,若在主函数中,则会退出函数...
  • 转载自 http://blog.csdn.net/chilumanxi/article/details/47050969void exit(int staus) 函数, 在头文件 #include &lt;stdlib.h&gt;void _exit(int status...中在Linux &amp; C中描述两者区别是:_exi...
  • exit()就是退出,传入的参数是程序退出时的状态码,0表示正常退出,其他表示非正常退出,一般都用-1或者1,标准C里有EXIT_SUCCESS和EXIT_FAILURE两个宏,用exit(EXIT_SUCCESS);可读性比较好一点。1、异同: 作为系统...
  • LinuxExit和_exit函数说明

    千次阅读 2018-12-05 10:18:05
    exit和_exit函数都是用来终止进程的。当程序执行到exit或_exit时,系统无条件的停止剩下所有操作,清除包括PCB(进程控制块)在内的各种数据结构,并终止本进程的运行。但是,这两个函数是有区别的。 exit()函数的...
  • linuxexit()和_exit()区别

    千次阅读 2016-05-08 09:17:09
    linuxexit()和_exit()的用法及区别: exit和_exit作为系统调用而言,_exitexit是一对孪生兄弟。通常我们会认为,他们之间没有什么区别:但是没有区别会有两个函数,你要知道程序猿虽然苦但不笨,这种事儿发生的...
  • Linux内核之do_exit

    千次阅读 2016-04-07 20:25:42
    do_exit(long code)(1) __exit_mm(tsk): //释放存储空间(2) sem_exit(); //释放用户空间的“信号量”(3) __exit_files(tsk); //释放已经打开的文件(4) __exit_fs(tsk); //释放用于表示工作目录等结构(5) exit_sigh...
  • Linux exit() 和 return 的区别

    千次阅读 2017-10-15 22:46:05
    exit()表示终止当前进程,return表示从当前函数返回。 exit()带参数表示终止状态,通常exit(0)表示正常终止, return 带一个参数表示返回值。 exit()执行完一些清理工作(终止处理程序,刷新输出流并关闭所有打开的流...
  • Linux编程return与exilt区别 exit 是用来结束一个程序的执行的,而return只是用来从一个函数中返回。 return  return 表示从被调函数返回到主调函数继续执行,返回时可附带一个返回值,由return后面的参数指定,...
  •  linux中进程退出函数:exit()和_exit()的区别 (1)_exit()执行后立即返回给内核,而exit()要先执行一些清除操作,然后将控制权交给内核。 (2)调用_exit函数时,其会关闭进程所有的文件描述符,清理内存以及...
  • linux 退出码 exit

    2014-11-19 18:59:34
    1.exit命令用来结束脚本   2.每个命令结束时,都会返回一个exit状态,通常成功返回0,失败返回非0(1-255错误码)   3.$? 来读取shell的退出码 注意:$?只能用一次,第二次再用$?时结果就是上次$?的返回值,...
  • 图解Linux命令之--exit命令

    千次阅读 2016-09-24 17:48:12
    exit命令--> 该命令用来退出当前的shell或退出终端 备注: 1) 退出码(exit status 或 exit code)的约定: 2) 检查上一个命令或脚本的退出码示例: 3) 在脚本中,判断参数数量;不匹配就打印使用方式,退出; ...
  • linuxexit()和 _exit()说明

    千次阅读 2012-04-10 09:06:49
    ...当程序执行到exit或_exit时,系统无条件的停止剩下所有操作,清除包括PCB在内的各种数据结构,并终止本进程的运行。但是,这两个函数是有区别的。 exit()函数的作用是:直接使用进程停止运
  • Linux-shell exit code

    千次阅读 2016-10-14 09:28:37
    source comes from:http://tldp.org/LDP/abs/html/exitcodes.html Table E-1. Reserved Exit Codes ...Exit Code Number Meaning Example Comments 1 Catchall for general errors let
  • linux c中return 与exit的区别

    千次阅读 2012-10-09 15:27:06
    return 是c语言中的关键字,而exitlinux中的系统调用。 return是表示函数的返回,而exit是进程的终止。 如果return 或者exit出现在main函数中,两者的作用是一样。 如果return出现在子程序中表示返回,而...
1 2 3 4 5 ... 20
收藏数 383,989
精华内容 153,595
关键字:

5 exit linux