2017年9月22日 星期五

建立行程: system / fork / clone / execl

system - execute a shell command (3)
int system(const char *command);

The system() library function uses fork(2) to create a child process that executes the shell command specified in command
using execl(3) as follows:
           execl("/bin/sh", "sh", "-c", command, (char *) 0);
system() returns after the command has been completed.
The main cost of system() is inefficiency: additional system calls are required to create the process that runs the shell and to execute the shell.

During execution of the command, SIGCHLD will be blocked, and SIGINT and SIGQUIT will be ignored, in the process that calls system().

fork - create a child process (2)
pid_t fork(void);
clone, __clone2 - create a child process (2)
int clone(int (*fn)(void *), void *child_stack,
                 int flags, void *arg, ...
                 /* pid_t *ptid, void *newtls, pid_t *ctid */ );
C library/kernel differences
      Since version 2.3.3, rather than invoking the kernel's fork() system call, the glibc fork() wrapper that is provided as part of the NPTL threading implementation invokes clone(2) with flags that provide the same effect as the traditional system call.

execl, execlp, execle, execv, execvp, execvpe - execute a file (3)
The exec() family of functions replaces the current process image with a new process image.
int execl(const char *path, const char *arg, ... /* (char  *) NULL */);
int execlp(const char *file, const char *arg, ... /* (char  *) NULL */);
int execle(const char *path, const char *arg, ... /*, (char *) NULL, char * const envp[] */);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[], char *const envp[]);

execve - execute program (2)
int execve(const char *filename, char *const argv[], char *const envp[]);

strace ./create_process
//system()
rt_sigaction(SIGINT, {SIG_IGN, [], SA_RESTORER, 0x7f5cc398d7f0}, {SIG_DFL, [], 0}, 8) = 0
rt_sigaction(SIGQUIT, {SIG_IGN, [], SA_RESTORER, 0x7f5cc398d7f0}, {SIG_DFL, [], 0}, 8) = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
clone(child_stack=0, flags=CLONE_PARENT_SETTID|SIGCHLD, parent_tidptr=0x7ffc96f5c9ac) = 3576
wait4(3576,
...
[{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 3576
rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7f5cc398d7f0}, NULL, 8) = 0
rt_sigaction(SIGQUIT, {SIG_DFL, [], SA_RESTORER, 0x7f5cc398d7f0}, NULL, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=3576, si_uid=1001, si_status=0, si_utime=0, si_stime=0} ---

//fork()
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f5cc3f379d0) = 3823
wait4(3823,
...
[{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 3823
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=3823, si_uid=1001, si_status=0, si_utime=0, si_stime=0} ---

//execl
execve("./cmd_process", ["cmd_process"], [/* 20 vars */]) = 0

sigaction, rt_sigaction - examine and change a signal action
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
sigprocmask, rt_sigprocmask - examine and change blocked signals
/* Prototype for the underlying system call */
int rt_sigprocmask(int how, const kernel_sigset_t *set, kernel_sigset_t *oldset, size_t sigsetsize);
    SIG_BLOCK
        The set of blocked signals is the union of the current set and the set argument.


kernel/fork.c
/*
 *  Ok, this is the main fork-routine.
 *
 * It copies the process, and if successful kick-starts
 * it and waits for it to finish using the VM if required.
 */
long _do_fork(unsigned long clone_flags,
          unsigned long stack_start,
          unsigned long stack_size,
          int __user *parent_tidptr,
          int __user *child_tidptr,
          unsigned long tls)
{
    struct task_struct *p;
...
    p = copy_process(clone_flags, stack_start, stack_size,
             child_tidptr, NULL, trace, tls, NUMA_NO_NODE);
...
}

/*
 * This creates a new process as a copy of the old one,
 * but does not actually start it yet.
 *
 * It copies the registers, and all the appropriate
 * parts of the process environment (as per the clone
 * flags). The actual kick-off is left to the caller.
 */
static __latent_entropy struct task_struct *copy_process(
                    unsigned long clone_flags,
                    unsigned long stack_start,
                    unsigned long stack_size,
                    int __user *child_tidptr,
                    struct pid *pid,
                    int trace,
                    unsigned long tls,
                    int node)



參考資料:
範例程式
https://github.com/bruce690813/example/tree/master/test_create_process
補充資料 poepn


沒有留言:

張貼留言