Linux系统编程34 进程控制 - fork()详解1,与行缓冲,全缓冲的联系,必加fflush()刷新

1 getpid()/getppid
2 fork()


getpid()/getppid

NAME
getpid, getppid - get process identification

SYNOPSIS
#include <sys/types.h>
#include <unistd.h>

   pid_t getpid(void);
   pid_t getppid(void);

DESCRIPTION
getpid() returns the process ID of the calling process. (This is often used by routines that generate unique temporary filenames.)

getppid() returns the process ID of the parent of the calling process.


fork()

NAME
fork - create a child process

SYNOPSIS
#include <unistd.h>

   pid_t fork(void);

DESCRIPTION
fork() creates a new process by duplicating the calling process. The new process is referred to as the child process. The calling process is referred to as the parent process.

通过复制当前进程创建一个新进程,即父子进程相同,执行到的位置也相同

RETURN VALUE
On success, the PID of the child process is returned in the parent, and 0 is returned in the child. On failure, -1 is returned in the parent, no child process is created, and errno is set appropriately.

说明:
fork()后 ,在父进程当中返回的是子进程的pid,而在子进程中返回值为0。此时代码变成两份,两份一样的代码,就连执行到位置都一样。

在这里插入图片描述

#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>

int main(void)
{

	pid_t pid;

	printf("[%d]:Begin!\n",getpid());

	pid = fork();
	if(pid < 0)
	{
		fprintf(stderr,"getpid() failed!");
		exit(1);
	}

	//child
	if(pid == 0)
	{
		printf("[%d]:Chiled is working\n",getpid());
	}
	else//parrent
	{
		printf("[%d]:Parrent is working\n",getpid());
	}

	printf("[%d]:End!\n",getpid());
	exit(0);

}



mhr@ubuntu:~/Desktop/xitongbiancheng/test$ gcc test.c 
mhr@ubuntu:~/Desktop/xitongbiancheng/test$ ./a.out 
[13186]:Begin!
[13186]:Parrent is working
[13186]:End!
mhr@ubuntu:~/Desktop/xitongbiancheng/test$ [13187]:Chiled is working
[13187]:End!
^C
mhr@ubuntu:~/Desktop/xitongbiancheng/test$ 

注意:
注意 程序运行结果:

[13186]:Begin!
[13186]:Parrent is working
[13186]:End!
mhr@ubuntu:~/Desktop/xitongbiancheng/test$ [13187]:Chiled is working
[13187]:End!


第一部分 : 打印 Begin
第二部分:执行父进程语句 后 end
第三部分:执行子进程语句 后 end

问题1
首先 需要注意的是 父子进程的执行顺序,父子进程的执行顺序 是由程序调度器决定的,并不是我们决定,也有可能是 先执行子进程内容,再执行父进程内容。

问题2
其次 关于打印 Begin ,为什么只打印了一次 ?

关于问题1:
如果一定要子进程先运行,那么可以在 父进程语句中 sleep()一下:

#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>

int main(void)
{

	pid_t pid;

	printf("[%d]:Begin!\n",getpid());

	pid = fork();
	if(pid < 0)
	{
		fprintf(stderr,"getpid() failed!");
		exit(1);
	}

	//child
	if(pid == 0)
	{
		printf("[%d]:Chiled is working\n",getpid());
	}
	else//parrent
	{
		sleep(1);
		printf("[%d]:Parrent is working\n",getpid());
	}

	printf("[%d]:End!\n",getpid());
	exit(0);

}

mhr@ubuntu:~/Desktop/xitongbiancheng/test$ gcc test.c 
mhr@ubuntu:~/Desktop/xitongbiancheng/test$ 
mhr@ubuntu:~/Desktop/xitongbiancheng/test$ 
mhr@ubuntu:~/Desktop/xitongbiancheng/test$ ./a.out 
[13255]:Begin!
[13256]:Chiled is working
[13256]:End!
[13255]:Parrent is working
[13255]:End!
mhr@ubuntu:~/Desktop/xitongbiancheng/test$ 

关于问题2:有换行符 并 打印到行缓冲 终端
如下:

printf("[%d]:Begin!\n",getpid());

如果标准输出是终端设备,则标准输出是行缓冲,否则是全缓冲

在终端上只打印了一次 Begin 的原因在于,终端是行缓冲模式,由于后面的换行符,已经将缓冲区中内容刷新到磁盘,缓冲区已经清空。故在父进程中 会打印 Begin ,而在子进程 复制而来的缓冲区中并没有 Begin 信息,早已经在父进程中刷新,故子进程中 不会再次打印 Begin

变化1: 标准输出是终端 行缓冲 将换行符去掉

printf("[%d]:Begin!",getpid()); 

结果:

mhr@ubuntu:~/Desktop/xitongbiancheng/test$ 
mhr@ubuntu:~/Desktop/xitongbiancheng/test$ gcc test.c 
mhr@ubuntu:~/Desktop/xitongbiancheng/test$ ./a.out 
[14003]:Begin![14003]:Parrent is working
[14003]:End!
mhr@ubuntu:~/Desktop/xitongbiancheng/test$ [14003]:Begin![14004]:Chiled is working
[14004]:End!

打印两次 Begin, 此时 父进程与子进程中会分别打印一次 Begin,同理, 终端是标准输出,换行符刷新,此时没有换行符,在exit 之前 父进程的 Begin 信息存储在 缓冲区中,并没有写进磁盘。同理 复制而来的子进程的缓冲区中也有 Begin信息。所以两个进程在exit的时候 会打印两次 Begin

变化2:重定向到 全缓冲模式的文件

mhr@ubuntu:~/Desktop/xitongbiancheng/test$ 
mhr@ubuntu:~/Desktop/xitongbiancheng/test$ ./a.out > TEST_FILE 
mhr@ubuntu:~/Desktop/xitongbiancheng/test$ cat TEST_FILE 
[14084]:Begin![14084]:Parrent is working
[14084]:End!
[14084]:Begin![14085]:Chiled is working
[14085]:End!
mhr@ubuntu:~/Desktop/xitongbiancheng/test$ 

当将标准输出重定向到一个文件的时候,此时标准输出是全缓冲模式,即 不论有没有换行符,都不会刷新缓冲区,即都不会写入磁盘,也就是两个进程的缓冲区中分别都有 Begin信息,故会打印两次。

重点: fork()之前 fflush所有打开的流,避免子进程重复打印

所以 介于以上的原因,我们一般在 fork()之前 fflush()一次,刷新所有成功打开的流:

#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>

int main(void)
{

	pid_t pid;

	printf("[%d]:Begin!\n",getpid());
	
	fflush(NULL);

	pid = fork();
	if(pid < 0)
	{
		fprintf(stderr,"getpid() failed!");
		exit(1);
	}

	//child
	if(pid == 0)
	{
		printf("[%d]:Chiled is working\n",getpid());
	}
	else//parrent
	{
		//sleep(1);
		printf("[%d]:Parrent is working\n",getpid());
	}

	printf("[%d]:End!\n",getpid());
	exit(0);

}


mhr@ubuntu:~/Desktop/xitongbiancheng/test$ 
mhr@ubuntu:~/Desktop/xitongbiancheng/test$ gcc test.c 

mhr@ubuntu:~/Desktop/xitongbiancheng/test$ 
mhr@ubuntu:~/Desktop/xitongbiancheng/test$ ./a.out 
[14169]:Begin!
[14169]:Parrent is working
[14169]:End!
mhr@ubuntu:~/Desktop/xitongbiancheng/test$ [14170]:Chiled is working
[14170]:End!

mhr@ubuntu:~/Desktop/xitongbiancheng/test$ 

猜你喜欢

转载自blog.csdn.net/LinuxArmbiggod/article/details/113779321
今日推荐