- 浏览: 128798 次
- 性别:
- 来自: 北京
-
最新评论
-
yangtaoxf126.com:
public class SimpleJDBC {
publ ...
oracle的sys用户怎么通过jdbc连接
上章讲到线程,现在对线程创建的代码流程分析下。来一步一步揭开她神秘的面纱
linux内核创建线程函数 kernel_thread(),最终会调用do_fork().
前面谈到线程也是用task_struct结构表示它拥有的信息,只是是共享进程的资源。
根据clone_flags标志,来调用clone()创建"线程",表示共享内存、共享文件系统访问计数、共享文件描述符表,以及共享信号处理方式。
kernel_thread定义在/arch/kernel/process.c
int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
{
struct pt_regs regs;
memset(®s, 0, sizeof(regs)); //把该结构的变量全部清0
regs.ebx = (unsigned long) fn; /* ebx指向函数地址 */
regs.edx = (unsigned long) arg; /* edx指向参数地址 */
regs.xds = __USER_DS;
regs.xes = __USER_DS;
regs.xfs = __KERNEL_PERCPU;
regs.orig_eax = -1;
regs.eip = (unsigned long) kernel_thread_helper; /*eip指向回调函数*/
regs.xcs = __KERNEL_CS | get_kernel_rpl();
regs.eflags = X86_EFLAGS_IF | X86_EFLAGS_SF | X86_EFLAGS_PF | 0x2;
/* 利用do_fork来产生一个新的线程,共享父进程地址空间,并且不允许调试子进程 */
return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL);
}
其中__USER_DS,__KERNEL_PERCPU,__KERNEL_CS都是一些宏定义。在/linux/include/asm-i386/segment.h
extern void kernel_thread_helper(void); /* 定义成全局变量 */
__asm__(".section .text\n"
".align 4\n"
"kernel_thread_helper:\n\t"
"movl %edx,%eax\n\t"
"pushl %edx\n\t" /* edx指向参数,压入堆栈 */
"call *%ebx\n\t" /* ebx指向函数地址,执行函数 */
"pushl %eax\n\t"
"call do_exit\n" /* 结束线程 */
".previous");
在kernel_thread中调用了do_fork,让我们揭开do_fork()的面纱.
long do_fork(unsigned long clone_flags,
unsigned long stack_start,
struct pt_regs *regs,
unsigned long stack_size,
int __user *parent_tidptr,
int __user *child_tidptr)
{
...
...
p = copy_process(clone_flags, stack_start, regs, stack_size, parent_tidptr, child_tidptr, pid);
...
...
}
接着分析do_fork(),copy_proces()是它的核心函数。重点分析一下:
static struct task_struct *copy_process(unsigned long clone_flags,
unsigned long stack_start,
struct pt_regs *regs,
unsigned long stack_size,
int __user *parent_tidptr,
int __user *child_tidptr,
struct pid *pid)
{
int retval;
struct task_struct *p = NULL;
//clone_flags参数的有效性判断
//不能同时定义CLONE_NEWNS,CLONE_FS
if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS))
return ERR_PTR(-EINVAL);
//如果定义CLONE_THREAD,则必须要定义CLONE_SIGHAND
if ((clone_flags & CLONE_THREAD) && !(clone_flags & CLONE_SIGHAND))
return ERR_PTR(-EINVAL);
//如果定义CLONE_SIGHAND,则必须要定义CLONE_VM
if ((clone_flags & CLONE_SIGHAND) && !(clone_flags & CLONE_VM))
return ERR_PTR(-EINVAL);
retval = security_task_create(clone_flags);
if (retval)
goto fork_out;
retval = -ENOMEM;
//从父进程中复制出一个task
p = dup_task_struct(current);
if (!p)
goto fork_out;
rt_mutex_init_task(p);
#ifdef CONFIG_TRACE_IRQFLAGS
DEBUG_LOCKS_WARN_ON(!p->hardirqs_enabled);
DEBUG_LOCKS_WARN_ON(!p->softirqs_enabled);
#endif
retval = -EAGAIN;
//如果用户的进程总数超过了限制
if (atomic_read(&p->user->processes) >=
p->signal->rlim[RLIMIT_NPROC].rlim_cur) {
if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE) &&
p->user != current->nsproxy->user_ns->root_user)
goto bad_fork_free;
}
//更新进程用户的相关计数
atomic_inc(&p->user->__count);
atomic_inc(&p->user->processes);
get_group_info(p->group_info);
//当前进程数是否大于系统规定的最大进程数
if (nr_threads >= max_threads)
goto bad_fork_cleanup_count;
//加载进程的相关执行模块
if (!try_module_get(task_thread_info(p)->exec_domain->module))
goto bad_fork_cleanup_count;
if (p->binfmt && !try_module_get(p->binfmt->module))
goto bad_fork_cleanup_put_domain;
//子进程还在进行初始化,没有execve
p->did_exec = 0;
delayacct_tsk_init(p); /* Must remain after dup_task_struct() */
//copy父进程的所有标志,除了PF_SUPERPRIV(超级权限)
//置子进程的PF_FORKNOEXEC标志,表示正在被FORK
copy_flags(clone_flags, p);
//赋值子进程的pid
p->pid = pid_nr(pid);
retval = -EFAULT;
if (clone_flags & CLONE_PARENT_SETTID)
if (put_user(p->pid, parent_tidptr))
goto bad_fork_cleanup_delays_binfmt;
//初始化子进程的几个链表
INIT_LIST_HEAD(&p->children);
INIT_LIST_HEAD(&p->sibling);
p->vfork_done = NULL;
spin_lock_init(&p->alloc_lock);
//父进程的TIF_SIGPENDING被复制进了子进程,这个标志表示有末处理的信号
//这个标志子进程是不需要的
clear_tsk_thread_flag(p, TIF_SIGPENDING);
init_sigpending(&p->pending);
//初始化子进程的time
p->utime = cputime_zero;
p->stime = cputime_zero;
p->prev_utime = cputime_zero;
……
……
//tgid = pid
p->tgid = p->pid;
if (clone_flags & CLONE_THREAD)
p->tgid = current->tgid;
//copy父进程的其它资源.比例打开的文件,信号,VM等等
if ((retval = security_task_alloc(p)))
goto bad_fork_cleanup_policy;
if ((retval = audit_alloc(p)))
goto bad_fork_cleanup_security;
/* copy all the process information */
if ((retval = copy_semundo(clone_flags, p)))
goto bad_fork_cleanup_audit;
if ((retval = copy_files(clone_flags, p)))
goto bad_fork_cleanup_semundo;
if ((retval = copy_fs(clone_flags, p)))
goto bad_fork_cleanup_files;
if ((retval = copy_sighand(clone_flags, p)))
goto bad_fork_cleanup_fs;
if ((retval = copy_signal(clone_flags, p)))
goto bad_fork_cleanup_sighand;
if ((retval = copy_mm(clone_flags, p)))
goto bad_fork_cleanup_signal;
if ((retval = copy_keys(clone_flags, p)))
goto bad_fork_cleanup_mm;
if ((retval = copy_namespaces(clone_flags, p)))
goto bad_fork_cleanup_keys;
retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs);
if (retval)
goto bad_fork_cleanup_namespaces;
p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL;
/*
* Clear TID on mm_release()?
*/
p->clear_child_tid = (clone_flags & CLONE_CHILD_CLEARTID) ? child_tidptr: NULL;
p->robust_list = NULL;
#ifdef CONFIG_COMPAT
p->compat_robust_list = NULL;
#endif
INIT_LIST_HEAD(&p->pi_state_list);
p->pi_state_cache = NULL;
/*
* sigaltstack should be cleared when sharing the same VM
*/
if ((clone_flags & (CLONE_VM|CLONE_VFORK)) == CLONE_VM)
p->sas_ss_sp = p->sas_ss_size = 0;
/*
* Syscall tracing should be turned off in the child regardless
* of CLONE_PTRACE.
*/
clear_tsk_thread_flag(p, TIF_SYSCALL_TRACE);
#ifdef TIF_SYSCALL_EMU
clear_tsk_thread_flag(p, TIF_SYSCALL_EMU);
#endif
/* Our parent execution domain becomes current domain
These must match for thread signalling to apply */
p->parent_exec_id = p->self_exec_id;
/* ok, now we should be set up.. */
//exit_signal: 子进程退出时给父进程发送的信号
p->exit_signal = (clone_flags & CLONE_THREAD) ? -1 : (clone_flags & CSIGNAL);
//pdeath_signal:进程退出时.给其下的子进程发送的信号
p->pdeath_signal = 0;
p->exit_state = 0;
……
……
if (likely(p->pid)) {
add_parent(p);
if (unlikely(p->ptrace & PT_PTRACED))
__ptrace_link(p, current->parent);
if (thread_group_leader(p)) {
p->signal->tty = current->signal->tty;
p->signal->pgrp = process_group(current);
set_signal_session(p->signal, process_session(current));
attach_pid(p, PIDTYPE_PGID, task_pgrp(current));
attach_pid(p, PIDTYPE_SID, task_session(current));
list_add_tail_rcu(&p->tasks, &init_task.tasks);
__get_cpu_var(process_counts)++;
}
attach_pid(p, PIDTYPE_PID, pid);
//当前进程数递增
nr_threads++;
}
//被fork的进程数计数递增
total_forks++;
spin_unlock(¤t->sighand->siglock);
write_unlock_irq(&tasklist_lock);
proc_fork_connector(p);
return p;
……
……
}
参考:深入理解linux内核
到这里为止,进程的运行内间已经设置好了。但子进程的怎么返回到用户空间呢?这是在copy_process()—> copy_thread()中完成的。
int copy_thread(int nr, unsigned long clone_flags, unsigned long esp,
unsigned long unused,
struct task_struct * p, struct pt_regs * regs)
{
struct pt_regs * childregs;
struct task_struct *tsk;
int err;
//子进程的内核堆栈起点
childregs = task_pt_regs(p);
//将父进程的regs参数赋值到子进程的内核堆栈
//regs参数:里面存放的是父进程陷入内核后的各寄存器的值
*childregs = *regs;
//eax:返回值. 将其设为0,子进程返回到用户空间后,它的返回值是0
childregs->eax = 0;
//esp:子进程的用户堆栈指针位置
childregs->esp = esp;
//子进程内核堆栈位置
p->thread.esp = (unsigned long) childregs;
//子进程内核堆栈指针位置
p->thread.esp0 = (unsigned long) (childregs+1);
//子进程要执行的下一条指令.对应子进程从系统空间返回用户空间
p->thread.eip = (unsigned long) ret_from_fork;
savesegment(gs,p->thread.gs);
tsk = current;
if (unlikely(test_tsk_thread_flag(tsk, TIF_IO_BITMAP))) {
p->thread.io_bitmap_ptr = kmemdup(tsk->thread.io_bitmap_ptr,
IO_BITMAP_BYTES, GFP_KERNEL);
if (!p->thread.io_bitmap_ptr) {
p->thread.io_bitmap_max = 0;
return -ENOMEM;
}
set_tsk_thread_flag(p, TIF_IO_BITMAP);
}
/*
* Set a new TLS for the child thread?
*/
if (clone_flags & CLONE_SETTLS) {
struct desc_struct *desc;
struct user_desc info;
int idx;
err = -EFAULT;
if (copy_from_user(&info, (void __user *)childregs->esi, sizeof(info)))
goto out;
err = -EINVAL;
if (LDT_empty(&info))
goto out;
idx = info.entry_number;
if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX)
goto out;
desc = p->thread.tls_array + idx - GDT_ENTRY_TLS_MIN;
desc->a = LDT_entry_a(&info);
desc->b = LDT_entry_b(&info);
}
err = 0;
out:
if (err && p->thread.io_bitmap_ptr) {
kfree(p->thread.io_bitmap_ptr);
p->thread.io_bitmap_max = 0;
}
return err;
}
在这里把ret_from_fork的地址赋值给p->thread.eip,p->thread.eip表示当进程下一次调度时的指令开始地址,
所以当线程创建后被调度时,是从ret_from_fork地址处开始的.
到这里说明,新的线程已产生了.
ENTRY(ret_from_fork)
pushl %eax
call schedule_tail
GET_THREAD_INFO(%ebp)
popl %eax
jmp syscall_exit
syscall_exit:
...
work_resched:
call schedule
...
当他从ret_from_fork退出时,会从堆栈中弹出原来保存的eip,而ip指向kernel_thread_helper,
至此kernel_thread_helper被调用,他就能够运行我们的指定的函数了do_exit().
从内核空间返回到用户空间。
linux内核创建线程函数 kernel_thread(),最终会调用do_fork().
前面谈到线程也是用task_struct结构表示它拥有的信息,只是是共享进程的资源。
根据clone_flags标志,来调用clone()创建"线程",表示共享内存、共享文件系统访问计数、共享文件描述符表,以及共享信号处理方式。
kernel_thread定义在/arch/kernel/process.c
int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
{
struct pt_regs regs;
memset(®s, 0, sizeof(regs)); //把该结构的变量全部清0
regs.ebx = (unsigned long) fn; /* ebx指向函数地址 */
regs.edx = (unsigned long) arg; /* edx指向参数地址 */
regs.xds = __USER_DS;
regs.xes = __USER_DS;
regs.xfs = __KERNEL_PERCPU;
regs.orig_eax = -1;
regs.eip = (unsigned long) kernel_thread_helper; /*eip指向回调函数*/
regs.xcs = __KERNEL_CS | get_kernel_rpl();
regs.eflags = X86_EFLAGS_IF | X86_EFLAGS_SF | X86_EFLAGS_PF | 0x2;
/* 利用do_fork来产生一个新的线程,共享父进程地址空间,并且不允许调试子进程 */
return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL);
}
其中__USER_DS,__KERNEL_PERCPU,__KERNEL_CS都是一些宏定义。在/linux/include/asm-i386/segment.h
extern void kernel_thread_helper(void); /* 定义成全局变量 */
__asm__(".section .text\n"
".align 4\n"
"kernel_thread_helper:\n\t"
"movl %edx,%eax\n\t"
"pushl %edx\n\t" /* edx指向参数,压入堆栈 */
"call *%ebx\n\t" /* ebx指向函数地址,执行函数 */
"pushl %eax\n\t"
"call do_exit\n" /* 结束线程 */
".previous");
在kernel_thread中调用了do_fork,让我们揭开do_fork()的面纱.
long do_fork(unsigned long clone_flags,
unsigned long stack_start,
struct pt_regs *regs,
unsigned long stack_size,
int __user *parent_tidptr,
int __user *child_tidptr)
{
...
...
p = copy_process(clone_flags, stack_start, regs, stack_size, parent_tidptr, child_tidptr, pid);
...
...
}
接着分析do_fork(),copy_proces()是它的核心函数。重点分析一下:
static struct task_struct *copy_process(unsigned long clone_flags,
unsigned long stack_start,
struct pt_regs *regs,
unsigned long stack_size,
int __user *parent_tidptr,
int __user *child_tidptr,
struct pid *pid)
{
int retval;
struct task_struct *p = NULL;
//clone_flags参数的有效性判断
//不能同时定义CLONE_NEWNS,CLONE_FS
if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS))
return ERR_PTR(-EINVAL);
//如果定义CLONE_THREAD,则必须要定义CLONE_SIGHAND
if ((clone_flags & CLONE_THREAD) && !(clone_flags & CLONE_SIGHAND))
return ERR_PTR(-EINVAL);
//如果定义CLONE_SIGHAND,则必须要定义CLONE_VM
if ((clone_flags & CLONE_SIGHAND) && !(clone_flags & CLONE_VM))
return ERR_PTR(-EINVAL);
retval = security_task_create(clone_flags);
if (retval)
goto fork_out;
retval = -ENOMEM;
//从父进程中复制出一个task
p = dup_task_struct(current);
if (!p)
goto fork_out;
rt_mutex_init_task(p);
#ifdef CONFIG_TRACE_IRQFLAGS
DEBUG_LOCKS_WARN_ON(!p->hardirqs_enabled);
DEBUG_LOCKS_WARN_ON(!p->softirqs_enabled);
#endif
retval = -EAGAIN;
//如果用户的进程总数超过了限制
if (atomic_read(&p->user->processes) >=
p->signal->rlim[RLIMIT_NPROC].rlim_cur) {
if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE) &&
p->user != current->nsproxy->user_ns->root_user)
goto bad_fork_free;
}
//更新进程用户的相关计数
atomic_inc(&p->user->__count);
atomic_inc(&p->user->processes);
get_group_info(p->group_info);
//当前进程数是否大于系统规定的最大进程数
if (nr_threads >= max_threads)
goto bad_fork_cleanup_count;
//加载进程的相关执行模块
if (!try_module_get(task_thread_info(p)->exec_domain->module))
goto bad_fork_cleanup_count;
if (p->binfmt && !try_module_get(p->binfmt->module))
goto bad_fork_cleanup_put_domain;
//子进程还在进行初始化,没有execve
p->did_exec = 0;
delayacct_tsk_init(p); /* Must remain after dup_task_struct() */
//copy父进程的所有标志,除了PF_SUPERPRIV(超级权限)
//置子进程的PF_FORKNOEXEC标志,表示正在被FORK
copy_flags(clone_flags, p);
//赋值子进程的pid
p->pid = pid_nr(pid);
retval = -EFAULT;
if (clone_flags & CLONE_PARENT_SETTID)
if (put_user(p->pid, parent_tidptr))
goto bad_fork_cleanup_delays_binfmt;
//初始化子进程的几个链表
INIT_LIST_HEAD(&p->children);
INIT_LIST_HEAD(&p->sibling);
p->vfork_done = NULL;
spin_lock_init(&p->alloc_lock);
//父进程的TIF_SIGPENDING被复制进了子进程,这个标志表示有末处理的信号
//这个标志子进程是不需要的
clear_tsk_thread_flag(p, TIF_SIGPENDING);
init_sigpending(&p->pending);
//初始化子进程的time
p->utime = cputime_zero;
p->stime = cputime_zero;
p->prev_utime = cputime_zero;
……
……
//tgid = pid
p->tgid = p->pid;
if (clone_flags & CLONE_THREAD)
p->tgid = current->tgid;
//copy父进程的其它资源.比例打开的文件,信号,VM等等
if ((retval = security_task_alloc(p)))
goto bad_fork_cleanup_policy;
if ((retval = audit_alloc(p)))
goto bad_fork_cleanup_security;
/* copy all the process information */
if ((retval = copy_semundo(clone_flags, p)))
goto bad_fork_cleanup_audit;
if ((retval = copy_files(clone_flags, p)))
goto bad_fork_cleanup_semundo;
if ((retval = copy_fs(clone_flags, p)))
goto bad_fork_cleanup_files;
if ((retval = copy_sighand(clone_flags, p)))
goto bad_fork_cleanup_fs;
if ((retval = copy_signal(clone_flags, p)))
goto bad_fork_cleanup_sighand;
if ((retval = copy_mm(clone_flags, p)))
goto bad_fork_cleanup_signal;
if ((retval = copy_keys(clone_flags, p)))
goto bad_fork_cleanup_mm;
if ((retval = copy_namespaces(clone_flags, p)))
goto bad_fork_cleanup_keys;
retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs);
if (retval)
goto bad_fork_cleanup_namespaces;
p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL;
/*
* Clear TID on mm_release()?
*/
p->clear_child_tid = (clone_flags & CLONE_CHILD_CLEARTID) ? child_tidptr: NULL;
p->robust_list = NULL;
#ifdef CONFIG_COMPAT
p->compat_robust_list = NULL;
#endif
INIT_LIST_HEAD(&p->pi_state_list);
p->pi_state_cache = NULL;
/*
* sigaltstack should be cleared when sharing the same VM
*/
if ((clone_flags & (CLONE_VM|CLONE_VFORK)) == CLONE_VM)
p->sas_ss_sp = p->sas_ss_size = 0;
/*
* Syscall tracing should be turned off in the child regardless
* of CLONE_PTRACE.
*/
clear_tsk_thread_flag(p, TIF_SYSCALL_TRACE);
#ifdef TIF_SYSCALL_EMU
clear_tsk_thread_flag(p, TIF_SYSCALL_EMU);
#endif
/* Our parent execution domain becomes current domain
These must match for thread signalling to apply */
p->parent_exec_id = p->self_exec_id;
/* ok, now we should be set up.. */
//exit_signal: 子进程退出时给父进程发送的信号
p->exit_signal = (clone_flags & CLONE_THREAD) ? -1 : (clone_flags & CSIGNAL);
//pdeath_signal:进程退出时.给其下的子进程发送的信号
p->pdeath_signal = 0;
p->exit_state = 0;
……
……
if (likely(p->pid)) {
add_parent(p);
if (unlikely(p->ptrace & PT_PTRACED))
__ptrace_link(p, current->parent);
if (thread_group_leader(p)) {
p->signal->tty = current->signal->tty;
p->signal->pgrp = process_group(current);
set_signal_session(p->signal, process_session(current));
attach_pid(p, PIDTYPE_PGID, task_pgrp(current));
attach_pid(p, PIDTYPE_SID, task_session(current));
list_add_tail_rcu(&p->tasks, &init_task.tasks);
__get_cpu_var(process_counts)++;
}
attach_pid(p, PIDTYPE_PID, pid);
//当前进程数递增
nr_threads++;
}
//被fork的进程数计数递增
total_forks++;
spin_unlock(¤t->sighand->siglock);
write_unlock_irq(&tasklist_lock);
proc_fork_connector(p);
return p;
……
……
}
参考:深入理解linux内核
到这里为止,进程的运行内间已经设置好了。但子进程的怎么返回到用户空间呢?这是在copy_process()—> copy_thread()中完成的。
int copy_thread(int nr, unsigned long clone_flags, unsigned long esp,
unsigned long unused,
struct task_struct * p, struct pt_regs * regs)
{
struct pt_regs * childregs;
struct task_struct *tsk;
int err;
//子进程的内核堆栈起点
childregs = task_pt_regs(p);
//将父进程的regs参数赋值到子进程的内核堆栈
//regs参数:里面存放的是父进程陷入内核后的各寄存器的值
*childregs = *regs;
//eax:返回值. 将其设为0,子进程返回到用户空间后,它的返回值是0
childregs->eax = 0;
//esp:子进程的用户堆栈指针位置
childregs->esp = esp;
//子进程内核堆栈位置
p->thread.esp = (unsigned long) childregs;
//子进程内核堆栈指针位置
p->thread.esp0 = (unsigned long) (childregs+1);
//子进程要执行的下一条指令.对应子进程从系统空间返回用户空间
p->thread.eip = (unsigned long) ret_from_fork;
savesegment(gs,p->thread.gs);
tsk = current;
if (unlikely(test_tsk_thread_flag(tsk, TIF_IO_BITMAP))) {
p->thread.io_bitmap_ptr = kmemdup(tsk->thread.io_bitmap_ptr,
IO_BITMAP_BYTES, GFP_KERNEL);
if (!p->thread.io_bitmap_ptr) {
p->thread.io_bitmap_max = 0;
return -ENOMEM;
}
set_tsk_thread_flag(p, TIF_IO_BITMAP);
}
/*
* Set a new TLS for the child thread?
*/
if (clone_flags & CLONE_SETTLS) {
struct desc_struct *desc;
struct user_desc info;
int idx;
err = -EFAULT;
if (copy_from_user(&info, (void __user *)childregs->esi, sizeof(info)))
goto out;
err = -EINVAL;
if (LDT_empty(&info))
goto out;
idx = info.entry_number;
if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX)
goto out;
desc = p->thread.tls_array + idx - GDT_ENTRY_TLS_MIN;
desc->a = LDT_entry_a(&info);
desc->b = LDT_entry_b(&info);
}
err = 0;
out:
if (err && p->thread.io_bitmap_ptr) {
kfree(p->thread.io_bitmap_ptr);
p->thread.io_bitmap_max = 0;
}
return err;
}
在这里把ret_from_fork的地址赋值给p->thread.eip,p->thread.eip表示当进程下一次调度时的指令开始地址,
所以当线程创建后被调度时,是从ret_from_fork地址处开始的.
到这里说明,新的线程已产生了.
ENTRY(ret_from_fork)
pushl %eax
call schedule_tail
GET_THREAD_INFO(%ebp)
popl %eax
jmp syscall_exit
syscall_exit:
...
work_resched:
call schedule
...
当他从ret_from_fork退出时,会从堆栈中弹出原来保存的eip,而ip指向kernel_thread_helper,
至此kernel_thread_helper被调用,他就能够运行我们的指定的函数了do_exit().
从内核空间返回到用户空间。
发表评论
-
__define_initcall 作用
2010-11-25 19:26 1258前言 宏定义__define_initcall ... -
转载--Linux 2.6内核I/O端口资源管理
2010-11-02 15:39 855申明:本文章是对“Linux对I/O端口资源的管理”该文章进行 ... -
linux 2.6进程与线程
2010-11-02 15:09 12431 > 线程和进程的 ... -
linux中ELF加载过程分析
2010-10-19 11:16 1960linux中ELF加载过程分析 - 博青港湾-技术空间 - ... -
Linux设备模型 学习总结
2010-10-13 21:21 961看LDD3中设备模型一章,觉得思维有些混乱。这里从整体的角度来 ... -
linux软中断的实现
2010-10-13 21:12 1120中断服务程序往往都是在CPU关中断的条件下执行的,以避免中断 ... -
zImage / vmlinux / Image生成的流程图
2010-10-13 21:11 2448SRC_TREE :=./ HEADER_PATH ... -
执行程序时内存管理的情景
2010-10-13 21:07 1471do_execve ->open_exec->de ... -
Linux Kernel 2.6进程调度的分析(揭示了几乎所有2.6调度的东西)
2010-10-13 20:16 1921第一章 Kernel 2.4存在的 ... -
ibm developer blog
2010-10-10 10:05 888Linux slab 分配器剖析 http://www.ibm ... -
linux2.6.29 启动过程详细分析
2010-08-12 16:02 1374突然心血来潮,想自己写个模块,于是就把linux2.6.29的 ... -
Linux内存:内存管理的实质
2010-08-04 21:22 711<转 http://linux.chinaunix. ... -
container_of 解析
2010-07-29 22:50 986在学习Linux驱动的过程中,遇到一个宏叫做container ... -
高级IO操作——ioctl
2010-07-23 20:48 1464_IO, _IOR, _IOW, _IOWR 宏的用法与解析 ... -
主设备号、次设备号
2010-07-23 16:13 933主设备号、次设备号分 ... -
Linux在控制台下不同类型文件显示不同颜色
2010-07-20 23:47 2580# ~/.bashrc: executed by bash(1 ... -
linux 编译内核 选项出错 综合
2010-07-20 23:24 1015具体错误提示见最后面 ... -
ARM SDT下汇编到GNU汇编的转换
2010-07-16 08:52 897将ARM SDT下的汇编码移植到GCC for ARM编译器时 ... -
GNU ARM 汇编指令(转)
2010-07-16 08:50 1675第一部分 Linux下ARM汇编语法尽管在Linux下使用C或 ... -
arm 嵌入式LINUX启动过程
2010-07-11 00:44 3912一位大师级的人物写的,不看要后悔的哟!! ...
相关推荐
《Linux2.6内核标准教程》适合Linux内核爱好者、Linux驱动开发人员、Linux系统工程师参考使用,也可以作为计算机及相关专业学生深入学 习操作系统的参考书。 引用: 目录 第1章 Linux内核学习基础 1 1.1 为什么...
《Linux2.6内核标准教程》适合Linux内核爱好者、Linux驱动开发人员、Linux系统工程师参考使用,也可以作为计算机及相关专业学生深入学 习操作系统的参考书。 引用: 目录 第1章 Linux内核学习基础 1 1.1 为什么...
《Linux2.6内核标准教程》适合Linux内核爱好者、Linux驱动开发人员、Linux系统工程师参考使用,也可以作为计算机及相关专业学生深入学 习操作系统的参考书。 引用: 目录 第1章 Linux内核学习基础 1 1.1 为什么...
通过深入阅读《Linux内核协议栈源码解析(2.6.18内核)》,读者不仅可以了解Linux内核的网络机制,还能学习到如何分析和优化网络性能,这对于系统管理员、网络工程师以及驱动开发人员来说都是非常有价值的。...
《Linux2.6内核标准教程》适合Linux内核爱好者、Linux驱动开发人员、Linux系统工程师参考使用,也可以作为计算机及相关专业学生深入学 习操作系统的参考书。 引用: 目录 第1章 Linux内核学习基础 1 1.1 为什么...
《Linux2.6内核标准教程》适合Linux内核爱好者、Linux驱动开发人员、Linux系统工程师参考使用,也可以作为计算机及相关专业学生深入学 习操作系统的参考书。 引用: 目录 第1章 Linux内核学习基础 1 1.1 为什么...
《Linux2.6内核标准教程》适合Linux内核爱好者、Linux驱动开发人员、Linux系统工程师参考使用,也可以作为计算机及相关专业学生深入学 习操作系统的参考书。 引用: 目录 第1章 Linux内核学习基础 1 1.1 为什么...
《Linux2.6内核标准教程》适合Linux内核爱好者、Linux驱动开发人员、Linux系统工程师参考使用,也可以作为计算机及相关专业学生深入学 习操作系统的参考书。 引用: 目录 第1章 Linux内核学习基础 1 1.1 为什么...
《Linux2.6内核标准教程》适合Linux内核爱好者、Linux驱动开发人员、Linux系统工程师参考使用,也可以作为计算机及相关专业学生深入学 习操作系统的参考书。 引用: 目录 第1章 Linux内核学习基础 1 1.1 为什么...
在"linux-2.6.11.12.bak.20130905_linux源码_"这个压缩包中,包含了Linux内核的重要组成部分的源代码注释,这对于理解Linux内核的工作原理以及进行内核开发和调试非常有帮助。下面我们将详细探讨其中涉及的关键知识...
1.4 分析Linux内核的意义 1.5 Linux内核结构 1.6 Linux内核源代码 1.7 Linux内核源代码分析工具 第二章 Linux运行的硬件基础 2.1 i386的寄存器 2.2 内存地址 2.3 段机制和描述符 2.4 分页机制 2.5 Linux中的分页机制...
深入分析Linux内核源码 前言 第一章 走进linux 1.1 GNU与Linux的成长 1.2 Linux的开发模式和运作机制 1.3走进Linux内核 1.3.1 Linux内核的特征 1.3.2 Linux内核版本的变化 1.4 分析Linux内核的意义 ...
综上所述,NPTL作为Linux操作系统中的新一代线程库,在线程创建、销毁及切换开销方面相比LinuxThreads有了显著的性能提升。这些改进主要得益于NPTL对内核特性的充分利用,以及对用户级线程管理机制的优化。对于追求...
### Linux内核源码核心知识点解析 #### 一、走进Linux **1.1 GNU与Linux的成长** - **背景介绍**:了解Linux的历史背景对于理解其设计哲学至关重要。GNU项目始于1983年,旨在创建一套完全自由的类Unix操作系统。而...
7. **多线程和同步机制**:内核源码中的线程管理及同步原语,如信号量、自旋锁、读写锁,帮助我们理解多处理器环境下的并发执行。 8. **模块化设计**:Linux 2.4.0内核支持模块加载,允许用户在运行时添加或移除...
- **进程创建**:分析Linux内核中进程创建的具体实现方法。 - **线程实现**:介绍Linux内核中线程模型的设计与实现。 - **进程终止**:讨论进程结束时内核如何处理相关的资源释放和清理工作。 ##### 第四章:...
5.3 实验内容——创建Linux内核和文件系统 5.4 本章小结 5.5 思考与练习 第6章 文件I/O编程 6.1 Linux系统调用及用户编程接口(API) 6.1.1 系统调用 6.1.2 用户编程接口(API) 6.1.3 系统命令 6.2 Linux中文件及文件...
- **获取内核源码**:通过Git或其他方式获取最新的Linux内核源码。可以从Linux内核官网或者官方GitHub仓库获取。 - **配置环境**:根据目标硬件平台的特性,可能需要配置交叉编译器、内核配置选项等。 - **了解...
在Linux内核2.6版本中,`ping`的源代码揭示了其工作原理和实现细节。这篇分析将深入探讨`ping`命令背后的编程概念和系统调用。 首先,`ping`的主要功能是发送ICMP回显请求(类型8,代码0)到目标主机,并等待接收...