http://www.gnu.org/software/libc/manual/html_node/Processes.html#Processes
进程是分配系统资源的基本单元。每个进程都有自己的地址空间且(通常)有一个控制线程。一个进程执行一个程序;当然可以有多个进程执行相同的程序,但每个进程在其地址空间中都有一个该程序的副本,且该副本是独立于其他进程的。
进程是分层的(或分级的)。每个进程的创建都需要一个父进程。被一个父进程创建的进程称为子进程。子进程继承了父进程的很多属性。
本章描述了一个程序怎么创建、终止、控制子进程。实际上,他包括三方面独立相对独立的操作:创建一个子进程,让子进程执行程序,对已完成的子程序和原程序进行协调。
系统函数system()为运行其他程序提供了一种简单的、可移植的机制;他合并了上面所讲三步操作。你如果想提高可控性,也可以依次执行那三步操作,而不使用该函数。
• 运行一个命令: | 运行其他程序的简单方式。 | |
• 创建进程的一些概念: | 对创建进程关键点的一个概览。 | |
• 进程号: | 怎么得到进程ID。 | |
• 创建一个进程: | 怎样叉出子进程。 | |
• 执行一个文件: | 怎么让一个进程执行其他程序。 | |
• 进程完成: | 子进程完成使命后怎么做。 | |
• 进程完成状态: | 该如何解释子进程返回的状态值。 | |
• BSD的一些Wait函数: | 因为相后兼容性而保留这些函数。 | |
• 进程创建的示例: | 一个完整的例程。 |
一、运行一个命令
运行令一个程序的简单方法是使用system函数。这个函数帮你做了运行子程序的所有工作,但你也因此失去了控制执行细节的灵活性:你只能等到子程序终止才可以做其他事。
函数原型: int system (const char *command)
-
-
此函数如shell命令一样来执行一个命令(command)。在GNU C库中,他总是使用默认shell sh来运行命令。根据不同平台,他会搜索PATH变量中的目录来查找要执行的程序。如果创建shell进程失败就会返回 -1,创建成功就返回该shell进程的状态。“进程完成” 一节会有进程状态的详细解释。
-
-
如果(command)参数为null指针,会返回 0,表示没有要执行的命令。
-
-
This function is a cancellation point in multi-threaded programs. This is a problem if the thread allocates some resources (like memory, file descriptors, semaphores or whatever) at the time
system
is called. If the thread gets canceled these resources stay allocated until the program ends. To avoid this calls tosystem
should be protected using cancellation handlers.
-
-
声明system函数的头文件是stdlib.h。
Preliminary: | MT-Safe | AS-Unsafe plugin heap lock | AC-Unsafe lock mem | See POSIX Safety Concepts.
可移植性提示: Some C implementations may not have any notion of a command processor that can execute other programs. You can determine whether a command processor exists by executing system (NULL)
; if the return value is nonzero, a command processor is available.
The popen
and pclose
functions (see Pipe to a Subprocess) are closely related to the system
function. They allow the parent process to communicate with the standard input and output channels of the command being executed.
示例:
#include <stdlib.h>
#include <stdio.h>
int main()
{
//会打印出当前目录下的所有文件和目录
system("ls -a");
printf("%d\n", system(NULL));
}
二、进程创建的一些概念
本小节讲解了进程的一些概念,及创建进程并运行不同程序的一些步骤。
每个进程都有个称作进程ID的数字编号。当创建进程时会为其分配一个唯一的进程ID。其生命周期从进程创建成功到向他的父进程发出终止信号;这时,会释放所有进程资源(包括进程ID)。
使用fork()系统调用来创建进程(因而创建新进程有时也称作fork一个进程)。由fork()创建新进程将会复制其父进程,区别是拥有自己的进程ID。
fork()头文件是 unistd.h 。
在叉(fork)出子进程后,父进程和子进程都会正常继续独立执行。如果想要等到子进程执行完毕再继续执行,那么在叉(fork)子进程之后要显式调用wait或waitpid(可看“进程完成”一节),此函数子进程终止的部分信息——比如,退出(exit)状态码。
在fork()调用返回时,会执行其父进程所执行的程序。当然,你可以根据fork()的返回值来判断该程序是运行在父进程还是子进程中。
#include <stdio.h>
#include <unistd.h>
int main()
{
pid_t pid = fork();
char *pidname;
if(pid == 0) pidname = "子进程";
else if(pid < 0) pidname = "错进程";
else{ pidname = "父进程";}
printf("%s.........哈哈,printf会执行两次\n", pidname);
}
通常很少用到多个进程来执行同一个程序,但一个进程执行多个程序则很常见,可以在子进程中使用exec程序来执行其他程序,可看“执行一个文件”小节。进程正在执行的程序称作“进程映像”。当该进程执行新程序时,会忘掉前面所有的进程映像;当新程序退出时,该进程也会退出,而不是返回到上一个进程映像。
#include <stdio.h>
#include <unistd.h>
int main()
{
pid_t pid = fork();
char *pidname;
if(pid == 0){
pidname = "子进程";
static char *child[]={"echo","我是子进程,嘿嘿,懒得执行老爸的程序了",NULL};
execv("/bin/echo", child);
}else if(pid < 0){
pidname = "错进程";
}else{
pidname = "父进程";
}
printf("%s.........哈哈,这次printf只执行一次\n", pidname);
return 0;
}
三、进程标志
数据类型pid_t被用来描述进程ID。可以通过调用getpid()来得到进程ID,通过调用getppid()来得到当前的进程的父进程ID(parent process ID)。
pid_t头文件为sys/types.h;getpid()、getppid()头文件为unistd.h。
-
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
getpid()函数返回当前进程的进程ID。
函数: pid_t getpid (void)
-
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
getppid()函数返回当前进程的父进程ID。
函数:pid_t getppid (void)
四、创建一个进程
使用fork()函数是创建进程的较原始方法,其声明在unistd.h头文件中。
-
函数:
pid_t
fork
(void)
-
Preliminary: | MT-Safe | AS-Unsafe plugin | AC-Unsafe lock | See POSIX Safety Concepts.
-
fork()函数创建一个新进程。
-
-
如果操作成功,在父进程和子进程中都能够看到fork()的返回值,但值不同:子进程中返回0,父进程中返回子进程的进程ID。
-
-
如果进程创建失败,在父进程中fork()会返回-1。下面是专为fork()错误条件定义的errno:
-
-
没有足够的系统资源来创建另一个进程,或者已有过多的进程在运行。这说明已超过了RLIMIT_NPROC的资源限制,当然还可以增加;可查看Limits on Resources。
-
系统无法满足创建进程所需要的空间。
-
EAGAIN
ENOMEM
子进程不同于父进程的特定属性:
- 子进程有自己唯一的进程ID。
- 子进程的父ID就是父进程的进程ID。
- 子进程会复制父进程所打开文件的描述符。之后父进程对文件描述符的更改不会影响到子进程的文件描述符,反之亦然,可查看Control Operations。然而,两个进程的文件描述符都关联到同一个文件;see File Position。
- The elapsed processor times for the child process are set to zero; see Processor Time.
- 不会继承父进程设置的文件锁. See Control Operations.
- The child doesn’t inherit alarms set by the parent process. See Setting an Alarm.
- The set of pending signals (see Delivery of Signal) for the child process is cleared. (The child process inherits its mask of blocked signals and signal actions from the parent process.)
-
函数:
pid_t
vfork
(void)
-
Preliminary: | MT-Safe | AS-Unsafe plugin | AC-Unsafe lock | See POSIX Safety Concepts.
-
vfork()函数与fork()类似,但在有些系统上效率会更高;但是,想要安全使用需要遵循一些限制条件。
-
-
fork()会将调用他的进程的地址空间进行完全复制,并且允许父进程和子进程各自独立的执行,而vfork()并不进行这种复制。使用vfork()创建的子进程会共享父进程的地址空间,直到调用_exit()或者exec()函数为止。与此同时,父进程会暂停执行。
-
-
你必须小心地防止子进程更改全局数据甚至与父进程共享的局部变量。Furthermore, the child process cannot return from (or do a long jump out of) the function that called
vfork
! This would leave the parent process’s control information very confused. 如有疑虑,可以使用fork()来代替。 -
-
有些系统并没有真正实现vfork()。GNU C库允许你在所有系统中使用vfork(),但如果vfork()不存在,实际上是执行的fork()。如果你在使用vfork()时遵循了适当的预防措施,系统就是调用fork()代替vfork(),你的程序也能够很好的工作。