红联Linux门户
Linux帮助

几篇关于linux进程的好文章

发布时间:2006-11-24 01:05:38来源:红联作者:Victory
1:轻量级进程:

两个轻量级进程可以共享一些资源,如:地址空间,打开文件等。只要一个进程修改共享资源,其他进程就可以立刻获知,所以两个进程应该同步访问共享资源。

在传统的Unix操作系统中,当创建一个进程时,该进程将复制父进程的资源,这样创建进程时非常慢,而且效率也很低。

现代Unix已经采用下面三种方法改进了创建进程的方法:

(1) Copy On Write技术允许父进程和子进程同时读同一个物理页面;当其中的一个写物理页面时,内核复制页面上的内容到一个新的分配写进程物理页面上。

(2) 轻量级进程允许父子进程共享每个处理器的内核数据结构,如:页表,打开文件表等。

(3) Vfork系统调用创建的进程可以共享父进程的内存空间,为了阻止父进程修改子进程需要的数据,父进程可以堵塞,直到子进程执行结束。

2:clone, fork, vfork系统调用

(1) clone:使用clone创建轻量级进程。在C库中,clone只是简单分装了sys_clone系统调用,sys_clone实现了没有fn和arg参数的系统调用。

asmlinkage int sys_clone(struct pt_regs regs)

{

unsigned long clone_flags;

unsigned long newsp;

int __user *parent_tidptr, *child_tidptr;

clone_flags = regs.ebx;

newsp = regs.ecx;

parent_tidptr = (int __user *)regs.edx;

child_tidptr = (int __user *)regs.edi;

if (!newsp)

newsp = regs.esp;

return do_fork(clone_flags, newsp, ®s, 0, parent_tidptr, child_tidptr);

}



(2) fork:当clone系统调用的flag标志指定SIGCHLD信号和所有的clone标志清楚,以及child_stack是当前父进程的栈指针时,fork系统调用被调用。所以父子进程暂时共享用户态栈,但是考虑到Copy On Write机制,当他们修改栈时,他们就获取独立的用户态栈。

asmlinkage int sys_vfork(struct pt_regs regs)

{

return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, ®s, 0, NULL, NULL);

}

(3) vfork:当clone系统调用指定flags为SIGCHLD,CLONE_VM,CLONE_VFORK以及child_stack为父进程的栈时,vfork被调用。

asmlinkage int sys_fork(struct pt_regs regs)

{

return do_fork(SIGCHLD, regs.esp, ®s, 0, NULL, NULL);

}

3:do_fork

do_fork处理clone, fork, vfork系统调用。do_fork使用辅助函数copy_process建立进程描述符以及内核数据结构。

(1) 通过查找pidmap_array给子进程分配一个PID.

long pid = alloc_pidmap();

(2) 检查父进程的ptrace,如果不为0,说明父进程被请他进程跟踪,do_fork需要检查debugger是否跟踪子进程。

if (unlikely(current->ptrace)) {

trace = fork_traceflag (clone_flags);

if (trace)

clone_flags |= CLONE_PTRACE;

}

(3) 调用copy_process,复制进程描述符。如果有可用的资源,该函数返回新建进程的task_struct。

p = copy_process(clone_flags, stack_start, regs, stack_size, parent_tidptr, child_tidptr, pid);

(4) 如果设置CLONE_STOPPED或者子进程必须跟踪,即:设置p->ptrace。子进程的状态一直为TASK_STOPPED,直到起他进程将该状态变为TASK_RUNNING.

if ((p->ptrace & PT_PTRACED) || (clone_flags & CLONE_STOPPED)) {

/*

* We'll start up with an immediate SIGSTOP.

*/

sigaddset(&p->pending.signal, SIGSTOP);

set_tsk_thread_flag(p, TIF_SIGPENDING);

}

(5) 如果没有设置CLONE_STOPPED,调用wake_up_new_task, 否则设置子进程为TASK_STOPPED.

if (!(clone_flags & CLONE_STOPPED))

wake_up_new_task(p, clone_flags);

else

p->state = TASK_STOPPED;

(6) 如果父进程是被跟踪的,在当前进程的ptrace_message域中保存子进程的PID,并且调用ptrace_notify,停止当前进程,并且发送SIGCHD到父进程。子进程的祖父进程是被调试进程的debugger, SIGCHD告诉debugger当前进程创建了一个子进程,子进程的PID可以从当前进程的ptrace_message中找到。

if (unlikely (trace)) {

current->ptrace_message = pid;

ptrace_notify ((trace << 8) | SIGTRAP);

}



(7) 如果设置CLONE_VFORK,父进程被插入到等待队列,并且挂起它。直到子进程释放它的内存空间。

if (clone_flags & CLONE_VFORK) {

wait_for_completion(&vfork);

if (unlikely (current->ptrace & PT_TRACE_VFORK_DONE))



ptrace_notify ((PTRACE_EVENT_VFORK_DONE << 8) | SIGTRAP);

}



(8) 返回子进程的PID



二 copy_process



3: copy_process

(1) 检查clone_flags是否兼容,如果不兼容,返回一个错误码。

if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS))

return ERR_PTR(-EINVAL);

/*

* Thread groups must share signals as well, and detached threads

* can only be started up within the thread group.

*/

if ((clone_flags & CLONE_THREAD) && !(clone_flags & CLONE_SIGHAND))

return ERR_PTR(-EINVAL);

/*

* Shared signal handlers imply shared VM. By way of the above,

* thread groups also imply shared VM. Blocking this case allows

* for various simplifications in other code.

*/

if ((clone_flags & CLONE_SIGHAND) && !(clone_flags & CLONE_VM))

return ERR_PTR(-EINVAL);


(2) 调用security_task_create进行附加的安全检查。

retval = security_task_create(clone_flags);

if (retval)

goto fork_out;

(3) 调用dup_task_struct获取子进程的进程描述符。

p = dup_task_struct(current);

if (!p)

goto fork_out;

该函数代码如下:

static struct task_struct *dup_task_struct(struct task_struct *orig)

{

struct task_struct *tsk;

struct thread_info *ti;



prepare_to_copy(orig);



tsk = alloc_task_struct();

if (!tsk)

return NULL;



ti = alloc_thread_info(tsk);

if (!ti) {

free_task_struct(tsk);

return NULL;

}



*ti = *orig->thread_info;

*tsk = *orig;

tsk->thread_info = ti;

ti->task = tsk;



/* One for us, one for whoever does the "release_task()" (usually parent) */

atomic_set(&tsk->usage,2);

return tsk;

}



(4) 检查存储在p->signal->rlim[RLIMIT_NPROC].rlim_cur的值是否小于等于用户拥有的进程数,如果是,并且用户没有root权限,返回错误码。该函数从每个用户的数据结构user_struct中获取当前用户拥有的进程数。user_struct可以通过进程描述符得user中的指针获取到。

if (atomic_read(&p->user->processes) >=

p->signal->rlim[RLIMIT_NPROC].rlim_cur) {

if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE) &&

p->user != &root_user)

goto bad_fork_free;

}

(5) 增加user_struct的用户数和当前用户的进程数。

atomic_inc(&p->user->__count);

atomic_inc(&p->user->processes);

(6) 检查系统的进程数是否大于系统允许的最大进程数。最大进程数和系统的RAM有关,一般规则是:thread_info和内核栈的大小不能超过物理空间的1/8。

(7) 如果新进程中实现执行域和可执行格式的内核函数在内核模块中,模块的使用数加1。

if (!try_module_get(p->thread_info->exec_domain->module))

goto bad_fork_cleanup_count;



if (p->binfmt && !try_module_get(p->binfmt->module))

goto bad_fork_cleanup_put_domain;

(8) 保存新进程的pid, 如果设置CLONE_PARENT_SETTID,复制孩子的pid到parent_tidptr参数寻址的用户态变量中。

copy_flags(clone_flags, p);

p->pid = pid;

retval = -EFAULT;

if (clone_flags & CLONE_PARENT_SETTID)

if (put_user(p->pid, parent_tidptr))

goto bad_fork_cleanup;

(9) 初始化子进程描述符中的list_head和自选锁,以及建立一些与挂起信号,时钟和时间统计的域。

INIT_LIST_HEAD(&p->children);

INIT_LIST_HEAD(&p->sibling);

p->vfork_done = NULL;

spin_lock_init(&p->alloc_lock);

spin_lock_init(&p->proc_lock);



clear_tsk_thread_flag(p, TIF_SIGPENDING);

init_sigpending(&p->pending);



p->utime = cputime_zero;

p->stime = cputime_zero;

p->sched_time = 0;

p->rchar = 0; /* I/O counter: bytes read */

p->wchar = 0; /* I/O counter: bytes written */

p->syscr = 0; /* I/O counter: read syscalls */

p->syscw = 0; /* I/O counter: write syscalls */

acct_clear_integrals(p);



p->it_virt_expires = cputime_zero;

p->it_prof_expires = cputime_zero;

p->it_sched_expires = 0;

INIT_LIST_HEAD(&p->cpu_timers[0]);

INIT_LIST_HEAD(&p->cpu_timers[1]);

INIT_LIST_HEAD(&p->cpu_timers[2]);

(10) 调用copy_semundo( ), copy_files( ), copy_fs( ), copy_sighand( ), copy_signal( ), copy_mm( )和copy_namespace( )创建新的数据结构,并且复制父进程中相应的内容到这些结构中。

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_namespace(clone_flags, p)))

goto bad_fork_cleanup_keys;

(11) 调用copy_thread用CPU寄存器的值初始化子进程的内核态栈。将0强制保存在eax寄存器相应的域(这是clone, fork系统调用生成的孩子进程的返回值)。用孩子进程内核栈的基地址初始化子进程描述符的thread.esp域。汇编语言写的函数(ret_from_fork)的地址保存在thread.eip.如果父进程使用I/O permission Bitmap, 子进程复制父进程的;最后,如果设置CLONE_SETTLS,子进程获取用户态数据结构设置的TSL段。

retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs);

if (retval)

goto bad_fork_cleanup_namespace

(12) 如果设置CLONE_CHILD_SETTID,CLONE_CHILD_CLEARTID,复制child_tidptr的值到新进程的set_child_tid或者set_child_tid域。



(13) 为了防止ret_from_fork通知调试进程系统调用结束,关闭thread_info中的TIF_SYSCALL_TRACE
文章评论

共有 7 条评论

  1. f78815925 于 2010-08-25 20:40:28发表:

    看的有点晕头{:2_102:}

  2. kencae 于 2010-08-22 11:57:10发表:

    确实看不太懂。。。初学的。

  3. xianglin134 于 2010-08-20 16:50:27发表:

    {:2_91:}

  4. xianglin134 于 2010-08-20 16:50:20发表:

    {:2_91:}

  5. chameleon 于 2006-11-29 08:37:44发表:

    学习中

  6. nickck 于 2006-11-28 22:58:54发表:

    谢谢提供

  7. xxh2005 于 2006-11-28 21:10:48发表:

    :0(1 初学不大懂