注意到此二者的差异源于《Unix环境高级编程》一书8.3章节fork function。
示例程序
注意自行验证时,应注释掉apue.h包含,自行实现err_sys函数,以及包含缺乏的头文件,即可编译过。下面是经过书中示例修改后的代码,linux下可以编译运行
//#include "apue.h"
#include "unistd.h"
void err_sys(char * errinfo)
{
printf("Error: %s\n", errinfo);
exit(-1);
}
int globvar = 6; /* external variable in initialized data */
char buf[] = "a write to stdout\n";
int main(void)
{
int var; /* automatic variable on the stack */
pid_t pid;
var = 88;
if (write(STDOUT_FILENO, buf, sizeof(buf)-1) != sizeof(buf)-1)
err_sys("write error");
printf("before fork\n"); /* we don’t flush stdout */
if ((pid = fork()) < 0) {
err_sys("fork error");
} else if (pid == 0) { /* child */
globvar++; /* modify variables */
var++;
} else {
sleep(2); /* parent */
}
printf("pid = %ld, glob = %d, var = %d\n", (long)getpid(), globvar,var);
exit(0);
}
程序运行结果 前者为直接运行的打印结果,后者为程序输出重定向到文件后的运行打印结果:
$./a.out
a write to stdout
before fork
pid = 430, glob = 7, var = 89 #child’s variables were changed
pid = 429, glob = 6, var = 88 #parent’s copy was not changed
$ ./a.out > temp.out
$ cat temp.out
a write to stdout
before fork
pid = 432, glob = 7, var = 89
before fork
pid = 431, glob = 6, var = 88
sizeof与strlen
When we write to standard output, we subtract 1 from the size of buf to avoid
writing the terminating null byte. Although strlen will calculate the length of a string
not including the terminating null byte, sizeof calculates the size of the buffer, which
does include the terminating null byte. Another difference is that using strlen
requires a function call, whereas sizeof calculates the buffer length at compile time, as
the buffer is initialized with a known string and its size is fixed.
示例程序中write函数写入字符串时计算长度使用的是sizeof,并且减去1,去掉字符串末尾的空字符,避免write函数写空字符(相当于没有写入内容);实际也可以直接使用strlen(buf),等价于sizeof(buf) -1。
- sizeof是运算符,在编译时即可计算出结果
- strlen是函数,使用时存在函数调用开销
- char *buf1 = “abcdef”,sizeof(buf1) 结果是4 指针自身占用的内存字节数,而非指向内容所占字节数;
- strlen 使用时,所用对象必须是‘/0’结束
write 与 printf
the write function is not buffered. Because write is called
before the fork, its data is written once to standard output. The standard I/O library,
however, is buffered.standard output is line buffered if it’s
connected to a terminal device; otherwise, it’s fully buffered.
When we run the
program interactively, we get only a single copy of the first printf line, because the
standard output buffer is flushed by the newline. When we redirect standard output to
a file, however, we get two copies of the printf line. In this second case, the printf
before the fork is called once, but the line remains in the buffer when fork is called.
This buffer is then copied into the child when the parent’s data space is copied to the
child. Both the parent and the child now have a standard I/O buffer with this line in it.
The second printf, right before the exit, just appends its data to the existing buffer.
When each process terminates, its copy of the buffer is finally flushed
- write 函数是字节流写入,无缓存的,即每次调用,即立即写入所有内容
- printf 函数是有缓存的,当标准输出是终端时,为行缓存方式,遇到换行或者行缓存满时,才真正输出打印;当标准输出非终端时,如上面的示例中重定向到文件,其一直处于缓存状态,直到程序结束时触发flush,才输出到文件。(因为fork创建的子进程会复制父进程的内存内容,创建自己的地址空间,由于重定向到文件时,printf函数的内容仍在缓存中,所以会看到两次的before fork 打印)
- 上述示例程序中若在printf("before fork\n")后立即调用fflush(STDOUT_FILENO)将标准输出的缓存立即输出打印,则上面第二次重定向输出到文件时的结果与第一次一样
网友评论