Linux 커 널 - 프로 세 스 - dofork()

Linux 는 세 가지 방식 으로 프로 세 스 를 만 듭 니 다.
  •  fork: 부모 프로 세 스 의 모든 자원 은 데이터 구조의 복 제 를 통 해 하위 프로 세 스에 유전 된다.쓸 때 복사 하면 부자 프로 세 스 가 같은 물리 페이지 를 읽 을 수 있 도록 합 니 다. 둘 중 하 나 는 물리 페이지 를 쓰 려 고 하면 커 널 은 이 페이지 의 내용 을 새로운 물리 페이지 로 복사 하고 이 새로운 물리 페이지 를 쓰 고 있 는 프로 세 스에 할당 합 니 다
  •  clone: 자원 을 하위 프로 세 스에 선택 적 으로 복사 하고 복사 하지 않 은 데이터 구 조 는 포인터 복 사 를 통 해 하위 프로 세 스 를 공유 합 니 다. 예 를 들 어 페이지 표 (전체 사용자 상태 공간), 열 린 파일 표 와 신호 처리 등 은 보통 스 레 드 를 만 듭 니 다
  •  vfork: vfork 시스템 에서 만 든 프로 세 스 를 호출 하면 부모 프로 세 스 의 메모리 주소 공간 을 공유 할 수 있 습 니 다.부모 프로 세 스 가 하위 프로 세 스 에 필요 한 데 이 터 를 다시 쓰 는 것 을 방지 하기 위해 부모 프로 세 스 의 실행 을 막 습 니 다. 하위 프로 세 스 가 종료 되 거나 새로운 프로그램 을 실행 할 때 까지 입 니 다

  • 위의 세 가지 방식 의 시스템 호출 코드 는 다음 과 같다.
    asmlinkage int sys_fork(struct pt_regs regs)
    {
    	return do_fork(SIGCHLD, regs.esp, &regs, 0, NULL, NULL);
    }
    
    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);
    }
    
    asmlinkage int sys_vfork(struct pt_regs regs)
    {
    	return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, &regs, 0, NULL, NULL);
    }

    이 세 가지 방식 은 마지막 에 도 호출 을 통 해fork 함수 로 이 루어 진, dofork 함수 이용 보조 함수 copyprocess 는 프로 세 스 설명자 한 근 의 하위 프로 세 스 수행 에 필요 한 모든 커 널 데이터 구 조 를 만 듭 니 다. dofork 함수 원본 코드 는 다음 과 같 습 니 다:
    /**
     *     clone,fork,vfork    。
     * clone_flags- clone flag    
     * stack_start- clone child_stack  
     * regs-         。                        。
     * stack_size-   ,   0
     * parent_tidptr,child_tidptr-clone     ptid,ctid  
     */
    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)
    {
    	struct task_struct *p;
    	int trace = 0;
    	/**
    	 *     pidmap_array  ,        pid  .
    	 */
    	long pid = alloc_pidmap();
    
    	if (pid < 0)
    		return -EAGAIN;
    	/**
    	 *           ,   debugger          .           (CLONE_UNTRACED   )
    	 *      CLONE_PTRACE  .
    	 */
    	if (unlikely(current->ptrace)) {
    		trace = fork_traceflag (clone_flags);
    		if (trace)
    			clone_flags |= CLONE_PTRACE;
    	}
    
    	/**
    	 * copy_process       .              ,         task_struct      .
    	 *            .
    	 */
    	p = copy_process(clone_flags, stack_start, regs, stack_size, parent_tidptr, child_tidptr, pid);
    	
    	if (!IS_ERR(p)) {
    		struct completion vfork;
    
    		if (clone_flags & CLONE_VFORK) {
    			p->vfork_done = &vfork;
    			init_completion(&vfork);
    		}
    
    		/**
    		 *      CLONE_STOPPED,         .
    		 *        TASK_STOPPED  ,   SIGSTOP     .
    		 */
    		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);
    		}
    
    		/**
    		 *     CLONE_STOPPED,   wake_up_new_task
    		 *                .
    		 *             CPU ,           (CLONE_VM    0).  ,              .
    		 *             .       :               ,                     .
    		 *   ,        CPU ,             .                 .
    		 */
    		if (!(clone_flags & CLONE_STOPPED))
    			wake_up_new_task(p, clone_flags);
    		else/*  CLONE_STOPPED     ,        TASK_STOPPED  。*/
    			p->state = TASK_STOPPED;
    
    		/**
    		 *         ,      PID       ptrace_message,   ptrace_notify
    		 * ptrace_notify         ,            SIGCHLD  .               debugger  .
    		 * dubugger      ptrace_message         PID.
    		 */
    		if (unlikely (trace)) {
    			current->ptrace_message = pid;
    			ptrace_notify ((trace << 8) | SIGTRAP);
    		}
    
    		/**
    		 *      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);
    		}
    	} else {
    		free_pidmap(pid);
    		pid = PTR_ERR(p);
    	}
    	return pid;
    }

    copy_process 함 수 는 다음 과 같 습 니 다.
    /**
     *                           
     *      do_fork  。      PID。
     */
    static task_t *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,
    				 int 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);
    
    	/*
    	 * Thread groups must share signals as well, and detached threads
    	 * can only be started up within the thread group.
    	 */
    	/**
    	 * 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);
    
    	/**
    	 *     security_task_create      security_task_alloc           。
    	 * LINUX2.6            ,   unix  ,            。
    	 */
    	retval = security_task_create(clone_flags);
    	if (retval)
    		goto fork_out;
    
    	retval = -ENOMEM;
    	/**
    	 *   dup_task_struct           。
    	 */
    	p = dup_task_struct(current);
    	if (!p)
    		goto fork_out;
    
    	/**
    	 *      current->sigal->rlim[RLIMIT_NPROC].rlim_cur     ,                 。
    	 *    ,      。  , root    。
    	 * p->user        ,p->user->processes            
    	 * xie.baoyou :      >=   >
    	 */
    	retval = -EAGAIN;
    	if (atomic_read(&p->user->processes) >=
    			p->signal->rlim[RLIMIT_NPROC].rlim_cur) {
    		/**
    		 *   ,   root        
    		 */
    		if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE) &&
    				p->user != &root_user)
    			goto bad_fork_free;
    	}
    
    	/**
    	 *   user        
    	 */
    	atomic_inc(&p->user->__count);
    	/**
    	 *            。
    	 */
    	atomic_inc(&p->user->processes);
    	get_group_info(p->group_info);
    
    	/**
    	 *           (nr_threads)    max_threads
    	 * max_threads               。     :   thread_info             
    	 *          1/8。  ,         /proc/sys/kernel/thread-max        。
    	 */
    	if (nr_threads >= max_threads)
    		goto bad_fork_cleanup_count;
    
    	/**
    	 *                               ,
    	 *            。
    	 */
    	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;
    
    	/**
    	 *                 。
    	 */
    
    	/**
    	 * did_exec      execve       ,   0
    	 */
    	p->did_exec = 0;
    	/**
    	 *          tsk_flags        。
    	 *     PF_SUPERPRIV。                    。
    	 *     PF_FORKNOEXEC  。           execve    。
    	 */
    	copy_flags(clone_flags, p);
    	/**
    	 *       pid 。
    	 */
    	p->pid = pid;
    	retval = -EFAULT;
    	/**
    	 *   CLONE_PARENT_SETTID     ,      PID     parent_tidptr         。
    	 * xie.baoyou:         pid = fork()   。
    	 */
    	if (clone_flags & CLONE_PARENT_SETTID)
    		if (put_user(p->pid, parent_tidptr))
    			goto bad_fork_cleanup;
    
    	p->proc_dentry = NULL;
    
    	/**
    	 *            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->it_real_value = 0;
    	p->it_real_incr = 0;
    	p->it_virt_value = cputime_zero;
    	p->it_virt_incr = cputime_zero;
    	p->it_prof_value = cputime_zero;
    	p->it_prof_incr = cputime_zero;
    	init_timer(&p->real_timer);
    	p->real_timer.data = (unsigned long) p;
    
    	p->utime = cputime_zero;
    	p->stime = cputime_zero;
    	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);
    
    	/**
    	 *             -1
    	 */
    	p->lock_depth = -1;		/* -1 = no lock */
    	do_posix_clock_monotonic_gettime(&p->start_time);
    	p->security = NULL;
    	p->io_context = NULL;
    	p->io_wait = NULL;
    	p->audit_context = NULL;
    #ifdef CONFIG_NUMA
     	p->mempolicy = mpol_copy(p->mempolicy);
     	if (IS_ERR(p->mempolicy)) {
     		retval = PTR_ERR(p->mempolicy);
     		p->mempolicy = NULL;
     		goto bad_fork_cleanup;
     	}
    #endif
    
    	p->tgid = p->pid;
    	if (clone_flags & CLONE_THREAD)
    		p->tgid = current->tgid;
    
    	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 */
    	/**
    	 * copy_semundo,copy_files,copy_fs,copy_sighand,copy_signal
    	 * copy_mm,copy_keys,copy_namespace        ,                      。
    	 *   clone_flags           。
    	 */
    	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;
    	/**
    	 *   copy_thread,   clone     CPU     (             )
    	 *            。  ,copy_thread eax         (  fork clone             )
    	 *     0。       thread.esp                。ret_from_fork      thread.eip 。
    	 *        IO    。              。
    	 *   ,  CLONE_SETTLS     ,       CLONE       tls              TLS 。
    	 */
    	retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs);
    	if (retval)
    		goto bad_fork_cleanup_namespace;
    
    	/**
    	 *   clone_flags       CLONE_CHILD_SETTID CLONE_CHILD_CLEARTID
    	 *   child_tidptr         set_child_tid clear_child_tid  。
    	 *       :               dhild_tidptr        
    	 *               。
    	 */
    	p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL;
    	p->clear_child_tid = (clone_flags & CLONE_CHILD_CLEARTID) ? child_tidptr: NULL;
    
    	/**
    	 *   TIF_SYSCALL_TRACE  。 ret_from_fork                     。
    	 *            ,          fork.
    	 */
    	clear_tsk_thread_flag(p, TIF_SYSCALL_TRACE);
    
    	p->parent_exec_id = p->self_exec_id;
    
    	/**
    	 *  clone_flags               tsk_exit_signal  。
    	 *  CLONE_THREAD     ,  exit_signal      -1。
    	 *       :      ,          ,                 。
    	 *            ,               。
    	 */
    	p->exit_signal = (clone_flags & CLONE_THREAD) ? -1 : (clone_flags & CSIGNAL);
    	p->pdeath_signal = 0;
    	p->exit_state = 0;
    
    	/**
    	 *   sched_fork                  。
    	 *             TASK_RUNNING,  thread_info   preempt_count     1,
    	 *       。
    	 *   ,        ,             。
    	 */
    	sched_fork(p);
    
    	p->group_leader = p;
    	INIT_LIST_HEAD(&p->ptrace_children);
    	INIT_LIST_HEAD(&p->ptrace_list);
    
    	write_lock_irq(&tasklist_lock);
    
    	p->cpus_allowed = current->cpus_allowed;
    	/**
    	 *        cpu  。
    	 */
    	set_task_cpu(p, smp_processor_id());
    
    	if (sigismember(¤t->pending.signal, SIGKILL)) {
    		write_unlock_irq(&tasklist_lock);
    		retval = -EINTR;
    		goto bad_fork_cleanup_namespace;
    	}
    
    	/**
    	 *             ,  CLONE_PARENT  CLONE_THREAD    
    	 *   current->real_parent   ,  ,               。
    	 */
    	if (clone_flags & (CLONE_PARENT|CLONE_THREAD))
    		p->real_parent = current->real_parent;
    	else
    		p->real_parent = current;
    	p->parent = p->real_parent;
    
    	if (clone_flags & CLONE_THREAD) {
    		spin_lock(¤t->sighand->siglock);
    		
    		if (current->signal->flags & SIGNAL_GROUP_EXIT) {
    			spin_unlock(¤t->sighand->siglock);
    			write_unlock_irq(&tasklist_lock);
    			retval = -EAGAIN;
    			goto bad_fork_cleanup_namespace;
    		}
    		p->group_leader = current->group_leader;
    
    		if (current->signal->group_stop_count > 0) {
    			
    			current->signal->group_stop_count++;
    			set_tsk_thread_flag(p, TIF_SIGPENDING);
    		}
    
    		spin_unlock(¤t->sighand->siglock);
    	}
    
    	/** 
    	 *            
    	 */
    	SET_LINKS(p);
    
    	/**
    	 * PT_PTRACED          ,  current->parent  tsk->parent,                 。
    	 */
    	if (unlikely(p->ptrace & PT_PTRACED))
    		__ptrace_link(p, current->parent);
    
    	/**
    	 *         PID  pidhash    。
    	 */
    	attach_pid(p, PIDTYPE_PID, p->pid);
    	attach_pid(p, PIDTYPE_TGID, p->tgid);
    
    	/**
    	 *               (CLONE_THREAD    0)
    	 */
    	if (thread_group_leader(p)) {
    		/**
    		 *            。
    		 */
    		attach_pid(p, PIDTYPE_PGID, process_group(p));
    		attach_pid(p, PIDTYPE_SID, p->signal->session);
    		if (p->pid)
    			__get_cpu_var(process_counts)++;
    	}
    
    	/**
    	 *   
    	 */
    	nr_threads++;
    	total_forks++;
    	write_unlock_irq(&tasklist_lock);
    	retval = 0;
    
    fork_out:
    	if (retval)
    		return ERR_PTR(retval);
    	return p;
    
    bad_fork_cleanup_namespace:
    	exit_namespace(p);
    bad_fork_cleanup_keys:
    	exit_keys(p);
    bad_fork_cleanup_mm:
    	if (p->mm)
    		mmput(p->mm);
    bad_fork_cleanup_signal:
    	exit_signal(p);
    bad_fork_cleanup_sighand:
    	exit_sighand(p);
    bad_fork_cleanup_fs:
    	exit_fs(p); /* blocking */
    bad_fork_cleanup_files:
    	exit_files(p); /* blocking */
    bad_fork_cleanup_semundo:
    	exit_sem(p);
    bad_fork_cleanup_audit:
    	audit_free(p);
    bad_fork_cleanup_security:
    	security_task_free(p);
    bad_fork_cleanup_policy:
    #ifdef CONFIG_NUMA
    	mpol_free(p->mempolicy);
    #endif
    bad_fork_cleanup:
    	if (p->binfmt)
    		module_put(p->binfmt->module);
    bad_fork_cleanup_put_domain:
    	module_put(p->thread_info->exec_domain->module);
    bad_fork_cleanup_count:
    	put_group_info(p->group_info);
    	atomic_dec(&p->user->processes);
    	free_uid(p->user);
    bad_fork_free:
    	free_task(p);
    	goto fork_out;
    }

    좋은 웹페이지 즐겨찾기