Linux进程管理之进程的终止

    内核源码:linux-2.6.38.8.tar.bz2

    目标平台:ARM体系结构

 

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

$ man 3 exit
       void exit(int status);
$ man 2 exit_group
       void exit_group(int status);

$ man 3 pthread_exit
       void pthread_exit(void *retval);
$ man 2 _exit
       void _exit(int status);

    库函数exit使用系统调用exit_group来终止整个线程组,库函数pthread_exit使用系统调用_exit来终止某一个线程。

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

/* linux-2.6.38.8/kernel/exit.c */
SYSCALL_DEFINE1(exit, int, error_code)
{
	do_exit((error_code&0xff)<<8);
}

SYSCALL_DEFINE1(exit_group, int, error_code)
{
	do_group_exit((error_code & 0xff) << 8);
	/* NOTREACHED */
	return 0;
}

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

NORET_TYPE void
do_group_exit(int exit_code)
{
	struct signal_struct *sig = current->signal;

	BUG_ON(exit_code & 0x80); /* core dumps don't get here */

	if (signal_group_exit(sig)) //检查current->sig->flags的SIGNAL_GROUP_EXIT标志是否置位,或者current->sig->group_exit_task是否不为NULL。
		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函数来处理。 

/* linux-2.6.38.8/kernel/exit.c */
NORET_TYPE void do_exit(long code)

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

	profile_task_exit(tsk);

/* linux-2.6.38.8/drivers/oprofile/buffer_sync.c */
static struct notifier_block task_exit_nb = {
	.notifier_call	= task_exit_notify,
};

    2、检查current->fs_excl是否为0,不为0时也不会终止后续代码的执行 

	WARN_ON(atomic_read(&tsk->fs_excl));

/* linux-2.6.38.8/include/asm-generic/bug.h */
#ifndef WARN_ON
#define WARN_ON(condition) ({						\
	int __ret_warn_on = !!(condition);				\
	if (unlikely(__ret_warn_on))					\
		__WARN(); /* 输出警告信息的位置(哪个文件的哪行)*/	\
	unlikely(__ret_warn_on);					\
})
#endif

    3、oops消息 

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

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

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

	set_fs(USER_DS);

/* linux-2.6.38.8/arch/arm/include/asm/uaccess.h */
#define USER_DS		TASK_SIZE
#define TASK_SIZE		(UL(CONFIG_PAGE_OFFSET) - UL(0x01000000))

static inline void set_fs(mm_segment_t fs)
{
	current_thread_info()->addr_limit = fs;
	modify_domain(DOMAIN_KERNEL, fs ? DOMAIN_CLIENT : DOMAIN_MANAGER);
}

    5、current->flags的PF_EXITING标志表示进程正在被删除。 

	if (unlikely(tsk->flags & PF_EXITING)) {//检查PF_EXITING标志是否未被设置,如果设置了则执行大括号里的代码
		printk(KERN_ALERT
			"Fixing recursive fault but reboot is needed!\n");

		tsk->flags |= PF_EXITPIDONE;
		set_current_state(TASK_UNINTERRUPTIBLE); //设置进程状态为不可中断的等待状态
		schedule(); //调度其它进程
	}

    6、设置current->irqaction->flags的IRQTF_DIED标志,表示清除当前进程的中断服务例程 

	exit_irq_thread();

/* linux-2.6.38.8/kernel/irq/mamage.c */
void exit_irq_thread(void)
{
	struct task_struct *tsk = current;

	if (!tsk->irqaction)
		return;

	printk(KERN_ERR
	       "exiting task \"%s\" (%d) is an active IRQ thread (irq %d)\n",
	       tsk->comm ? tsk->comm : "", tsk->pid, tsk->irqaction->irq);

	/*
	 * Set the THREAD DIED flag to prevent further wakeups of the
	 * soon to be gone threaded handler.
	 */
	set_bit(IRQTF_DIED, &tsk->irqaction->flags);
}

    7、设置PF_EXITING标志 

	exit_signals(tsk);  /* sets PF_EXITING */

/* linux-2.6.38.8/kernel/signal.c */
void exit_signals(struct task_struct *tsk)
{
	int group_stop = 0;
	struct task_struct *t;

	if (thread_group_empty(tsk) || signal_group_exit(tsk->signal)) { //检查线程组链表是否为空,或者是否要终止整个线程组
		tsk->flags |= PF_EXITING;
		return;
	}

	spin_lock_irq(&tsk->sighand->siglock);
	/*
	 * From now this task is not visible for group-wide signals,
	 * see wants_signal(), do_signal_stop().
	 */
	tsk->flags |= PF_EXITING;
	if (!signal_pending(tsk)) //signal_pending函数用于检查当前进程是否有非阻塞的挂起信号,如果有则返回1,否则返回0
		goto out;

	/* It could be that __group_complete_signal() choose us to
	 * notify about group-wide signal. Another thread should be
	 * woken now to take the signal since we will not.
	 */
	for (t = tsk; (t = next_thread(t)) != tsk; ) //检查线程组中的其他进程
		if (!signal_pending(t) && !(t->flags & PF_EXITING)) //如果没有设置TIF_SIGPENDING标志,而且也没有设置PF_EXITING标志
			recalc_sigpending_and_wake(t); //则设置TIF_SIGPENDING标志,表示有挂起信号

	if (unlikely(tsk->signal->group_stop_count) &&
			!--tsk->signal->group_stop_count) { //表示只终止线程组中的某个线程
		tsk->signal->flags = SIGNAL_STOP_STOPPED;
		group_stop = tracehook_notify_jctl(CLD_STOPPED, CLD_STOPPED);
	}
out:
	spin_unlock_irq(&tsk->sighand->siglock);

	if (unlikely(group_stop)) {
		read_lock(&tasklist_lock);
		do_notify_parent_cldstop(tsk, group_stop);
		read_unlock(&tasklist_lock);
	}
}

    8)、内存屏障,用于确保在它之后的操作开始执行之前,它之前的操作已经完成 

	smp_mb();
	raw_spin_unlock_wait(&tsk->pi_lock); //一直等待,直到获得current->pi_lock自旋锁
/* linux-2.6.38.8/arch/arm/include/asm/system.h */
#define smp_mb()	barrier()  //!CONFIG_SMP
/* linux-2.6.38.8/include/linux/compiler-gcc.h */
#define barrier() __asm__ __volatile__("": : :"memory")

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

	acct_update_integrals(tsk);

void acct_update_integrals(struct task_struct *tsk)
{
	if (likely(tsk->mm)) {
		cputime_t time, dtime;
		struct timeval value;
		unsigned long flags;
		u64 delta;

		local_irq_save(flags);
		time = tsk->stime + tsk->utime;
		dtime = cputime_sub(time, tsk->acct_timexpd);
		jiffies_to_timeval(cputime_to_jiffies(dtime), &value);
		delta = value.tv_sec;
		delta = delta * USEC_PER_SEC + value.tv_usec;

		if (delta == 0)
			goto out;
		tsk->acct_timexpd = time;
		tsk->acct_rss_mem1 += delta * get_mm_rss(tsk->mm); //统计分配给进程的页框数(MM_FILEPAGES和MM_ANONPAGES两种类型的页框)
		tsk->acct_vm_mem1 += delta * tsk->mm->total_vm; //total_vm用来表示进程地址空间的大小(页数)
	out:
		local_irq_restore(flags);
	}
}

/* linux-2.6.38.8/include/linux/mm.h */
static inline unsigned long get_mm_rss(struct mm_struct *mm)
{
	return get_mm_counter(mm, MM_FILEPAGES) +
		get_mm_counter(mm, MM_ANONPAGES);
}
static inline unsigned long get_mm_counter(struct mm_struct *mm, int member) //!USE_SPLIT_PTLOCKS
{
	return mm->rss_stat.count[member];
}

    然后,把它们清零。 

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

/* linux-2.6.38.8/mm/memory.c */
void sync_mm_rss(struct task_struct *task, struct mm_struct *mm)
{
	__sync_task_rss_stat(task, mm);
}
static void __sync_task_rss_stat(struct task_struct *task, struct mm_struct *mm)
{
	int i;

	for (i = 0; i < NR_MM_COUNTERS; i++) {  //共有三类,MM_FILEPAGES、MM_ANONPAGES和MM_SWAPENTS
		if (task->rss_stat.count[i]) {
			add_mm_counter(mm, i, task->rss_stat.count[i]);
			task->rss_stat.count[i] = 0;
		}
	}
	task->rss_stat.events = 0;
}

    10)、清除定时器 

	group_dead = atomic_dec_and_test(&tsk->signal->live); //live用来表示线程组中活动进程的数量
	if (group_dead) { //当没有活动的进程时
		hrtimer_cancel(&tsk->signal->real_timer); //取消高精度定时器
		exit_itimers(tsk->signal); //删除POSIX.1b类型的定时器
		if (tsk->mm)
			setmax_mm_hiwater_rss(&tsk->signal->maxrss, tsk->mm); //获取进程所拥有的最大页框数
	}

    11)、收集进程会计信息 

	acct_collect(code, group_dead);

    12)、审计 

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

    13)、输出taskstats信息 

	tsk->exit_code = code; //设置终止代码
	taskstats_exit(tsk, group_dead);

    14)、释放线性区描述符和页表 

	exit_mm(tsk);

/* linux-2.6.38.8/kernel/exit.c */
static void exit_mm(struct task_struct * tsk)
{
	struct mm_struct *mm = tsk->mm;
	struct core_state *core_state;

	mm_release(tsk, mm);   //其中会唤醒tsk->vfork_done,让父进程开始执行,用于vfork时
	if (!mm)
		return;
	/*
	 * Serialize with any possible pending coredump.
	 * We must hold mmap_sem around checking core_state
	 * and clearing tsk->mm.  The core-inducing thread
	 * will increment ->nr_threads for each thread in the
	 * group with ->mm != NULL.
	 */
	down_read(&mm->mmap_sem);
	core_state = mm->core_state;
	if (core_state) {  //内存转储
		struct core_thread self;
		up_read(&mm->mmap_sem);

		self.task = tsk;
		self.next = xchg(&core_state->dumper.next, &self);
		/*
		 * Implies mb(), the result of xchg() must be visible
		 * to core_state->dumper.
		 */
		if (atomic_dec_and_test(&core_state->nr_threads))
			complete(&core_state->startup);

		for (;;) {
			set_task_state(tsk, TASK_UNINTERRUPTIBLE);
			if (!self.task) /* see coredump_finish() */
				break;
			schedule();
		}
		__set_task_state(tsk, TASK_RUNNING);
		down_read(&mm->mmap_sem);
	}
	atomic_inc(&mm->mm_count); //递增mm->mm_count计数,确保内存描述符暂时不会被删除,当要把正在被终止的进程从本地CPU撤销时,才由finish_task_switch函数来释放内存描述。
	BUG_ON(mm != tsk->active_mm);
	/* more a memory barrier than a real lock */
	task_lock(tsk);
	tsk->mm = NULL; //设置进程描述符的mm字段为NULL。 
	up_read(&mm->mmap_sem);
	enter_lazy_tlb(mm, current); //使处理器处于懒惰TLB模式,ARM体系结构不支持。
	/* We don't want this task to be frozen prematurely */
	clear_freeze_flag(tsk);  //设置TIF_FREEZE标志。
	if (tsk->signal->oom_score_adj == OOM_SCORE_ADJ_MIN)
		atomic_dec(&mm->oom_disable_count);
	task_unlock(tsk);
	mm_update_next_owner(mm);
	mmput(mm);  //当mm->mm_users为0(即没有任何进程使用它)时,释放线性区描述符和页表,但这时还不会释放内存描述符
}

    15)、输出进程会计信息 

	if (group_dead)
		acct_process();
	trace_sched_process_exit(tsk); //用于跟踪,定义在linux-2.6.38.8/include/trace/events/sched.h文件中

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

	exit_sem(tsk);

    17)、释放文件对象相关资源 

	exit_files(tsk);

/* linux-2.6.38.8/kernel/exit.c */
void exit_files(struct task_struct *tsk)
{
	struct files_struct * files = tsk->files;

	if (files) {
		task_lock(tsk);
		tsk->files = NULL; //把进程描述符的files字段设为NULL。
		task_unlock(tsk);
		put_files_struct(files);
	}
}

void put_files_struct(struct files_struct *files)
{
	struct fdtable *fdt;

	if (atomic_dec_and_test(&files->count)) { //当共享该表的进程数目为0时
		close_files(files); //执行进程终止时应该执行的文件操作相关函数,如release
		/*
		 * Free the fd and fdset arrays if we expanded them.
		 * If the fdtable was embedded, pass files for freeing
		 * at the end of the RCU grace period. Otherwise,
		 * you can free files immediately.
		 */
		rcu_read_lock();
		fdt = files_fdtable(files);
		if (fdt != &files->fdtab)
			kmem_cache_free(files_cachep, files); //释放struct files_struct结构体所用内存
		free_fdtable(fdt);
		rcu_read_unlock();
	}
}

    18)、释放struct fs_struct结构体 

	exit_fs(tsk);

/* linux-2.6.38.8/fs/fs_struct.c */
void exit_fs(struct task_struct *tsk)
{
	struct fs_struct *fs = tsk->fs;

	if (fs) {
		int kill;
		task_lock(tsk);
		spin_lock(&fs->lock);
		write_seqcount_begin(&fs->seq);
		tsk->fs = NULL; //设置进程描述符的fs字段为NULL
		kill = !--fs->users;  //fs->users表示共享这个表的进程个数
		write_seqcount_end(&fs->seq);
		spin_unlock(&fs->lock);
		task_unlock(tsk);
		if (kill) //当为0时
			free_fs_struct(fs); //释放结构体所用内存
	}
}

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

	check_stack_usage();
/* linux-2.6.38.8/kernel/exit.c */
#ifdef CONFIG_DEBUG_STACK_USAGE
static void check_stack_usage(void)
{
	static DEFINE_SPINLOCK(low_water_lock);
	static int lowest_to_date = THREAD_SIZE;
	unsigned long free;

	free = stack_not_used(current);

	if (free >= lowest_to_date)
		return;

	spin_lock(&low_water_lock);
	if (free < lowest_to_date) {
		printk(KERN_WARNING "%s used greatest stack depth: %lu bytes "
				"left\n",
				current->comm, free);
		lowest_to_date = free;
	}
	spin_unlock(&low_water_lock);
}
#else
static inline void check_stack_usage(void) {}
#endif

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

	exit_thread();

/* linux-2.6.38.8/arch/arm/kernel/process.c */
void exit_thread(void)
{
	thread_notify(THREAD_NOTIFY_EXIT, current_thread_info());
}

    21)、Performance Event功能相关资源的释放 

	perf_event_exit_task(tsk);

/* linux-2.6.38.8/kernel/perf_event.c */
void perf_event_exit_task(struct task_struct *child)
{
	struct perf_event *event, *tmp;
	int ctxn;

	mutex_lock(&child->perf_event_mutex);
	list_for_each_entry_safe(event, tmp, &child->perf_event_list,
				 owner_entry) {
		list_del_init(&event->owner_entry);

		/*
		 * Ensure the list deletion is visible before we clear
		 * the owner, closes a race against perf_release() where
		 * we need to serialize on the owner->perf_event_mutex.
		 */
		smp_wmb();
		event->owner = NULL;
	}
	mutex_unlock(&child->perf_event_mutex);

	for_each_task_context_nr(ctxn)
		perf_event_exit_task_context(child, ctxn);
}

    22)、释放Control Groups相关的资源 

	cgroup_exit(tsk, 1);

/* linux-2.6.38.8/kernel/cgroup.c */
void cgroup_exit(struct task_struct *tsk, int run_callbacks)
{
	int i;
	struct css_set *cg;

	if (run_callbacks && need_forkexit_callback) {
		/*
		 * modular subsystems can't use callbacks, so no need to lock
		 * the subsys array
		 */
		for (i = 0; i < CGROUP_BUILTIN_SUBSYS_COUNT; i++) {
			struct cgroup_subsys *ss = subsys[i];
			if (ss->exit)
				ss->exit(ss, tsk);
		}
	}

	/*
	 * Unlink from the css_set task list if necessary.
	 * Optimistically check cg_list before taking
	 * css_set_lock
	 */
	if (!list_empty(&tsk->cg_list)) {
		write_lock(&css_set_lock);
		if (!list_empty(&tsk->cg_list))
			list_del_init(&tsk->cg_list);
		write_unlock(&css_set_lock);
	}

	/* Reassign the task to the init_css_set. */
	task_lock(tsk);
	cg = tsk->cgroups;
	tsk->cgroups = &init_css_set;
	task_unlock(tsk);
	if (cg)
		put_css_set_taskexit(cg);
}

    23)、脱离控制终端 

	if (group_dead)
		disassociate_ctty(1);

    24)、执行域 

	module_put(task_thread_info(tsk)->exec_domain->module);

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

	proc_exit_connector(tsk);

/* linux-2.6.38.8/drivers/connector/cn_proc.c */
void proc_exit_connector(struct task_struct *task)
{
	struct cn_msg *msg;
	struct proc_event *ev;
	__u8 buffer[CN_PROC_MSG_SIZE];
	struct timespec ts;

	if (atomic_read(&proc_event_num_listeners) < 1)
		return;

	msg = (struct cn_msg*)buffer;
	ev = (struct proc_event*)msg->data;
	get_seq(&msg->seq, &ev->cpu);
	ktime_get_ts(&ts); /* get high res monotonic timestamp */
	put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
	ev->what = PROC_EVENT_EXIT;
	ev->event_data.exit.process_pid = task->pid;
	ev->event_data.exit.process_tgid = task->tgid;
	ev->event_data.exit.exit_code = task->exit_code;
	ev->event_data.exit.exit_signal = task->exit_signal;

	memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
	msg->ack = 0; /* not used */
	msg->len = sizeof(*ev);
	cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
}

    参考文档:linux-2.6.38.8/Documentation/connector/connector.txt

    http://www.ibm.com/developerworks/cn/linux/l-connector/

    26)、注销断点 

	ptrace_put_breakpoints(tsk);

/* linux-2.6.38.8/kernel/ptrace.c */
void ptrace_put_breakpoints(struct task_struct *tsk)
{
	if (atomic_dec_and_test(&tsk->ptrace_bp_refcnt))
		flush_ptrace_hw_breakpoint(tsk);
}
/* linux-2.6.38.8/arch/arm/kernel/ptrace.c */
void flush_ptrace_hw_breakpoint(struct task_struct *tsk)
{
	int i;
	struct thread_struct *t = &tsk->thread;

	for (i = 0; i < ARM_MAX_HBP_SLOTS; i++) {
		if (t->debug.hbp[i]) {
			unregister_hw_breakpoint(t->debug.hbp[i]);
			t->debug.hbp[i] = NULL;
		}
	}
}

    27)、更新所有子进程的父进程 

	exit_notify(tsk, group_dead);

/* linux-2.6.38.8/kernel/exit.c */
static void exit_notify(struct task_struct *tsk, int group_dead)
{
	int signal;
	void *cookie;

	/*
	 * This does two things:
	 *
  	 * A.  Make init inherit all the child processes
	 * B.  Check to see if any process groups have become orphaned
	 *	as a result of our exiting, and if they have any stopped
	 *	jobs, send them a SIGHUP and then a SIGCONT.  (POSIX 3.2.2.2)
	 */
	forget_original_parent(tsk); //将子进程的父进程重新设置为线程组中的其他线程或init进程
	exit_task_namespaces(tsk); //当使用计数(current->nsproxy->count)为0时,释放命名空间(current->nsproxy)

	write_lock_irq(&tasklist_lock);
	if (group_dead)
		kill_orphaned_pgrp(tsk->group_leader, NULL);

	/* Let father know we died
	 *
	 * Thread signals are configurable, but you aren't going to use
	 * that to send signals to arbitary processes.
	 * That stops right now.
	 *
	 * If the parent exec id doesn't match the exec id we saved
	 * when we started then we know the parent has changed security
	 * domain.
	 *
	 * If our self_exec id doesn't match our parent_exec_id then
	 * we have changed execution domain as these two values started
	 * the same after a fork.
	 */
	if (tsk->exit_signal != SIGCHLD && !task_detached(tsk) &&  //task_detached函数用于判断tsk->exit_signal是否等于-1
	    (tsk->parent_exec_id != tsk->real_parent->self_exec_id ||
	     tsk->self_exec_id != tsk->parent_exec_id))
		tsk->exit_signal = SIGCHLD;  //设置SIGCHLD信号

	signal = tracehook_notify_death(tsk, &cookie, group_dead); //判断当前进程是否被跟踪
	if (signal >= 0)
		signal = do_notify_parent(tsk, signal); //告知父进程当前进程死亡

	tsk->exit_state = signal == DEATH_REAP ? EXIT_DEAD : EXIT_ZOMBIE; //当tsk->exit_signal不等于-1,或进程正在被跟踪,则设置tsk->exit_state为EXIT_ZOMBIE

	/* mt-exec, de_thread() is waiting for group leader */
	if (unlikely(tsk->signal->notify_count < 0))
		wake_up_process(tsk->signal->group_exit_task);
	write_unlock_irq(&tasklist_lock);

	tracehook_report_death(tsk, signal, cookie, group_dead);

	/* If the process is dead, release it - nobody will wait for it */
	if (signal == DEATH_REAP) //如果tsk->exit_state为EXIT_DEAD状态
		release_task(tsk); //则调用release_task函数回收进程的其他数据结构所占用的内存
}

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

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

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

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

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

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

/* linux-2.6.38.8/block/blk-ioc.c */
void exit_io_context(struct task_struct *task)
{
	struct io_context *ioc;

	task_lock(task);
	ioc = task->io_context;
	task->io_context = NULL;
	task_unlock(task);

	if (atomic_dec_and_test(&ioc->nr_tasks))
		cfq_exit(ioc);

	put_io_context(ioc);
}

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

	if (tsk->splice_pipe)
		__free_pipe_info(tsk->splice_pipe);

/* linux-2.6.38.8/fs/pipe.c */
void __free_pipe_info(struct pipe_inode_info *pipe)
{
	int i;

	for (i = 0; i < pipe->buffers; i++) {
		struct pipe_buffer *buf = pipe->bufs + i;
		if (buf->ops)
			buf->ops->release(pipe, buf);
	}
	if (pipe->tmp_page)
		__free_page(pipe->tmp_page);
	kfree(pipe->bufs);
	kfree(pipe);
}

    32)、调度其它进程 

	tsk->state = TASK_DEAD; //调度程序忽略处于TASK_DEAD状态的进程
	schedule();

    在调用do_exit函数之后,尽管进程已经不能再被调度,但系统还是保留了它的进程描述符,这样做是为了让系统有办法在进程终止后仍能获得它的信息。在父进程获得已终止子进程的信息后,子进程的task_struct结构体才被释放(包括此进程的内核栈)。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tanglinux

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值