慢慢欣赏linux 进程和线程(一)

进程是具有独立地址空间的;线程没有独立地址空间,一种说法是内核里面只有线程,因为内核的地址空间是共享的(其实操作系统里面很多实现没有非黑即白的概念)。
有两篇文章写得很好
http://blog.csdn.net/u012927281/article/details/51602898
http://www.2ndmoon.net/weblog/?p=603

关于进程的子进程的补充:

parent、sibling、children都是task_struct中的成员,其中parent是task_struct指针,sibling和children都是list_head结构体。
有趣是因为,除了parent比较容易理解之外,另外两个都“不太正常”。

  • sibling.next指向进程的下一个兄弟进程的进程描述符sibling成员,若其后没有其他兄弟进程,则指向父进程;而sibling.prev指向进程的上一个兄弟进程,若其之前没有兄弟进程,则指向父进程。
  • children.next指向父进程的第一个子进程的sibling成员(而不是children成员!),而children.prev却指向父进程的最后一个子进程的sibling成员。
创建子进程
do_fork
	=>p = copy_process(clone_flags, stack_start, regs, stack_size, child_tidptr, NULL, trace);
		=>p = dup_task_struct(current);
			=>ti = alloc_thread_info_node(tsk, node);
			=>err = arch_dup_task_struct(tsk, orig);
			=>setup_thread_stack(tsk, orig);//8K内核堆栈区
		=>sched_fork(p);
			=>p->state = TASK_RUNNING;//子进程为就绪态
		=>pid = alloc_pid(p->nsproxy->pid_ns);//申请pid
			=>pid = kmem_cache_alloc(ns->pid_cachep, GFP_KERNEL);
			=>nr = alloc_pidmap(tmp);
			=>hlist_add_head_rcu(&upid->pid_chain,
				&pid_hash[pid_hashfn(upid->nr, upid->ns)]);
		=>retval = copy_thread(clone_flags, stack_start, stack_size, p, regs);
	=>wake_up_new_task(p);//将新的子进程加入就绪队列
		=>rq = __task_rq_lock(p);
		=>activate_task(rq, p, 0);
			=>enqueue_task(rq, p, flags);
				=>p->sched_class->enqueue_task(rq, p, flags);//进入就绪队列

创建内核线程
//powerpc的做法
kernel_thread
	=>mr	r30,r3		/* function *///压栈,系统调用sys_clone
	mr	r31,r4		/* argument */
	ori	r3,r5,CLONE_VM	/* flags */
	oris	r3,r3,CLONE_UNTRACED>>16
	li	r4,0		/* new sp (unused) */
	li	r0,__NR_clone
	sc
		=>sys_clone
			=>do_fork(clone_flags, usp, regs, 0, parent_tidp, child_tidp);
//X86的做法
kernel_thread
	=>regs.orig_ax = -1;
	regs.ip = (unsigned long) kernel_thread_helper;
	regs.cs = __KERNEL_CS | get_kernel_rpl();
	regs.flags = X86_EFLAGS_IF | 0x2;
	=>do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);

关于switch_to
schedule
	=>preempt_disable();
	=>cpu = smp_processor_id();
	rq = cpu_rq(cpu);
	=>prev = rq->curr;
	=>next = pick_next_task(rq);//按照策略算法选择下一个要切换的进程
	=>context_switch(rq, prev, next);//切换
		=>switch_to(prev, next, prev);
			=>(last) = __switch_to((prev), (next))
				=>new_thread = &new->thread;
				old_thread = &current->thread;
				=>last = _switch(old_thread, new_thread);
					=>stw	r1,KSP(r3)	/* Set old stack pointer */
					=>lwz	r1,KSP(r4)	/* Load new stack pointer */
					=>lwz	r4,_NIP(r1)	/* Return to _switch caller in new task */
						=>DEFINE(_NIP, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, nip));
							=>#define DEFINE(sym, val) \
						        asm volatile("\n->" #sym " %0 " #val : : "i" (val))
						    thread_struct {
							    ...
							    struct pt_regs	*regs;	
							    ...
						    }
						    struct pt_regs {
							    ...
							    unsigned long nip;
								unsigned long msr;
							    ...
						    }
					mtlr	r4
					addi	r1,r1,INT_FRAME_SIZE
					blr
	=>preempt_enable_no_resched();

copy_thread初始化子进程的内核栈,和新进程的正文段在linux的核心空间,与其它核心进程以及linux内核共享相同的核心正文段。
进程切换只发生在内核态,在执行进程切换之前,用户态进程使用的所有寄存器内容都已经保存在内核态堆栈上,包括用户态进程的SS和ESP寄存器。
Linux系统存在两类用户进程,用户进程和用户线程。严格意义上说,只有拥有了进程描述符,PID,进程正文端,用户进程的数据段和栈段,核心栈段的进程才被称为用户进程,二没有独立数据段和栈段的进程被称为线程。
LINUX系统中,进程和线程都有自己的进程描述符与核心栈段。其中,核心栈段用来支持用户进程或线程在LINUX核心中执行。

关于switch_to,下面这篇文章讲得挺好
linux内核——进程切换宏switch_to
http://www.cnblogs.com/ISeeIC/p/3625556.html

进程状态可以通过show_state_filter函数查看

参考如下博客:
task_struct与进程关系
http://blog.csdn.net/seayoungsjtu/article/details/51077840

关于thread_info结构
Linux进程内核栈与thread_info结构详解–Linux进程的管理与调度(九)
http://blog.csdn.net/gatieme/article/details/51577479

linux内核分析笔记----进程管理
http://www.cnblogs.com/hanyan225/archive/2011/07/09/2101962.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值