一 : fork函数实例
下面是一个fork函数的实例
[cpp]
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int glob = 6;
char buf[] = "a write to stdoutn";
int main(void)
{
int var;
pid_t pid;
var = 88;
if(write(STDOUT_FILENO, buf, sizeof(buf) - 1) != sizeof(buf) -1)
printf("error: write errorn");
printf("before forkn");
if((pid = fork()) < 0) {
printf("error: fork errorn");
} else if(pid == 0) {
glob ++;
var ++;
} else {
sleep(2);
}
printf("pid = %d, glob = %d, var = %dn", getpid(), glob, var);
exit(0);
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int glob = 6;
char buf[] = "a write to stdoutn";
int main(void)
{
int var;
pid_t pid;
var = 88;
if(write(STDOUT_FILENO, buf, sizeof(buf) - 1) != sizeof(buf) -1)
printf("error: write errorn");
printf("before forkn");
if((pid = fork()) < 0) {
printf("error: fork errorn");
} else if(pid == 0) {
glob ++;
var ++;
} else {
sleep(2);
}
printf("pid = %d, glob = %d, var = %dn", getpid(), glob, var);
exit(0);
}
然后执行输出
jay@jay-vibox:~/workspace/UNIX/8-1$cc main.c
jay@jay-vibox:~/workspace/UNIX/8-1$./a.out
a write to stdout
before fork
pid = 2917, glob = 7, var = 89
pid = 2916, glob = 6, var = 88
------------------------------------------------
jay@jay-vibox:~/workspace/UNIX/8-1$./a.out > tmp.out
jay@jay-vibox:~/workspace/UNIX/8-1$
jay@jay-vibox:~/workspace/UNIX/8-1$
jay@jay-vibox:~/workspace/UNIX/8-1$cat tmp.out
a write to stdout
before fork
pid = 2925, glob = 7, var = 89
before fork
pid = 2924, glob = 6, var = 88
-----------------------------------------------------------
一般说来fork之后先执行父进程还是子进程是不确定的。
当写道标准输出时,我们将buf长度减去1作为输出字节数,这是为了避免终止null字符写出。strlen计算不包括终止null字符的字符串长度,而sizeof则计算包括终止null字节的缓冲区长度。两者之间的另一个差别是,使用strlen需进行一次函数调用,而对于sizeof而言,因为缓冲区一用一直字符串进行了初始化,其长度是固定的,所以sizeof在编译时计算缓冲区长度。
当一交互方式运行该程序时,只得到该printf输出的行一次,其原因是标准输出缓冲区由换行符冲洗。
但是当标准输出重定向到一个文件时,却得到printf输出两次。其原因是,当fork之前调用了printf一次,但当调用fork时,该行数据仍在缓冲区中,然后再将父进程数据空间复制到子进程中时,该缓冲区也被复制到子进程中。于是那时父子进程各自有了带改行内容的标准IO缓冲区。
作者:zhangjie201412二 : Linux学习之"fork函数"
nfork函数创建一个新进程,新进程被称为子进程
n函数原型:pid_t fork(void)
n返回值:
fork函数调用一次,但是返回两次:在子进程中返回0,在父进程中返回子进程ID,出错返回-1。[www.61k.com]通过返回值,可以确定是在父进程还是子进程中。
n子进程和父进程继续执行fork调用之后的指令。
子进程是父进程的副本:
1.子进程获得父进程数据空间、堆和栈的副本;父子进程并不共享这些存储空间。
2.父子进程共享正文段(只读的);
3.为了提高效率,fork后并不立即复制父进程空间,采用了COW(Copy-On-Write);当父子进程任意之一,要修改数据段、堆、栈时,进行复制操作,但仅复制修改区域;
看一个程序:
#include<iostream>
#include<unistd.h>
#include<stdio.h>
using namespace std;
int glob = 6;
char buf[] = "a write to stdoutn";
int main(void)
{
int var;
pid_t pid;
var = 88;
if (write(STDOUT_FILENO, buf, sizeof(buf)-1) != sizeof(buf)-1)
{
cout << "write error" << endl;
return 0;
}
printf("before forkn");
if ( (pid = fork()) < 0)
{
cout << "fork error" << endl;
return 0;
}
else if (pid == 0)
{
glob++;
var++;
}
else
{
sleep(2);
}
printf("pid = %d, glob = %d, var = %dn", getpid(), glob, var);
return 0;
}
直接输出到控制台:
a write to stdout
before fork
pid = 1867, glob = 7, var = 89
pid = 1866, glob = 6, var = 88
使用重定向“./a.out>a.txt”,a.txt内容如下:
a write to stdout
before fork
pid = 1939, glob = 7, var = 89
before fork
pid = 1938, glob = 6, var = 88
为什会有这个差别?
先来看下“STDOUT_FILENO”和“FILE *stdout”的区别:
stdin / stdout / stderr是FILE*类型,供标准C++一级提供的文件操作函数库使用,定义在头文件<stdio.h>中。
STDIN_FILENO / STDOUT_FILENO / STDERR_FILENO 是int类型,其实质是文件描述符(值分别为0,1,2),定义在头文件<unistd.h>中。
FILE * stdin / stdout / stderr 对应的文件描述符(fd)分别是 STDIN_FILENO(0) / STDOUT_FILENO(1) / STDERR_FILENO(2) 。
两者的差别主要是标准I/O是带缓冲(具体见下面说明)的,而STDIN_FILENO / STDOUT_FILENO / STDERR_FILENO是不带缓冲的。
我们只需记住:
使用stdin / stdout / stderr的函数主要有:fread、fwrite、fclose等,基本上都以f开头。
使用STDIN_FILENO / STDOUT_FILENO / STDERR_FILENO的函数有:read、write、close等。
关于两者更详细的说明:
1.“FILE *stdout 和 STDOUT_FILENO 的区别”()
2."对stdin,stdout 和STDOUT_FILENO,STDIN_FILENO的学习"()
下面看下关于"printf"/"write"和缓冲的说明:
printf是在stdio.h中声明的函数,而标准IO都是带缓冲的,所以printf是带缓冲的。而write则是不带缓冲的。
标准IO在输入或输出到终端设备时,它们是行缓冲的,否则(文件)它们是全缓冲的。而标准错误流stderr是不使用缓冲的。更为准确的描述是:当且仅当标准输入和标准输出并不涉及交互式设备使,他们才是全缓冲的。标准出错流不使用缓冲。
下列情况会引发缓冲区的刷新(清空缓冲区):
1、缓冲区满时;
2、执行flush语句;
3、执行endl语句(printf是"n");
4、关闭文件。
综上所述,write的内容在父进程直接输出到了设备,“before fork”在主线程输出到终端后因为换行符而清空了缓冲区,所以也只输出了一次。
而重定向到"a.txt"时,printf使用的是全缓冲,所以“before fork”并未输出到设备,而是随着fork()而被复制了一份到子进程的空间中,所以输出了两次。
注意:在重定向父进程输出时,子进程也被重定向了。
为什么父进程要等待2秒?
在fork时,父进程所有打开的文件描述符都被复制一份到子进程中,然而他们共享文件描述符所指向的文件对象(FILE结构:描述文件读写指针,偏移量,标志等)。如果不等待,文件偏移量被交替修改,很可能产生混乱的输出。
本文标题:fork函数-fork函数实例61阅读| 精彩专题| 最新文章| 热门文章| 苏ICP备13036349号-1