• 12.多进程程序的操作


    12.多进程程序的操作

    创建进程:

    创建进程的函数是fork():

    我们来看看帮助文件:man 2 fork:

    AME

    fork - create a child process

    SYNOPSIS

    #include <unistd.h>

    pid_t fork(void);

    DESCRIPTION

    fork() creates a new process by duplicating the calling process. The new process,

    referred to as the child, is an exact duplicate of the calling process, referred

    to as the parent, except for the following points:

    * The child has its own unique process ID, and this PID does not match the ID of

    any existing process group (setpgid(2)).

    * The child's parent process ID is the same as the parent's process ID.

    * The child does not inherit its parent's memory locks (mlock(2), mlockall(2)).

    * Process resource utilizations (getrusage(2)) and CPU time counters (times(2))

    are reset to zero in the child.

    * The child's set of pending signals is initially empty (sigpending(2)).

    * The child does not inherit semaphore adjustments from its parent (semop(2)).

    * The child does not inherit record locks from its parent (fcntl(2)).

    * The child does not inherit timers from its parent (setitimer(2), alarm(2),

    timer_create(2)).

    * The child does not inherit outstanding asynchronous I/O operations from its

    parent (aio_read(3), aio_write(3)), nor does it inherit any asynchronous I/O

    contexts from its parent (seeio_setup(2)).

    The process attributes in the preceding list are all specified in POSIX.1-2001.

    The parent and child also differ with respect to the following Linux-specific pro-

    cess attributes:

    * The child does not inherit directory change notifications (dnotify) from its

    parent (see the description of F_NOTIFY in fcntl(2)).

    * The prctl(2) PR_SET_PDEATHSIG setting is reset so that the child does not

    receive a signal when its parent terminates.

    * Memory mappings that have been marked with the madvise(2) MADV_DONTFORK flag

    are not inherited across a fork().

    * The termination signal of the child is always SIGCHLD (see clone(2)).

    Note the following further points:

    * The child process is created with a single thread — the one that called fork().

    The entire virtual address space of the parent is replicated in the child,

    including the states of mutexes, condition variables, and other pthreads

    objects; the use of pthread_atfork(3) may be helpful for dealing with problems

    that this can cause.

    * The child inherits copies of the parent's set of open file descriptors. Each

    file descriptor in the child refers to the same open file description (see

    open(2)) as the corresponding file descriptor in the parent. This means that

    the two descriptors share open file status flags, current file offset, and sig-

    nal-driven I/O attributes (see the description of F_SETOWN and F_SETSIG in

    fcntl(2)).

    * The child inherits copies of the parent's set of open message queue descriptors

    (see mq_overview(7)). Each descriptor in the child refers to the same open

    message queue description as the corresponding descriptor in the parent. This

    means that the two descriptors share the same flags (mq_flags).

    * The child inherits copies of the parent's set of open directory streams (see

    opendir(3)). POSIX.1-2001 says that the corresponding directory streams in the

    parent and child may share the directory stream positioning; on Linux/glibc

    they do not.

    RETURN VALUE

    On success, the PID of the child process is returned in the parent, and 0 is

    returned in the child. On failure, -1 is returned in the parent, no child process

    is created, and errno is set appropriately.

    ERRORS

    EAGAIN fork() cannot allocate sufficient memory to copy the parent's page tables

    and allocate a task structure for the child.

    EAGAIN It was not possible to create a new process because the caller's

    RLIMIT_NPROC resource limit was encountered. To exceed this limit, the

    process must have either the CAP_SYS_ADMIN or the CAP_SYS_RESOURCE capabil-

    ity.

    ENOMEM fork() failed to allocate the necessary kernel structures because memory is

    tight.

    CONFORMING TO

    SVr4, 4.3BSD, POSIX.1-2001.

    NOTES

    Under Linux, fork() is implemented using copy-on-write pages, so the only penalty

    that it incurs is the time and memory required to duplicate the parent's page

    tables, and to create a unique task structure for the child.

    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. The glibc wrapper invokes any fork handlers that have been established

    using pthread_atfork(3).

    EXAMPLE

    See pipe(2) and wait(2).

    SEE ALSO

    clone(2), execve(2), setrlimit(2), unshare(2), vfork(2), wait(2), daemon(3), capa-

    bilities(7), credentials(7)

    COLOPHON

    This page is part of release 3.22 of the Linux man-pages project. A description

    of the project, and information about reporting bugs, can be found at

    http://www.kernel.org/doc/man-pages/.

    创建一个进程的函数是fork()。函数的原型:

    pid_t fork(void);

    该函数的功能是创建一个子进程。需要的头文件:unistd.h。

    该函数的返回值:在成功的情况下返回两个:

    1. 在父进程中返回子进程的PID。
    2. 在子进程中返回的是0.

    在失败的情况下:返回的是-1.

    该函数是没有参数的。

    实例:multiprocess.c:

    #include <unistd.h>

    #include <stdio.h>

    void main(){

        fork();

        printf("process is me ");

        exit(0);

    }

    运行的结果:

    我们从运行的结果看到,程序运行一次,printf打印了两次信息。这就是fork函数产生的。当一个进程调用了fork函数后,会产生一个子进程,子进程开始运行的位置是fork函数之后的第一行开始运行。该子进程的参数是复制父进程的初始参数的参数。

    我们从上面知道,进程调用fork函数会返回两个值:父进程返回子进程的PID,子进程返回0.由此我们可以根据他们返回值的不同,可以使他们去实现不同的功能。

    我们来看下面的例子:

    muitiprocess1.c:

    #include <unistd.h>

    #include <stdio.h>

    void main(){

        pid_t pid;

        pid = fork();

        if(pid > 0){

            printf("hello father process! ");

            exit(0);

        }

        else{

            printf("hello child process! ");

            exit(0);

        }

    }

    运行的结果:

    这就实现了父子进程做不同的事情。

    另外一个创建进程的函数是:vfork:

    查看帮助文档:man 2 vfork:

    NAME

    vfork - create a child process and block parent

    SYNOPSIS

    #include <sys/types.h>

    #include <unistd.h>

    pid_t vfork(void);

    Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

    vfork(): _BSD_SOURCE || _XOPEN_SOURCE >= 500

    DESCRIPTION

    Standard Description

    (From POSIX.1) The vfork() function has the same effect as fork(2), except that

    the behavior is undefined if the process created by vfork() either modifies any

    data other than a variable of type pid_t used to store the return value from

    vfork(), or returns from the function in which vfork() was called, or calls any

    other function before successfully calling _exit(2) or one of the exec(3) family

    of functions.

    Linux Description

    vfork(), just like fork(2), creates a child process of the calling process. For

    details and return value and errors, see fork(2).

    vfork() is a special case of clone(2). It is used to create new processes without

    copying the page tables of the parent process. It may be useful in performance-

    sensitive applications where a child will be created which then immediately issues

    an execve(2).

    vfork() differs from fork(2) in that the parent is suspended until the child ter-

    minates (either normally, by calling _exit(2), or abnormally, after delivery of a

    fatal signal), or it makes a call to execve(2). Until that point, the child

    shares all memory with its parent, including the stack. The child must not return

    from the current function or call exit(3), but may call _exit(2).

    Signal handlers are inherited, but not shared. Signals to the parent arrive after

    the child releases the parent's memory (i.e., after the child terminates or calls

    execve(2)).

    Historic Description

    Under Linux, fork(2) is implemented using copy-on-write pages, so the only penalty

    incurred by fork(2) is the time and memory required to duplicate the parent's page

    tables, and to create a unique task structure for the child. However, in the bad

    old days a fork(2) would require making a complete copy of the caller's data

    space, often needlessly, since usually immediately afterwards an exec(3) is done.

    Thus, for greater efficiency, BSD introduced the vfork() system call, which did

    not fully copy the address space of the parent process, but borrowed the parent's

    memory and thread of control until a call to execve(2) or an exit occurred. The

    parent process was suspended while the child was using its resources. The use of

    vfork() was tricky: for example, not modifying data in the parent process depended

    on knowing which variables are held in a register.

    CONFORMING TO

    4.3BSD, POSIX.1-2001. POSIX.1-2008 removes the specification of vfork(). The

    requirements put on vfork() by the standards are weaker than those put on fork(2),

    so an implementation where the two are synonymous is compliant. In particular,

    the programmer cannot rely on the parent remaining blocked until the child either

    terminates or calls execve(2), and cannot rely on any specific behavior with

    respect to shared memory.

    NOTES

    Linux Notes

    Fork handlers established using pthread_atfork(3) are not called when a multi-

    threaded program employing the NPTL threading library calls vfork(). Fork han-

    dlers are called in this case in a program using the LinuxThreads threading

    library. (See pthreads(7) for a description of Linux threading libraries.)

    History

    The vfork() system call appeared in 3.0BSD. In 4.4BSD it was made synonymous to

    fork(2) but NetBSD introduced it again, cf. http://www.netbsd.org/Documenta-

    tion/kernel/vfork.html . In Linux, it has been equivalent to fork(2) until

    2.2.0-pre6 or so. Since 2.2.0-pre9 (on i386, somewhat later on other architec-

    tures) it is an independent system call. Support was added in glibc 2.0.112.

    BUGS

    It is rather unfortunate that Linux revived this specter from the past. The BSD

    man page states: "This system call will be eliminated when proper system sharing

    mechanisms are implemented. Users should not depend on the memory sharing seman-

    tics of vfork() as it will, in that case, be made synonymous to fork(2)."

    Details of the signal handling are obscure and differ between systems. The BSD

    man page states: "To avoid a possible deadlock situation, processes that are chil-

    dren in the middle of a vfork() are never sent SIGTTOU or SIGTTIN signals; rather,

    output or ioctls are allowed and input attempts result in an end-of-file indica-

    tion."

    SEE ALSO

    clone(2), execve(2), fork(2), unshare(2), wait(2)

    COLOPHON

    This page is part of release 3.22 of the Linux man-pages project. A description

    of the project, and information about reporting bugs, can be found at

    http://www.kernel.org/doc/man-pages/.

    Vfork的函数的原型是:

    pid_t vfork(void);

    该函数的功能是创建一个子进程,并阻塞父进程。这就决定了在调用vfork函数创建子进程的时候,先执行子进程,后再执行父进程。而上面的fork函数则次序是不一定谁先执行。

    需要的头文件:

    <sys/types.h> <unistd.h>

    返回值是:成功:

    1. 在父进程中返回子进程的PID。
    2. 在子进程中返回的是0.

    失败则返回-1.

    没有参数。

    实例:multiprocess2.c:

    #include <unistd.h>

    #include <stdio.h>

    void main(){

        pid_t pid;

        pid = vfork();

        if(pid > 0){

            printf("hello father process! ");

            exit(0);

        }

        else{

            printf("hello child process! ");

            exit(0);

        }

    }

    运行的结果:

    这就是vfork的一个例子,我们看到结果是先执行子进程后执行父进程。这是fork和vfork的一个不同点。还有一个不同点事:fork的父子进程使用不同但是初始条件相同的变量环境,即是栈,就是他们的各自的变量环境,初始条件是相同的。而vfork则是父子进程共用一个变量环境。

    Count.c:

    #include <unistd.h>

    #include <stdio.h>

    void main(){

        pid_t pid;

        int count = 0;

        pid = fork();

        count = count + 1;

        printf("count is %d ",count);

        exit(0);

    }

    运行的结果:

    实例:vcount.c:

    #include <unistd.h>

    #include <stdio.h>

    void main(){

        pid_t pid;

        int count = 0;

        pid = vfork();

        count = count + 1;

        printf("count is %d ",count);

        exit(0);

    }

    程序运行的结果:

    这跟我们fork和vfork的区别。

    进程退出:

    帮助文档的信息:man 2 exit:

    NAME

    _exit, _Exit - terminate the calling process

    SYNOPSIS

    #include <unistd.h>

    void _exit(int status);

    #include <stdlib.h>

    void _Exit(int status);

    Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

    _Exit(): _XOPEN_SOURCE >= 600 || _ISOC99_SOURCE; or cc -std=c99

    DESCRIPTION

    The function _exit() terminates the calling process "immediately". Any open file

    descriptors belonging to the process are closed; any children of the process are

    inherited by process 1, init, and the process's parent is sent a SIGCHLD signal.

    The value status is returned to the parent process as the process's exit status,

    and can be collected using one of the wait(2) family of calls.

    The function _Exit() is equivalent to _exit().

    RETURN VALUE

    These functions do not return.

    CONFORMING TO

    SVr4, POSIX.1-2001, 4.3BSD. The function _Exit() was introduced by C99.

    NOTES

    For a discussion on the effects of an exit, the transmission of exit status, zom-

    bie processes, signals sent, etc., see exit(3).

    The function _exit() is like exit(3), but does not call any functions registered

    with atexit(3) or on_exit(3). Whether it flushes standard I/O buffers and removes

    temporary files created with tmpfile(3) is implementation-dependent. On the other

    hand, _exit() does close open file descriptors, and this may cause an unknown

    delay, waiting for pending output to finish. If the delay is undesired, it may be

    useful to call functions like tcflush(3) before calling _exit(). Whether any

    pending I/O is canceled, and which pending I/O may be canceled upon _exit(), is

    implementation-dependent.

    In glibc up to version 2.3, the _exit() wrapper function invoked the kernel system

    call of the same name. Since glibc 2.3, the wrapper function invokes

    exit_group(2), in order to terminate all of the threads in a process.

    SEE ALSO

    execve(2), exit_group(2), fork(2), kill(2), wait(2), wait4(2), waitpid(2),

    atexit(3), exit(3), on_exit(3), termios(3)

    COLOPHON

    This page is part of release 3.22 of the Linux man-pages project. A description

    of the project, and information about reporting bugs, can be found at

    http://www.kernel.org/doc/man-pages/.

    在父进程中可以使用return 和exit来退出进程:

    成功:return 0 或 exit(0).如果异常则return 1 或exit(1).

    return是语言级别的,不管你用不用都是存在的,它表示了调用堆栈的返回;而exit是操作系统中系统调用级别的,它表示了一个进程的结束。当然如果是mian函数中return(0)跟exit(0)是等效的。

    Multiprocess1.c:

    #include <unistd.h>

    #include <stdio.h>

    void main(){

        pid_t pid;

        pid = fork();

        if(pid > 0){

            printf("hello father process! ");

            return 0;

            //exit(0);

        }

        else{

            printf("hello child process! ");

            exit(0);

        }

    }

    运行的结果:

    上面的程序中使用了return,正常退出父进程。

    Multiprocess2.c:

    #include <unistd.h>

    #include <stdio.h>

    void main(){

        pid_t pid;

        pid = fork();

        if(pid > 0){

            printf("hello father process! ");

            return 0;

            //exit(0);

        }

        else{

            printf("hello child process! ");

            return 0;

            //exit(0);

        }

    }

    运行的结果:

    跟父进程等效。

    线程等待:

    Man的信息:man 2 wait:

    NAME

    wait, waitpid, waitid - wait for process to change state

    SYNOPSIS

    #include <sys/types.h>

    #include <sys/wait.h>

    pid_t wait(int *status);

    pid_t waitpid(pid_t pid, int *status, int options);

    int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);

    Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

    waitid(): _SVID_SOURCE || _XOPEN_SOURCE

    DESCRIPTION

    All of these system calls are used to wait for state changes in a child of the

    calling process, and obtain information about the child whose state has

    changed. A state change is considered to be: the child terminated; the child

    was stopped by a signal; or the child was resumed by a signal. In the case of

    a terminated child, performing a wait allows the system to release the

    resources associated with the child; if a wait is not performed, then the ter-

    minated child remains in a "zombie" state (see NOTES below).

    If a child has already changed state, then these calls return immediately.

    Otherwise they block until either a child changes state or a signal handler

    interrupts the call (assuming that system calls are not automatically restarted

    using the SA_RESTART flag of sigaction(2)). In the remainder of this page, a

    child whose state has changed and which has not yet been waited upon by one of

    these system calls is termed waitable.

    wait() and waitpid()

    The wait() system call suspends execution of the calling process until one of

    its children terminates. The call wait(&status) is equivalent to:

    waitpid(-1, &status, 0);

    The waitpid() system call suspends execution of the calling process until a

    child specified by pid argument has changed state. By default, waitpid() waits

    only for terminated children, but this behavior is modifiable via the options

    argument, as described below.

    The value of pid can be:

    < -1 meaning wait for any child process whose process group ID is equal to

    the absolute value of pid.

    -1 meaning wait for any child process.

    0 meaning wait for any child process whose process group ID is equal to

    that of the calling process.

    > 0 meaning wait for the child whose process ID is equal to the value of

    pid.

    The value of options is an OR of zero or more of the following constants:

    WNOHANG return immediately if no child has exited.

    WUNTRACED also return if a child has stopped (but not traced via ptrace(2)).

    Status for traced children which have stopped is provided even if

    this option is not specified.

    WCONTINUED (since Linux 2.6.10)

    also return if a stopped child has been resumed by delivery of SIG-

    CONT.

    (For Linux-only options, see below.)

    If status is not NULL, wait() and waitpid() store status information in the int

    to which it points. This integer can be inspected with the following macros

    (which take the integer itself as an argument, not a pointer to it, as is done

    in wait() and waitpid()!):

    WIFEXITED(status)

    returns true if the child terminated normally, that is, by calling

    exit(3) or _exit(2), or by returning from main().

    WEXITSTATUS(status)

    returns the exit status of the child. This consists of the least sig-

    nificant 8 bits of the status argument that the child specified in a

    call to exit(3) or _exit(2) or as the argument for a return statement in

    main(). This macro should only be employed if WIFEXITED returned true.

    WIFSIGNALED(status)

    returns true if the child process was terminated by a signal.

    WTERMSIG(status)

    returns the number of the signal that caused the child process to termi-

    nate. This macro should only be employed if WIFSIGNALED returned true.

    WCOREDUMP(status)

    returns true if the child produced a core dump. This macro should only

    be employed if WIFSIGNALED returned true. This macro is not specified

    in POSIX.1-2001 and is not available on some Unix implementations (e.g.,

    AIX, SunOS). Only use this enclosed in #ifdef WCOREDUMP ... #endif.

    WIFSTOPPED(status)

    returns true if the child process was stopped by delivery of a signal;

    this is only possible if the call was done using WUNTRACED or when the

    child is being traced (see ptrace(2)).

    WSTOPSIG(status)

    returns the number of the signal which caused the child to stop. This

    macro should only be employed if WIFSTOPPED returned true.

    WIFCONTINUED(status)

    (since Linux 2.6.10) returns true if the child process was resumed by

    delivery of SIGCONT.

    waitid()

    The waitid() system call (available since Linux 2.6.9) provides more precise

    control over which child state changes to wait for.

    The idtype and id arguments select the child(ren) to wait for, as follows:

    idtype == P_PID

    Wait for the child whose process ID matches id.

    idtype == P_PGID

    Wait for any child whose process group ID matches id.

    idtype == P_ALL

    Wait for any child; id is ignored.

    The child state changes to wait for are specified by ORing one or more of the

    following flags in options:

    WEXITED Wait for children that have terminated.

    WSTOPPED Wait for children that have been stopped by delivery of a signal.

    WCONTINUED Wait for (previously stopped) children that have been resumed by

    delivery of SIGCONT.

    The following flags may additionally be ORed in options:

    WNOHANG As for waitpid().

    WNOWAIT Leave the child in a waitable state; a later wait call can be used

    to again retrieve the child status information.

    Upon successful return, waitid() fills in the following fields of the siginfo_t

    structure pointed to by infop:

    si_pid The process ID of the child.

    si_uid The real user ID of the child. (This field is not set on most

    other implementations.)

    si_signo Always set to SIGCHLD.

    si_status Either the exit status of the child, as given to _exit(2) (or

    exit(3)), or the signal that caused the child to terminate, stop,

    or continue. The si_code field can be used to determine how to

    interpret this field.

    si_code Set to one of: CLD_EXITED (child called _exit(2)); CLD_KILLED

    (child killed by signal); CLD_DUMPED (child killed by signal, and

    dumped core); CLD_STOPPED (child stopped by signal); CLD_TRAPPED

    (traced child has trapped); or CLD_CONTINUED (child continued by

    SIGCONT).

    If WNOHANG was specified in options and there were no children in a waitable

    state, then waitid() returns 0 immediately and the state of the siginfo_t

    structure pointed to by infop is unspecified. To distinguish this case from

    that where a child was in a waitable state, zero out the si_pid field before

    the call and check for a non-zero value in this field after the call returns.

    RETURN VALUE

    wait(): on success, returns the process ID of the terminated child; on error,

    -1 is returned.

    waitpid(): on success, returns the process ID of the child whose state has

    changed; if WNOHANG was specified and one or more child(ren) specified by pid

    exist, but have not yet changed state, then 0 is returned. On error, -1 is

    returned.

    waitid(): returns 0 on success or if WNOHANG was specified and no child(ren)

    specified by id has yet changed state; on error, -1 is returned. Each of these

    calls sets errno to an appropriate value in the case of an error.

    ERRORS

    ECHILD (for wait()) The calling process does not have any unwaited-for chil-

    dren.

    ECHILD (for waitpid() or waitid()) The process specified by pid (waitpid()) or

    idtype and id (waitid()) does not exist or is not a child of the calling

    process. (This can happen for one's own child if the action for SIGCHLD

    is set to SIG_IGN. See also the Linux Notes section about threads.)

    EINTR WNOHANG was not set and an unblocked signal or a SIGCHLD was caught; see

    signal(7).

    EINVAL The options argument was invalid.

    CONFORMING TO

    SVr4, 4.3BSD, POSIX.1-2001.

    NOTES

    A child that terminates, but has not been waited for becomes a "zombie". The

    kernel maintains a minimal set of information about the zombie process (PID,

    termination status, resource usage information) in order to allow the parent to

    later perform a wait to obtain information about the child. As long as a zom-

    bie is not removed from the system via a wait, it will consume a slot in the

    kernel process table, and if this table fills, it will not be possible to cre-

    ate further processes. If a parent process terminates, then its "zombie" chil-

    dren (if any) are adopted by init(8), which automatically performs a wait to

    remove the zombies.

    POSIX.1-2001 specifies that if the disposition of SIGCHLD is set to SIG_IGN or

    the SA_NOCLDWAIT flag is set for SIGCHLD (see sigaction(2)), then children that

    terminate do not become zombies and a call to wait() or waitpid() will block

    until all children have terminated, and then fail with errno set to ECHILD.

    (The original POSIX standard left the behavior of setting SIGCHLD to SIG_IGN

    unspecified. Note that even though the default disposition of SIGCHLD is

    "ignore", explicitly setting the disposition to SIG_IGN results in different

    treatment of zombie process children.) Linux 2.6 conforms to this specifica-

    tion. However, Linux 2.4 (and earlier) does not: if a wait() or waitpid() call

    is made while SIGCHLD is being ignored, the call behaves just as though SIGCHLD

    were not being ignored, that is, the call blocks until the next child termi-

    nates and then returns the process ID and status of that child.

    Linux Notes

    In the Linux kernel, a kernel-scheduled thread is not a distinct construct from

    a process. Instead, a thread is simply a process that is created using the

    Linux-unique clone(2) system call; other routines such as the portable

    pthread_create(3) call are implemented using clone(2). Before Linux 2.4, a

    thread was just a special case of a process, and as a consequence one thread

    could not wait on the children of another thread, even when the latter belongs

    to the same thread group. However, POSIX prescribes such functionality, and

    since Linux 2.4 a thread can, and by default will, wait on children of other

    threads in the same thread group.

    The following Linux-specific options are for use with children created using

    clone(2); they cannot be used with waitid():

    __WCLONE

    Wait for "clone" children only. If omitted then wait for "non-clone"

    children only. (A "clone" child is one which delivers no signal, or a

    signal other than SIGCHLD to its parent upon termination.) This option

    is ignored if __WALL is also specified.

    __WALL (since Linux 2.4)

    Wait for all children, regardless of type ("clone" or "non-clone").

    __WNOTHREAD (since Linux 2.4)

    Do not wait for children of other threads in the same thread group.

    This was the default before Linux 2.4.

    EXAMPLE

    The following program demonstrates the use of fork(2) and waitpid(). The pro-

    gram creates a child process. If no command-line argument is supplied to the

    program, then the child suspends its execution using pause(2), to allow the

    user to send signals to the child. Otherwise, if a command-line argument is

    supplied, then the child exits immediately, using the integer supplied on the

    command line as the exit status. The parent process executes a loop that moni-

    tors the child using waitpid(), and uses the W*() macros described above to

    analyze the wait status value.

    The following shell session demonstrates the use of the program:

    $ ./a.out &

    Child PID is 32360

    [1] 32359

    $ kill -STOP 32360

    stopped by signal 19

    $ kill -CONT 32360

    continued

    $ kill -TERM 32360

    killed by signal 15

    [1]+ Done ./a.out

    $

    Program source

    #include <sys/wait.h>

    #include <stdlib.h>

    #include <unistd.h>

    #include <stdio.h>

    int

    main(int argc, char *argv[])

    {

    pid_t cpid, w;

    int status;

    cpid = fork();

    if (cpid == -1) {

    perror("fork");

    exit(EXIT_FAILURE);

    }

    if (cpid == 0) { /* Code executed by child */

    printf("Child PID is %ld ", (long) getpid());

    if (argc == 1)

    pause(); /* Wait for signals */

    _exit(atoi(argv[1]));

    } else { /* Code executed by parent */

    do {

    w = waitpid(cpid, &status, WUNTRACED | WCONTINUED);

    if (w == -1) {

    perror("waitpid");

    exit(EXIT_FAILURE);

    }

    if (WIFEXITED(status)) {

    printf("exited, status=%d ", WEXITSTATUS(status));

    } else if (WIFSIGNALED(status)) {

    printf("killed by signal %d ", WTERMSIG(status));

    } else if (WIFSTOPPED(status)) {

    printf("stopped by signal %d ", WSTOPSIG(status));

    } else if (WIFCONTINUED(status)) {

    printf("continued ");

    }

    } while (!WIFEXITED(status) && !WIFSIGNALED(status));

    exit(EXIT_SUCCESS);

    }

    }

    SEE ALSO

    _exit(2), clone(2), fork(2), kill(2), ptrace(2), sigaction(2), signal(2),

    wait4(2), pthread_create(3), credentials(7), signal(7)

    COLOPHON

    This page is part of release 3.22 of the Linux man-pages project. A descrip-

    tion of the project, and information about reporting bugs, can be found at

    http://www.kernel.org/doc/man-pages/.

    线程的等待函数的原型:

    pid_t wait(int *status);

    该函数的功能是挂起调用的进程,直到其子进程结束。

    需要的头文件:

    <sys/types.h><sys/wait.h>

    该函数的返回值:成功则是返回等待期间执行的那个子进程的ID。

    失败则是返回-1.

    该函数有一个参数:记录子进程的退出状态。

    实例:wait.c:

    #include <unistd.h>

    #include <stdio.h>

    #include <sys/types.h>

    #include <sys/wait.h>

    void main(){

        pid_t pid;

        pid = fork();

        if(pid > 0){

            wait(NULL);

            printf("hello father process! ");

            exit(0);

        }

        else{

            printf("hello child process! ");

            exit(0);

        }

    }

    运行结果:

    程序执行:

    程序执行的函数是以函数族的形式存在:

    函数名execl:

    查看帮助文档:man execl:

    NAME

    execl, execlp, execle, execv, execvp - execute a file

    SYNOPSIS

    #include <unistd.h>

    extern char **environ;

    int execl(const char *path, const char *arg, ...);

    int execlp(const char *file, const char *arg, ...);

    int execle(const char *path, const char *arg,

    ..., char * const envp[]);

    int execv(const char *path, char *const argv[]);

    int execvp(const char *file, char *const argv[]);

    DESCRIPTION

    The exec() family of functions replaces the current process image with a new

    process image. The functions described in this manual page are front-ends for

    execve(2). (See the manual page for execve(2) for further details about the

    replacement of the current process image.)

    The initial argument for these functions is the pathname of a file which is to

    be executed.

    The const char *arg and subsequent ellipses in the execl(), execlp(), and exe-

    cle() functions can be thought of as arg0, arg1, ..., argn. Together they

    describe a list of one or more pointers to null-terminated strings that repre-

    sent the argument list available to the executed program. The first argument,

    by convention, should point to the filename associated with the file being exe-

    cuted. The list of arguments must be terminated by a NULL pointer, and, since

    these are variadic functions, this pointer must be cast (char *) NULL.

    The execv() and execvp() functions provide an array of pointers to null-termi-

    nated strings that represent the argument list available to the new program.

    The first argument, by convention, should point to the filename associated with

    the file being executed. The array of pointers must be terminated by a NULL

    pointer.

    The execle() function also specifies the environment of the executed process by

    following the NULL pointer that terminates the list of arguments in the argu-

    ment list or the pointer to the argv array with an additional argument. This

    additional argument is an array of pointers to null-terminated strings and must

    be terminated by a NULL pointer. The other functions take the environment for

    the new process image from the external variable environ in the current pro-

    cess.

    Special semantics for execlp() and execvp()

    The functions execlp() and execvp() will duplicate the actions of the shell in

    searching for an executable file if the specified filename does not contain a

    slash (/) character. The search path is the path specified in the environment

    by the PATH variable. If this variable isn't specified, the default path

    ":/bin:/usr/bin" is used. In addition, certain errors are treated specially.

    If permission is denied for a file (the attempted execve(2) failed with the

    error EACCES), these functions will continue searching the rest of the search

    path. If no other file is found, however, they will return with errno set to

    EACCES.

    If the header of a file isn't recognized (the attempted execve(2) failed with

    the error ENOEXEC), these functions will execute the shell (/bin/sh) with the

    path of the file as its first argument. (If this attempt fails, no further

    searching is done.)

    RETURN VALUE

    If any of the exec() functions returns, an error will have occurred. The

    return value is -1, and errno will be set to indicate the error.

    ERRORS

    All of these functions may fail and set errno for any of the errors specified

    for the library function execve(2).

    CONFORMING TO

    POSIX.1-2001.

    NOTES

    On some other systems the default path (used when the environment does not con-

    tain the variable PATH) has the current working directory listed after /bin and

    /usr/bin, as an anti-Trojan-horse measure. Linux uses here the traditional

    "current directory first" default path.

    The behavior of execlp() and execvp() when errors occur while attempting to

    execute the file is historic practice, but has not traditionally been docu-

    mented and is not specified by the POSIX standard. BSD (and possibly other

    systems) do an automatic sleep and retry if ETXTBSY is encountered. Linux

    treats it as a hard error and returns immediately.

    Traditionally, the functions execlp() and execvp() ignored all errors except

    for the ones described above and ENOMEM and E2BIG, upon which they returned.

    They now return if any error other than the ones described above occurs.

    SEE ALSO

    sh(1), execve(2), fork(2), ptrace(2), fexecve(3), environ(7)

    COLOPHON

    This page is part of release 3.22 of the Linux man-pages project. A descrip-

    tion of the project, and information about reporting bugs, can be found at

    http://www.kernel.org/doc/man-pages/.

    该函数的一个原型:

    int execl(const char *path, const char *arg, ...);

    该函数的功能是运行可执行文件。

    需要的头文件:<unistd.h>

    返回值:成功不返回。

    失败:-1

    Pathname:可执行文件的路径。

    arg:可执行文件需要的参数。以NULL来结束参数。

    execl.c:

    #include <unistd.h>

    #include <stdio.h>

    #include <sys/types.h>

    #include <sys/wait.h>

    void main(){

        pid_t pid;

        pid = fork();

        if(pid > 0){

            wait(NULL);

            printf("hello father process! ");

            exit(0);

        }

        else{

            execl("/bin/ls","ls","/home/forfish",NULL);

            printf("hello child process! ");

            exit(0);

        }

    }

    运行的结果:

    可以看到他列出了和直接使用ls命令一个的效果。但是没有打印execl后面的打印信息。这是因为当调用execl的时候,他会覆盖掉原有的代码。也就是说,在该程序中,由于调用了execl,结果是子进程的代码被execl的/bin/ls的代码覆盖掉。注意不是产生新进程,只是覆盖。所以,子进程没有打印信息。

    Fork与execl对比:

  • 相关阅读:
    Ubuntu创建VLAN接口配置
    Ubuntu配置网桥方法
    Ubuntu重启网卡的三种方法
    kvm存储池和存储卷
    ubuntu如何切换到root用户
    修改Ubuntu默认apt下载源
    Java之泛型<T> T与T的用法(转载)
    Java关键字(一)——instanceof(转载)-BeanCopier相关
    java代码之美(12)---CollectionUtils工具类(转载)
    Lombok-注解@Slf4j的使用
  • 原文地址:https://www.cnblogs.com/FORFISH/p/5188654.html
Copyright © 2020-2023  润新知