1.fork()函数
fork()函数用于从已经从在的进程中创建一个新进程。新进程为子进程,原进程为父进程。在父进程中执行fork()函数时,父进程会复制出一个子进程,而且父子进程的代码从fork()函数的返回开始分别在两个地址空间中同时运行,从而使两个进程分别获得其所属fork()函数的返回值,其中在父进程中的返回值是子进程的进程号,而在子进程中返回0。因此,可以通过返回值来判断是父进程还是子进程。
使用fork()函数的代价是很大的,它复制了父进程中的代码段、数据段和堆栈段里的大部分内容,使用fork()函数的系统开销比较大,执行速度也不是很快。
代码如下
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(void)
{
pid_t result;
//调用fork函数
result = fork();
if(result == -1)//错误
{
printf("Fork error\n");
}
else if(result == 0)//返回值0代表子进程
{
printf("The returned value is %d\n\
In child process!\n My PID is %d\n",result,getpid());
}
else //返回值大于0 代表父进程
{
printf("The returned value is %d\n\
In father process!\n My PID is %d\n",result,getpid());
}
return result;
}
执行结果如下
book@100ask:~/linux_multi_fork$ ./01_fork
The returned value is 3086
In father process!
My PID is 3085
The returned value is 0
In child process!
My PID is 3086
由于fork()完整地复制了父进程的整个地址空间,执行速度比较满。为了加快fork()的执行速度,unix系统设计者创建了vfork().
vfork()也能创建新进程,但它不产生父进程的副本。它是通过允许父子进程可访问相同物理内存,从而伪装了对进程地址空间的真实复制,当子进程需要改变内存中的数据时才复制父进程。这就是写操作时复制技术。
2.exec函数族
fork()函数用于创建一个子进程,该子进程几乎复制了父进程的全部内容;exec函数族提供了一个在进程中启动另一个程序执行的方法。它可以根据指定的文件名或目录名找到可执行文件,并用它来取代原调用进程的数据段、代码段、堆栈段,在执行完之后,原调用进程的内容除了进程号外,其他全部被新的进程替换了。这里的可执行程序可以是二进制文件或任何可执行的脚本文件。
在linux中使用exec函数族主要有两种情况:
1>当进程认为自己不能再为系统和用户做出任何贡献时,就可以调用exec函数族中的任意一个函数让自己重生。
2>如果一个进程想执行另一个程序,就调用fork()函数新建一个进程然后调用exec函数族中的任意一个函数,这样看起来 就像通过执行应用程序而产生一个新进程。
2.1execlp()函数
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
pid_t result;
//调用fork函数
result = fork();
if(result == -1)//错误
{
printf("Fork error\n");
}
else if(result == 0)//返回值0代表子进程
{
//调用execlp函数,这里相当于调用了“ps -ef”命令
if(execlp("ps","ps","-ef",NULL) < 0)
{
printf("Execlp error\n");
}
}
return result;
}
执行结果如下:
2.2 execl()函数
此函数必须要用以“/”开头的完整的文件目录查找对应的可执行文件。
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
pid_t result;
//调用fork函数
result = fork();
if(result == -1)//错误
{
printf("Fork error\n");
}
else if(result == 0)//返回值0代表子进程
{
//调用execl函数,这里相当于调用了“ps -ef”命令,但要给出完整的路径
if(execl("/bin/ps","ps","-ef",NULL) < 0)
{
printf("Execlp error\n");
}
}
return result;
}
执行结果
2.3 execle()函数
将环境变量添加到新建的子进程中,这里的“env”是查看当前环境变量的命令
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
pid_t result;
char *envp[]={"PATH=/tmp","USER=david",NULL};
//调用fork函数
result = fork();
if(result == -1)//错误
{
printf("Fork error\n");
}
else if(result == 0)//返回值0代表子进程
{
if(execle("/usr/bin/env","env",NULL,envp) < 0)
{
printf("Execle error\n");
}
}
return result;
}
执行结果
2.4 execve()函数
通过构造指针数组的方式来传递参数,注意参数链表一定要以NULL作为结束符。
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
pid_t result;
char *arg[] = {"env",NULL};
char *envp[]={"PATH=/tmp","USER=david",NULL};
//调用fork函数
result = fork();
if(result == -1)//错误
{
printf("Fork error\n");
}
else if(result == 0)//返回值0代表子进程
{
if(execve("/usr/bin/env",arg,envp) < 0)
{
printf("Execve error\n");
}
}
return result;
}
执行结果
3. exit()和_exit()
1>exit()和_exit()函数都是用来终止进程的。当程序执行到exit()和_exit()时,进程会无条件停止剩下的所欲操作,清除各种数据结构,并终止本进程的运行。
区别:
exit()在终止当前基础之前要检查该进程打开国那些文件,把文件缓冲区中的内容写回文件;
_exit()直接使进程停止运行,清除其使用的内存空间,并清除其字内核中的各种数据结构。
exit()代码
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
printf("Using exit ...\n");
printf("This is the content in buffer");
exit(0);
}
执行结果
_exit()代码
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
printf("Using exit ...\n");
printf("This is the content in buffer");
_exit(0);
}
执行结果
去掉第二个printf最后的回车符,代码如下
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
printf("Using exit ...\n");
printf("This is the content in buffer\n");
_exit(0);
}
执行结果
4.wait()和waitpid()
wait()函数用于使父进程阻塞,直到一个子进程结束或者该进程接收到了一个指定的信号为止。如果该父进程没有子进程或者它的子进程已经结束,则wait()就会立即返回。
waitpid()的作用和wait()一样,但它并不一定要等待第一个终止的子进程,它有若干选项,可以提供一个非阻塞版的wait()功能,也能支持作业控制。
代码
//waitpid
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
pid_t pc,pr;
pc = fork();
if(pc < 0)
{
printf("Error fork\n");
}
else if(pc == 0)//子进程
{
sleep(5);
exit(0);
}
else//父进程
{
//循环测试子进程是否推出
do
{
//调用waitpid(),且父进程不阻塞
pr = waitpid(pc, NULL, WNOHANG);
//若子进程还未退出,则父进程暂停1s
if(pr == 0)
{
printf("The child process has not exited\n");
sleep(1);
}
}while(pr == 0);
//若发现子进程退出,打印出相应情况
if(pr == pc)
{
printf("Get child exit code: %d\n",pr);
}
else
{
printf("Some error occured.\n");
}
}
}
执行结果