内容:
- fork()与vfork()
fork()与vfork()
1.fork()函数的执行次序是不确定的,是由操作系统进行调度的,而vfork()的执行次序是确定的,先执行子进程,再执行父进程。
2.fork()函数创建的子进程是拷贝父进程然后重新开辟地址空间的,而vfork是子进程和父进程共享地址空间。
下面我们进行实验说明:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void main(){
pid_t pid;
int i;
int tmp = 188;
pid = vfork();
if(pid<0)
{
perror("error");
}
if(pid == 0)
{
for(i=0;i<3;i++)
{
printf("子进程,pid:%d,ppid%d \n",getpid(),getppid());
tmp++;
sleep(1);
}
_exit(0);
}else
{
while(1)
{
printf("父进程,pid:%d,ppid%d \n",getpid(),getppid());
printf("父进程,tmp:%d \n",tmp);
sleep(1);
}
}
}
首先我们使用fork()函数创建子进程,然后在子进程中对变量tmp进行++,因为fork()是开辟了新的空间,子进程中的tmp的父进程的tmp是不同的两个,所以父进程中的tmp还是188.同时fork函数是子进程父进程随机执行,执行结果如下。
然后我们使用vfork()函数创建子进程,然后在子进程中对变量tmp进行++,因为vfork()是子进程父进程共用一块地址空间,所以子进程中的对tmp进行++,父进程中也会跟着就该,所以父进程中的tmp还是191.同时vfork函数是子进程先执行父进程后执行,执行结果如下。
exec函数族:
exec指的是一组函数,一共有六个函数,分别是:
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 execve(const char *path, char *const argv[], char *const envp[]);
其中execve()是真正的系统调用,其余5个都是C语言封装好的函数,主要是为了适应多种类型的参数列表,其他5个函数最终也是调用的execve()这个函数。
execl():
参数:文件路径,…(可变形参,一般以文件名开头 以NULL结尾)
这里要注意的是文件名称也是一个参数,以下都是。
代码:
#include <stdio.h>
#include <unistd.h>
void main(){
execl("/home/glq/class/hello.out","hello.out",NULL);
}
execlp():
参数:文件名称,…(可变形参,一般以文件名开头 以NULL结尾)
代码:
#include <stdio.h>
#include <unistd.h>
void main(){
execlp("ls","ls","-alh",NULL);
}
execle():
参数:文件路径,…(可变形参,一般以文件名开头 以NULL结尾),环境变量参数
代码:
#include <stdio.h>
#include <unistd.h>
void main(){
execle("/home/glq/class/hello.out","hello.out",NULL,NULL);
}
execv():
参数:文件路径,数组 一般以NULL结尾
代码:
#include <stdio.h>
#include <unistd.h>
void main(){
char *arg[]={"hello.out",NULL};
execv("/home/glq/class/hello.out",arg);
}
execvp():
参数:文件名称,数组 一般以NULL结尾
代码:
#include <stdio.h>
#include <unistd.h>
void main(){
char *arg[]={"ls","-alh",NULL};
execvp("ls",arg);
}
execve():
参数:文件路径,数组 一般以NULL结尾,环境变量参数
代码:
#include <stdio.h>
#include <unistd.h>
void main(){
char *arg[]={"hello.out",NULL};
execve("/home/glq/class/hello.out",arg,NULL);
}
作业:
用vfork()函数创建子进程,在子进程里通过execv函数调用一个自定义程序。分别在子进程、父进程和自定义程序里打印进程号和父进程号。
详解:
大概流程就是先用vfork()创建一个子进程,然后判断是子进程还是主进程,如果是子进程就输出pid和ppid,然后用execl调用一下test.out这个程序,在这个程序中输出一下pid和ppid;如果是父进程就直接输出pid和ppid。这里要注意的是虽然是vfork创建的,但是父进程并不会等到test.out这个执行完毕以后才执行,所以为了不让test.out这个变成孤儿进程,我们把父进程中sleep(2),等待test.out执行完毕。具体的程序如下:
hello.c:
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
void main(){
printf("tese程序----进程号:%d,父进程号:%d\n",getpid(),getppid());
}
mian.c:
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
void main(){
pid_t pid;
pid = vfork();
if(pid < 0)
{
perror("fork error \n");
}
if(pid == 0)
{
printf("main程序 子进程----进程号:%d,父进程号:%d\n",getpid(),getppid());
execl("/home/glq/hw/day18/test.out","test.out",NULL);
}else
{
sleep(2);
printf("main程序 主进程----进程号:%d,父进程号:%d\n",getpid(),getppid());
}
}
运行结果如下: