文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>Linux创建进程

Linux创建进程

时间:2006-03-06  来源:ChinaE_OS

创建进程

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, &regs, 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, &regs, 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, &regs, 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。

 

相关阅读 更多 +
排行榜 更多 +
试着飞手游下载

试着飞手游下载

休闲益智 下载
血染小镇(功能菜单)中文版下载

血染小镇(功能菜单)中文版下载

飞行射击 下载
泰坦之旅高爆版下载

泰坦之旅高爆版下载

角色扮演 下载