Linux进程控制Part2
文章目录
- Linux进程控制Part2
- Fork()函数详解
- 简单描述
- fork函数的使用
- 进程退出的方式
- _exit函数
- exit函数
- return 退出
- 进程等待
- 进程等待的方法
- kill的使用
- 进程替换
- 简单描述
- 命名原理
- END
Fork()函数详解
FORK(2) Linux Programmer’s Manual FORK(2)
NAME
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 asynchro‐
nous I/O contexts from its parent (see io_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 process 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.
* The default timer slack value is set to the parent's current timer slack value. See the description of PR_SET_TIMERSLACK in prctl(2).
* 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)).
* The port access permission bits set by ioperm(2) are not inherited by the child; the child must turn on any bits that it requires using iop‐
erm(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 descrip‐
tion (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 signal-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 capability.
ENOMEM fork() failed to allocate the necessary kernel structures because memory is tight.
ENOSYS fork() is not supported on this platform (for example, hardware without a Memory-Management Unit).
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. (A call to fork() is equivalent to a call
to clone(2) specifying flags as just SIGCHLD.) 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), exit(2), setrlimit(2), unshare(2), vfork(2), wait(2), daemon(3), capabilities(7), credentials(7)
COLOPHON
This page is part of release 3.53 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/.
Linux 2013-03-12 FORK(2)
以上为LinuxMan2号手册里的fork函数详解
简单描述
简单来讲就是使用fork()创建一个子进程,主进程返回子进程的pid,子进程返回的pid值为0。
fork函数的使用
1、 fork生成的子进程和父进程共享代码,他们的数据也是共享的,但是当一方发生写入时,就会通过写时拷贝来保证数据的安全性。
2、 子进程需要父进程等待,不然会导致僵尸进程发生内存泄漏。
eg:
#include <iostream>
#include<unistd.h>
using namespace std;
int main()
{
pid_t pid=fork();
if(pid==0)
{
cout<<"I am a Child Process"<<"pid:"<<getpid()<<"ppid:"<<getppid()<<endl;
cout<<endl;
}
else
{
while(1)
{
cout<<"i am a Parent Process"<<"pid:"<<getpid()<<"ppid:"<<getppid()<<endl;
cout<<endl;
sleep(1);
}
}
return 0;
}
观察可得子进程的STAT为Z+了,也就是变成了僵尸进程,这样就会造成内存泄漏
进程退出的方式
进程常见退出方式有三种
正常退出:
-
从mian返回
-
调用exit
-
_exit
异常退出:
ctrl+c
_exit函数
void _exit(int status);
参数:status 定义了进程的终止状态,父进程通过wait来获取该值
说明:虽然status是int,但是仅有低8位可以被父进程所用。所以_exit(-1)时,在终端执行$?发现返回值是255。
exit函数
void exit(int status);
exit最后也会调用exit, 但在调用exit之前,还做了其他工作:
- 执行用户通过 atexit或on_exit定义的清理函数。
- 关闭所有打开的流,所有的缓存数据均被写入
- 调用_exit
return 退出
return是一种更常见的退出进程方法。执行return n等同于执行exit(n),因为调用main的运行时函数会将main的返回值当做 exit的参数。
进程等待
之前讲过,子进程退出,父进程如果不管不顾,就可能造成‘僵尸进程’的问题,进而造成内存泄漏。
另外,进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill -9 也无能为力,因为谁也没有办法
杀死一个已经死去的进程。
最后,父进程派给子进程的任务完成的如何,我们需要知道。如,子进程运行完成,结果对还是不对,
或者是否正常退出。
父进程通过进程等待的方式,回收子进程资源,获取进程退出信息
进程等待的方法
等待进程的函数有两个分别是wait()和waitpid()。
pid_t wait(int*status);
//status 是输出型参数,用来获取进程退出状态若不关心则可以设置为null
//返回值:
//成功返回被等待进程pid,失败返回-1
pid_ t waitpid(pid_t pid, int *status, int options);
/*
返回值:
当正常返回的时候waitpid返回收集到的子进程的进程ID;
如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
参数:
pid:
Pid=-1,等待任一个子进程。与wait等效。
Pid>0.等待其进程ID与pid相等的子进程。
status:
WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
options:
WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进
程的ID。
*/
kill的使用
现在我们学习kill -9 信号
作用就是杀掉指定pid的进程(对僵尸进程无效)
拿上个程序举例,父进程一直在循环执行,在右侧终端发出杀进程信号后终止了程序执行。
进程替换
继续查手册
EXEC(3) Linux Programmer’s Manual EXEC(3)
NAME
execl, execlp, execle, execv, execvp, execvpe - 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[]);
int execvpe(const char *file, char *const argv[],
char *const envp[]);
Feature Test Macro Requirements for glibc (see feature_test_macros(7)):
execvpe(): _GNU_SOURCE
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 name of a file that is to
be executed.
The const char *arg and subsequent ellipses in the execl(), execlp(), and
execle() functions can be thought of as arg0, arg1, ..., argn. Together
they describe a list of one or more pointers to null-terminated strings
that represent the argument list available to the executed program. The
first argument, by convention, should point to the filename associated with
the file being executed. 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(), execvp(), and execvpe() functions provide an array of pointers
to null-terminated 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() and execvpe() functions allow the caller to specify the envi‐
ronment of the executed program via the argument envp. The envp 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 calling process.
Special semantics for execlp() and execvp()
The execlp(), execvp(), and execvpe() functions duplicate the actions of
the shell in searching for an executable file if the specified filename
does not contain a slash (/) character. The file is sought in the colon-
separated list of directory pathnames specified in the PATH environment
variable. If this variable isn’t defined, the path list defaults to the
current directory followed by the list of directories returned by conf‐
str(_CS_PATH). (This confstr(3) call typically returns the value
“/bin:/usr/bin”.)
If the specified filename includes a slash character, then PATH is ignored,
and the file at the specified pathname is executed.
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
The exec() functions return only if an error has occurred. The return
value is -1, and errno is set to indicate the error.
ERRORS
All of these functions may fail and set errno for any of the errors speci‐
fied for execve(2).
VERSIONS
The execvpe() function first appeared in glibc 2.11.
CONFORMING TO
POSIX.1-2001, POSIX.1-2008.
The execvpe() function is a GNU extension.
NOTES
On some other systems, the default path (used when the environment does not
contain 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.53 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/.
GNU 2010-09-25 EXEC(3)
简单描述
#include <unistd.h>`
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[]);
//其实有六种exec族的函数,统称为exec函数
//这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
//如果调用出错则返回-1
//所以exec函数只有出错的返回值而没有成功的返回值。
命名原理
- (list) : 表示参数采用列表
- v(vector) : 参数用数组
- p(path) : 有p自动搜索环境变量PATH
- e(env) : 表示自己维护环境变量
进程替换举例
#include <iostream>
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
using namespace std;
int main()
{
pid_t pid=fork();
if(pid)
{
waitpid(pid,NULL,WNOHANG);
}
else
{
execl("/bin/ls","ls","-la",NULL);
}
return 0;
}
这样就完成了ls的替换