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对比:

原文地址:https://www.cnblogs.com/FORFISH/p/5188654.html