Linux kernel Source-code Scenario analysis-exit ()

Source: Internet
Author: User

After the/bin/echo is executed, the do_exit is mobilized and the child process is destroyed:

Let's start with the system call exit () and first look at the implementation of exit () and go to the kernel-state execution sys_exit.

Asmlinkage long sys_exit (int error_code) {do_exit ((error_code&0xff) <<8);}
Noret_type void Do_exit (long code) {struct Task_struct *tsk = current;if (In_interrupt ())//Interrupt service cannot interrupt panic ("Aiee, killing Interrupt handler! "); if (!tsk->pid)//idling idle process is not allowed to exit the panic ("attempted to kill the Idle task!"); if (tsk->pid = = 1) The//init process is not allowed to exit the panic ("attempted to kill init!"); Tsk->flags |= Pf_exiting;del_timer_sync (&tsk->real_timer); Fake_volatile: #ifdef CONFIG_BSD_PROCESS_ Acctacct_process (code); #endif__exit_mm (tsk);//if shared through pointers, only reduces the share count mm->mm_users. If you are on your own, release mm_struct,vm_struct; Release page catalog table, page table Lock_kernel (); Sem_exit ();//signal correlation, read the interprocess communication __exit_files (TSK);//if shared via pointers, Just reduce the share count Files->count. If you are on your own, then release the FILES_STRUCT data Structure __EXIT_FS (TSK);//if shared through pointers, just reduce the share count Fs->count. If you are on your own, then release the FS_STRUCT data Structure Exit_sighand (TSK);//if shared through pointers, just reduce the share count Sig->count. If you're on your own, release the SIGNAL_STRUCT data Structure exit_thread ();//null function if (current->leader) Disassociate_ctty (1);p Ut_exec_domain ( Tsk->exec_domain); if (tsk->binfmt && tsk->binfmt->module) __mod_dec_use_count (tsk->binfmt- >module); tsk->exit_code = Code;exit_notify ();//sets the current process to a zombie state, sends a signal to the parent process, and sets the parent process of the child process of its current process to the INIT process schedule (); BUG ();/* * In order to get rid of the "volatile function does return" message * I do this little loop that confuses GCC t O Think Do_exit really * is volatile. In fact it's schedule () that's volatile in some * circumstances:when current->state = ZOMBIE, schedule () never * Retu RNs. * In fact the natural-a-do-all-to-have-is-to-have-the label and the * goto right through each of the other, but I put the Fake_ Volatile label at * The start of the function just in case Something/really/bad * happens, and the schedule returns. This is the we can try again. I ' m * not paranoid:it ' s just this everybody is out to get me. */goto Fake_volatile;}


__EXIT_MM, child process on its own, release mm_struct,vm_struct; free page Catalog table, page table:

static inline void __exit_mm (struct task_struct * tsk) {struct mm_struct * mm = Tsk->mm;mm_release (); if (mm) {Atomic_inc (&mm->mm_count); if (mm! = tsk->active_mm) BUG ();/* More a memory barrier than a real lock */task_lock (TSK); tsk-& gt;mm = Null;task_unlock (tsk), enter_lazy_tlb (mm, Current, smp_processor_id ()), mmput (mm);//mainly this sentence}}
void mmput ( struct Mm_struct *mm) {if (Atomic_dec_and_lock (&mm->mm_users, &mmlist_lock)) {//mm->mm_users to 1list_del (&mm->mmlist); Spin_unlock (&mmlist_lock); Exit_mmap (mm);//Release vm_struct, and the page table entries and page entries are clear 0mmdrop (mm);//release mm _struct and Page Catalog table, page table}} 
void Exit_mmap (struct mm_struct * mm) {struct vm_area_struct * mpnt;release_segments (mm); Spin_lock (&mm->page_ Table_lock); mpnt = Mm->mmap;mm->mmap = Mm->mmap_avl = Mm->mmap_cache = Null;spin_unlock (&mm->page_ Table_lock); Mm->rss = 0;MM-&GT;TOTAL_VM = 0;MM-&GT;LOCKED_VM = 0;while (mpnt) {struct Vm_area_struct * next = mpnt-> vm_next;unsigned Long start = mpnt->vm_start;unsigned long end = mpnt->vm_end;unsigned long size = end-start;if (M Pnt->vm_ops) {if (mpnt->vm_ops->close) mpnt->vm_ops->close (MPNT);} Mm->map_count--;remove_shared_vm_struct (MPNT); Flush_cache_range (mm, start, end) Zap_page_range (mm, start, size) , if (mpnt->vm_file) fput (mpnt->vm_file); Kmem_cache_free (Vm_area_cachep, mpnt); mpnt = Next;} /* This is just debugging */if (Mm->map_count) printk ("Exit_mmap:map count is%d\n", mm->map_count); clear_page_tabl Es (mm, FIRST_USER_PGD_NR, USER_PTRS_PER_PGD);}
static inline void Mmdrop (struct mm_struct * mm) {if (Atomic_dec_and_test (&mm->mm_count)) __mmdrop (mm);}
inline void __mmdrop (struct mm_struct *mm) {if (mm = = &init_mm) BUG ();p gd_free (MM->PGD);d estroy_context (mm); _MM (mm);}

Back to Do_exit, continue to execute __exit_files, the child process on its own, release files_struct data structure, the code is as follows:

static inline void __exit_files (struct task_struct *tsk) {struct files_struct * files = tsk->files;if (files) {Task_lock (tsk); tsk->files = Null;task_unlock (tsk);p ut_files_struct (files);}}
void Put_files_struct (struct files_struct *files) {if (Atomic_dec_and_test (&files->count)) {//files-> Count is 1close_files (files); */* Free the FD and fdset arrays if we expanded them. */if (FILES->FD! = &files->fd_array[0]) free_fd_array (FILES->FD, Files->max_fds); if (files->max_ Fdset > __fd_setsize) {free_fdset (Files->open_fds, Files->max_fdset); Free_fdset (Files->close_on_exec, Files->max_fdset);} Kmem_cache_free (Files_cachep, files);}}


Continue to execute the __EXIT_FS, the child process on its own, that will release the FS_STRUCT data structure, the code is as follows:

static inline void __exit_fs (struct task_struct *tsk) {struct fs_struct * fs = Tsk->fs;if (FS) {Task_lock (tsk); tsk->f s = Null;task_unlock (tsk); __put_fs_struct (fs);}}
static inline void __put_fs_struct (struct fs_struct *fs) {/* No need to hold fs->lock if we are killing it */if (atomic_ Dec_and_test (&fs->count)) {//fs->count is 1dput (fs->root), Mntput (FS->ROOTMNT);dp ut (fs->pwd); Mntput (FS->PWDMNT); if (fs->altroot) {dput (fs->altroot); Mntput (FS->ALTROOTMNT);} Kmem_cache_free (Fs_cachep, FS);}}


Continue to execute the Exit_sighand, the child process on its own, that will release the SIGNAL_STRUCT data structure, the code is as follows:

void Exit_sighand (struct task_struct *tsk) {struct Signal_struct * sig = Tsk->sig;spin_lock_irq (&tsk->sigmask _lock), if (sig) {Tsk->sig = Null;if (Atomic_dec_and_test (&sig->count))//sig->count is 1kmem_cache_free ( Sigact_cachep, SIG);} tsk->sigpending = 0;flush_sigqueue (&tsk->pending); Spin_unlock_irq (&tsk->sigmask_lock);}


Continue execution of Exit_notify, the current process is set to a zombie state, and the parent process is signaled; its parent process of the child process of the current process is set to the INIT process, the code is as follows:

static void Exit_notify (void) {struct task_struct * p, *t;forget_original_parent (current);//The parent process of the child process of the currently process is set to the init process/* *  Check to see if any process groups has become orphaned * as a result of our exiting, and if they has any stopped * jobs,  Send them a SIGHUP and then a sigcont. (POSIX 3.2.2.2) * Case I:our father was in a different pgrp than we am * and we were the only connection outside, so OU R PGRP * is on to become orphaned. */t = current->p_pptr;//notification is p_pptr, the forget_original_parent setting is P->p_opptr = Reaperif ((t->pgrp! = current- &GT;PGRP) && (t->session = = current->session) && will_become_orphaned_pgrp (CURRENT-&GT;PGRP, C urrent) && has_stopped_jobs (current->pgrp)) {KILL_PG (current->pgrp,sighup,1); Kill_pg (current-> pgrp,sigcont,1);} /* Let father know we died * Thread signals is configurable, but you aren ' t going to use * this to send signals to ARB  Itary processes. * That's stops right now. * * If the parent exec ID DOESN ' t match the exec ID we saved * When we started then we know the parent have changed security * domain. * IF our Self_exec ID doesn ' t match our parent_exec_id and we have changed execution domain as these the values start Ed * the same after a fork.      * */IF (current->exit_signal! = SIGCHLD && (current->parent_exec_id! = t->self_exec_id | | current->self_exec_id! = current->parent_exec_id) &&!capable (cap_kill)) current->exit_signal =  The signal that sigchld;//sends to the parent process is sigchld/* * This loop does the things: * * A.  Make init inherit all the processes * B. Check to see if any process groups has become orphaned *as a result of our exiting, and if they has any stopped *jobs, s  End them a SIGHUP and then a sigcont. (POSIX 3.2.2.2) */WRITE_LOCK_IRQ (&tasklist_lock); current->state = task_zombie;//The current process is set to zombie state do_notify_parent (Current, current->exit_signal);//signal to Parent process while (current->p_cptr! = NULL) {p = Current->p_cptr;current->p_ Cptr = p->p_osptr;p->p_ysptr = Null;p->ptrace = 0;p->p_pptr = p->p_opptr;//here, the p_pptr and the p_opptr are unified, All are reaperp->p_osptr = P->p_pptr->p_cptr;if (p->p_osptr) p->p_osptr->p_ysptr = P;p->p_pptr->p_ Cptr = p;if (p->state = = Task_zombie) do_notify_parent (P, p->exit_signal);/* * Process Group Orphan check * Case II: We are a different pgrp * than we were, and it was the only connection * outside, so the child pgrp D. */if ((p->pgrp! = current->pgrp) && (p->session = = current->session)) {int pgrp = P->pgrp;wri TE_UNLOCK_IRQ (&tasklist_lock), if (Is_orphaned_pgrp (PGRP) && has_stopped_jobs (pgrp)) {Kill_pg (PGRP, sighup,1); Kill_pg (pgrp,sigcont,1);} WRITE_LOCK_IRQ (&tasklist_lock);}} WRITE_UNLOCK_IRQ (&tasklist_lock);}
static inline void forget_original_parent (struct task_struct * father) {struct task_struct * p, *reaper;read_lock (&tasklist _lock);/* Next in our thread group */reaper = Next_thread (father); if (Reaper = = father) Reaper = Child_reaper;//init process For_e Ach_task (P) {if (p->p_opptr = = father) {/* We dont want people slaying init */p->exit_signal = sigchld;p->self_ex ec_id++;p->p_opptr = reaper;//The parent process of the child process of its current process is set to the Init process, where P_opptrif (p->pdeath_signal) Send_sig is set (P->pdeath _signal, p, 0);}} Read_unlock (&tasklist_lock);} 
void Do_notify_parent (struct task_struct *tsk, int sig)//sig for Sigchld{struct siginfo info;int why, Status;info.si_signo = Sig;info.si_errno = 0;info.si_pid = Tsk->pid;info.si_uid = tsk->uid;/* Fixme:find out whether or not it is Suppos Ed to is c*time. */info.si_utime = Tsk->times.tms_utime;info.si_stime = Tsk->times.tms_stime;status = Tsk->exit_code &  0x7f;why = si_kernel;/* shouldn ' t happen */switch (tsk->state) {case task_stopped:/* Fixme--Can we deduce CLD_TRAPPED or cld_continued? */if (Tsk->ptrace & pt_ptraced) why = cld_trapped;elsewhy = Cld_stopped;break;default:if (Tsk->exit_code & 0x80) Why = Cld_dumped;else if (Tsk->exit_code & 0x7f) what = cld_killed;else {why = Cld_exited;status = Tsk->exit_ Code >> 8;} break;} Info.si_code = Why;info.si_status = Status;send_sig_info (sig, &info, tsk->p_pptr);//Send SIGCHLD signal to parent process Wake_up_ Parent (tsk->p_pptr);//wake-up Father process, parent process when wait, set state to Task_interruptible, now set to Task_running}
At this point, the basic resources of the process have been freed, but the wreckage of the current process still occupies a minimal amount of resources, including its task_struct data structure and the two pages of the system space stack. The current process itself does not release these two pages, just as people themselves do not write their own account at the end of the same, but rather inform their parent process, let the parent process to cook funeral. The current process state isTask_zombieSchedule, the process is dispatched indefinitely when it is delayed.


Below, the last execution of schedule, assuming that only the parent and child processes, the state of the parent process is already task_running, switches to the parent process to continue execution.

#define SWITCH_TO (prev,next,last) do {asm volatile ("PUSHL%%esi\n\t" \////to deposit ESI into the current process prev stack "PUSHL%%edi\n\t" \//EDI deposit to current In the process prev stack "PUSHL%%ebp\n\t" \//Put EBP into the current process prev stack "MOVL%%esp,%0\n\t"/* Save ESP */\//Now process prev ESP saved in Prev->thr Ead.esp "MOVL%3,%%esp\n\t"/* restore ESP */\//The process to be toggled Next->thread.esp saved in ESP, the stack has switched "MOVL $1f,%1\n\t"/* sav e EIP */\//Now process prev EIP (i.e. "1:\t" address) saved in Prev->thread.eip "PUSHL%4\n\t"/* Restore EIP */\//The process to be switched next->thread. The EIP is stored in the EIP "jmp __switch_to\n" \/And does not say what is done in __switch_to, when the CPU executes the RET instruction there, because it is turned over through the JMP instructions, and finally into the stack next-> Thread.eip becomes the return address "1:\t" \//If the switch is not a subprocess, Next->thread.eip is actually the last time it was saved in Prev->thread.eip, which is the line statement "POPL%%ebp\n\t "\//Because the stack has switched over, pop out is the contents of the above deposit process prev Stack" popl%%edi\n\t "" Popl%%esi\n\t ":" =m "(PREV-&GT;THREAD.ESP)," =m "(pre       V-&GT;THREAD.EIP), "=b" (last): "M" (Next->thread.esp), "M" (NEXT-&GT;THREAD.EIP), "a" (prev), "D" (next), "B" (prev)); while (0)
Parent process waits in SYS_WAIT4, parent process continues from "1:\t", continues executionSYS_WAIT4 function。

Linux kernel Source-code Scenario analysis-exit ()

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.