(1)fork和vfork
之前用过fork来创建子进程,知道可以根据返回值来区分父子进程,对于父进程来说,fork的返回值是子进程ID,对于子进程来说返回PID为0.关于这一点,vfork函数与fork函数是相同的。而fork与vfork的区别在于在哪呢?
*********************************************************************************
用fork创建的子进程会复制父进程的资源,而用vfork创建的子进程会与父进程共享地址空间。
*********************************************************************************
下面用函数的执行结果说明一下:
#include
#include
#include
int g_var = 0;
int main (void)
{
int var = 1;
pid_t pid;
printf("Process ID: %ld\n",(long)getpid());
printf("Before execute fork system call, g_val = %d,var = %d.\n",g_var,var);
if((pid = fork())<0)
{
printf("Creat a new process failed.\n");
return 1;
}
else if(pid==0)
{
g_var++;
var++;
printf("Process ID: %ld,g_var = %d,var =%d.\n",(long)getpid(),g_var,var);
_exit(0);
}
printf("Process ID: %ld,g_var = %d,var =%d.\n",(long)getpid(),g_var,var);
return 0;
}
修改以上代码,分别使用fork和vfork,执行结果如下:
--->使用fork
gcc -o syst systemcall.c
./syst
Process ID: 6006
Before execute fork system call, g_val = 0,var = 1.
Process ID: 6007,g_var = 1,var =2.
Process ID: 6006,g_var = 0,var =1.
--->使用vfork
gcc -o syst systemcall.c
./syst
Process ID: 5990
Before execute vfork system call, g_val = 0,var = 1.
Process ID: 5991,g_var = 1,var =2.
Process ID: 5990,g_var = 1,var =2.
以上结果可以证明:用fork函数创建子进程,子进程中修改变量不会影响父进程中变量的取值;用vfork函数创建子进程,子进程修改变量会导致父进程中对应变量值的改变。
*********************************************************************************
使用fork创建子进程往往不是为了获得完全一样的进程,而是通过exec来执行其他可执行程序,所以完全的复制有些多余,存在一定程度的资源消耗,vfork的出现促进了fork机制的改变,目前linux对fork系统调用的实现采用了写时复制(copy-on-write COW)的方法,只有子进程对内存数据进行写操作时,才会进行资源的复制。COW机制使vfork几乎没有了价值,而且子进程对内存的修改会影响父进程,需要特别注意。
**********************************************************************************
(2)extc函数族
在子进程中用exec执行其他的可执行程序,可以对进程中的代码段、数据段、堆栈段进行替换。主要几个函数有execl,execlp,execle,execv,execve,execvp(函数定义略)等。
exec后面为l,表示该函数可以使用可变参数(个数可变);包含v字符,表示支持使用参数列表;包含p,表示如果给出的程序没有给出所在路径,系统会自动搜索PATH路径(不包含p的必须给出文件所在完整路径)。
下面函数示范了execlp和execvp的使用,后者明显具备更大的灵活性。
#include
#include
int main (int argc,char* argv[])
{
if(argc <2)
{
printf("Usage: %s path.\n",argv[0]);
return 1;
}
execlp("/bin/ls","ls",argv[1],(char*)NULL); //支持变长参数,以NULL结尾
return 0;
}
执行结果:
--->execlp()
gcc -o syst2 systemcall2.c
./syst2
Usage: ./syst2 path.
./syst2 /home/gaolu
commen files music
data.dat~ picture
desktop process.c~
document program~
etcnetworkinterface syst
Examples syst2
fcitx-3.4.2 systemcall2.c
fcitx-install systemcall2.c~
file.c~ systemcall.c~
fileopera~ test.c~
file_operate.c ~ test.dat~
fork_file.c~ test_folder
gao.lu.c~ test_program
ls~ test_program (copy)
LumaQQ uid.c~
lumaqq_2005-linux_gtk2_x86_with_jre.tar.gz uidprint.c~
mnt video
module
--->execvp()
#include
#include
int main (int argc,char* argv[])
{
if(argc <2)
{
printf("Usage: %s arg list 1 2 3...\n",argv[0]);
return 1;
}
execvp(argv[1],&argv[1]);
return 0;
}
执行结果:
gcc -o syst3 systemcall2.c
./syst3
Usage: ./syst3 arg list 1 2 3...
./syst3 ls /home/gaolu
commen files music
data.dat~ picture
desktop process.c~
document program~
etcnetworkinterface syst
Examples syst2
fcitx-3.4.2 syst3
fcitx-install systemcall2.c
file.c~ systemcall2.c~
fileopera~ systemcall.c~
file_operate.c ~ test.c~
fork_file.c~ test.dat~
gao.lu.c~ test_folder
ls~ test_program
LumaQQ test_program (copy)
lumaqq_2005-linux_gtk2_x86_with_jre.tar.gz uid.c~
mnt uidprint.c~
module video
(3)exit和_exit
可以将两者用在main函数中替代return,区别是exit和_exit不考虑返回值类型。
exit和_exit之间的区别在于:
*****************************************************************************************
[1] exit是在ANSIC中说明,_exit是在POSIX中说明。
[2] exit终止调用进程,退出之前关闭所有文件,清空标准输入输出缓冲区,并执行在atexit中注册的回调函数;_exit终止调用进程,但是不关闭文件,不清除标准输入输出缓冲区,也不调用回调函数。
因此vfork()创建的子进程可以调用_exit退出,但是不要调用exit()退出。
*****************************************************************************************
举例如下:
#include
#include
#include
void do_at_exit(void)
{
printf("You can see the output when the program teminates.\n");
}
int main ()
{
int flag = 0;
flag = atexit(do_at_exit);
if(flag != 0)
{
printf("Can't set exit function.\n");
return EXIT_FAILURE;
}
exit(EXIT_SUCCESS);
}
执行结果:
gcc -o syst4 systemcall2.c
./syst4
You can see the output when the program teminates. //执行了注册的回调函数
将函数修改为_exit()以后执行结果如下:
#include
#include
#include
void do_at_exit(void)
{
printf("You can see the output when the program teminates.\n");
}
int main ()
{
int flag = 0;
flag = atexit(do_at_exit);
if(flag != 0)
{
printf("Can't set exit function.\n");
return EXIT_FAILURE;
}
_exit(EXIT_SUCCESS);
}
gcc -o syst5 systemcall2.c
./syst5 //返回shell之前不会执行do_at_exit()