分类: C/C++
2005-12-12 09:41:22
fork()会产生一个与父程序相同的子程序,唯一不同之处在於其process id(pid)。
如果我们要撰写守护程序,或是例如网路伺服器,需要多个行程来同时提供多个连线,可以利用fork()来产生多个相同的进程。
函数声明:
pid_t fork(void);
pid_t vfork(void);
返回值:
-1 : 失败。
0 : 子程序。
>0 : 将子程序的process id传回给父程序。
在Linux下fork()及vfork()是相同的东西。
范例一: fork.c
在这个范例中,我们示范fork()的标准用法。
#include
#include
#include
void main(void)
{
pid_t pid;
printf("hello ");
pid = fork();
switch (pid) {
case -1: printf("failure! "); break;
case 0: printf("I am child! "); break;
default: printf("my child is %d ",pid); break;
}
for (;;) { /* do something here */ }
}
编译:
gcc -o ex1 fork.c
执行结果:
./ex1 &
hello
my child is 8650
I am child!
我们可以见到,使用fork(),可将一个程式分岐成两个。在分歧之前的程式码只执行一次。
检验行程:
ps | grep ex1
8649 p0 R 0:40 ./ex1
8650 p0 R 0:40 ./ex1
8649是父程序的pid,8650则为子程序的pid。
您会需要用到"killall ex1"来杀掉两个行程。
范例二: daemon.c
在UNIX中,我们一般都利用fork(),来实作所谓的"守护神程式",也就是DOS中所谓的"常驻程式"。一般的技巧是将父程序结束,而子程序便成为"守护神"。
这个范例中,示范一般标准的daemon写法。
#include
#include
#include
void main(void)
{
pid_t pid;
pid = fork();
if (pid>0) {
printf("daemon on duty! ");
exit(0);
} else
if (pid<0) {
printf("Can't fork! ");
exit(-1);
}
for (;;) {
printf("I am the daemon! ");
sleep(3);
/* do something your own here */
}
}
编译:
gcc -o ex2 daemon.c
执行结果:
./ex2
daemon on duty!
I am the daemon!
接下来每三秒钟,都会出现一个"I am the daemon!"的讯息,这表示您的程式已经"长驻"在系统中了。
检验行程:
ps | grep ex2
8753 p0 S 0:00 ./ex2
注意到在范例一中,我们下的指令为"./ex1 &",而在范例二中为"./ex2",没有"&"符号。
范例三: lock.c
许多的时候,我们希望"守护神"在系统中只有一个,这时候会需要用到pid lock的技巧。如果您注意到/var/run目录中的内容,您会发现到有许多的*.pid档,观看其内容都是一些数字,这些数字其实就是该行程的pid。
#include
#include
#include
void main(void)
{
FILE *fp;
pid_t pid;
if (access("/var/run/lock.pid",R_OK)==0) {
printf("Existing a copy of this daemon! ");
exit(1);
}
pid = fork();
if (pid>0) {
printf("daemon on duty! ");
fp = fopen("/var/run/lock.pid","wt");
fprintf(fp,"%d",pid);
fclose(fp);
exit(0);
} else
if (pid<0) {
printf("Can't fork! ");
exit(-1);
}
for (;;) {
printf("I am the daemon! ");
sleep(3);
}
}
编译:
gcc -o ex3 lock.c
执行:
./ex3
daemon on duty!
I am the daemon!
再执行一次
./ex3
Existing a copy of this daemon!
这时如果您将该行程杀掉,并重新执行:
killall ex3
./ex3
Existing a copy of this daemon!
您会发现daemon无法再度长驻,因为/var/run/lock.pid并没有因为行程被杀掉而删除掉。一般来说,开机後的启动Script,会将/var/run中所有内容自动清除,以避免这个问题的发生。如果您想要在行程被杀掉时,将/var/run/lock.pid也一并删除,那麽您需要利用signal来处理这件事。
您可手动删除该档案,daemon便可再度执行。
rm /var/run/lock.pid
范例四: children.c
如果您正在写伺服器,您可能会需要复制出许多的子行程,用以提供同时多人的服务,这时可利用fork(),一次复制出多个子行程。最佳的例子为Apache WWW Server。
#include
#include
#include
#define MAX_CHILD 9
void main(void)
{
pid_t pid;
int n;
printf("hello ");
n = 0;
do {
pid = fork();
n++;
switch (pid) {
case -1:
printf("failure! ");
exit(-1);
break;
case 0:
printf("I am child %d! ",n);
break;
default:
printf("my child is %d ",pid); break;
}
} while (pid!=0&&n
if (pid>0) exit(0);
for (;;) { /* do something here */ }
}
编译:
gcc -o ex4 children.c
执行结果:
./ex4
hello
my child is 8863
I am child 1!
my child is 8864
I am child 2!
my child is 8865
I am child 3!
my child is 8866
I am child 4!
my child is 8867
I am child 5!
my child is 8868
I am child 6!
my child is 8869
I am child 7!
my child is 8870
I am child 8!
my child is 8871
I am child 9!
检验行程:
ps | grep ex4
8863 p0 R 0:12 ./ex4
8864 p0 R 0:12 ./ex4
8865 p0 R 0:12 ./ex4
8866 p0 R 0:12 ./ex4
8867 p0 R 0:12 ./ex4
8868 p0 R 0:12 ./ex4
8869 p0 R 0:11 ./ex4
8870 p0 R 0:12 ./ex4
8871 p0 R 0:12 ./ex4