P10 Linux进程编程 fork创建子进程

目录

前言 

 01 fork()创建子进程

示例 1使用 fork()创建子进程。

02 fork创建新进程时发生了什么事?

 2.1 父、子进程中对应的文件描述符指向了相同的文件表 


前言 

                              

🎬 个人主页:@ChenPi

🐻推荐专栏1: 《Linux C应用编程(概念类)_@ChenPi的博客-CSDN博客》✨✨✨ 

🔥 推荐专栏2: 《C++_@ChenPi的博客-CSDN博客》✨✨✨

🛸推荐专栏3: ​​​​​​《链表_@ChenPi的博客-CSDN博客 》 ✨✨✨
🌺本篇简介  :  上一章我们讲了Linux进程的概念以及获取进程ID号和获取父进程的ID

                          这一章我们引进新的概念,父子进程

Linux 是一个多用户多任务的操作系统,每个用户可以同时运行多个程序

进程是程序运行的主体,包括进程的创建,调度和消亡的整个过程

当用户执行一个指令或者启动一个程序时,就创建了一个进程

一个运行的程序也可能有多个进程。

每个进程将被分配各种资源    

 01 fork()创建子进程

一个现有的进程可以调用 fork()函数创建一个新的进程,

调用 fork()函数的进程称为父进程,

由 fork()函 数创建出来的进程被称为子进程(child process),

fork()函数原型如下所示(fork()为系统调用):

2.1 fork()函数原型:

#include <unistd.h> 
 
pid_t fork(void); 

返回值: 

  1. 失败返回-1 不创建子进程,并设置 errno 
  2. 父进程的返回值pid为子进程的pid号,子进程的返回值为0

在诸多的应用中,创建多个进程是任务分解时行之有效的方法

  1. 譬如,某一网络服务器进程可在监听客 户端请求的同时,为处理每一个请求事件而创建一个新的子进程,与此同时,服务器进程会继续监听更多的 客户端连接请求
  2. 在一个大型的应用程序任务中,创建子进程通常会简化应用程序的设计,同时提高了系统 的并发性(即同时能够处理更多的任务或请求多个进程在宏观上实现同时运行)。

理解 fork()系统调用的关键在于,完成对其调用后将存在两个进程,

一个是原进程(父进程)、另一个 则是创建出来的子进程,

并且每个进程都会从 fork()函数的返回处继续执行,会导致调用 fork()返回两次值,

子进程返回一个值、父进程返回一个值,所以fork返回值后面的代码块也会调用两次

在程序代码中,可通过返回值来区分是子进程还是父进程。

fork()调用成功后,将会在父进程中返回子进程的 PID,而在子进程中返回值是 0;

如果调用失败,父进 程返回值-1,不创建子进程,并设置 errno。

fork()调用成功后,子进程和父进程会继续执行 fork()调用之后的指令,子进程、父进程各自在自己的进程空间中运行。

事实上,子进程是父进程的一个副本

  1. 譬如子进程拷贝了父进程的数据段、堆、栈以及继承 了父进程打开的文件描述符
  2. 父进程与子进程并不共享这些存储空间
  3. 这是子进程对父进程相应部分存储 空间的完全复制
  4. 执行 fork()之后,每个进程均可修改各自的栈数据以及堆段中的变量,而并不影响另一个进程

示例 1使用 fork()创建子进程。

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
 
int main()
{
    int pid =  fork(); //父进程的返回值pid为子进程的pid号,子进程的返回值为0
    switch (pid)
    {
        case -1:
            printf("fork error\n");
            break;
        case 0:
            printf("this is child ;PID = %d\n",getpid());
            break;
        default:
            printf("this is father;PID = %d\n",getpid());
            break;
    }
 
    return 0;
}

 

 从打印结果可知,fork()之后的语句被执行了两次,所以 switch…case 语句被执行了两次

  1. 第一次进入 到了"case 0"分支,表示进入子进程
  2. 第二次进入到了 default 分支,表示当前处于父进程
  3. 父进程的返回值pid为子进程的id号,子进程的返回值为0 

02 fork创建新进程时发生了什么事?

调用 fork()函数之后,子进程会获得父进程所有文件描述符的副本

这些副本的创建方式类似于 dup(), 这也意味着父、子进程对应的文件描述符均指向相同的文件表,如下图所示:

由此可知

子进程拷贝了父进程的文件描述符表,使得父、子进程中对应的文件描述符指向了相同的文件表

也意味着父、子进程中对应的文件描述符指向了磁盘中相同的文件,

因而这些文件在父、子进程间实 现了共享,

譬如,如果子进程更新了文件偏移量,那么这个改变也会影响到父进程中相应文件描述符的位置 偏移量。  

 2.1 父、子进程中对应的文件描述符指向了相同的文件表 

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#define MY_FILE "./file.txt"

int main()
{
	char buf [128];
	int fd = open(MY_FILE,O_RDWR|O_CREAT,0600);
	if(-1 == fd)
	{
		perror("open error? :");
		exit(-1);
	}
	int pid = fork();
	switch (pid)
	{
		case -1:
			printf("fork error\n");
			break;
		case 0:
			for(int i = 0;i<4;i++)
				write(fd," child",strlen(" child"));
			close(fd);
			break;
		default:
			for(int i = 0;i<4;i++)
				write(fd," father",strlen(" father"));
			close(fd);
			break;
	}
	return 0;
}

上述代码中,父进程 open 打开文件之后,才调用 fork()创建了子进程,

所以子进程了继承了父进程打 开的文件描述符 fd,我们需要验证的便是两个进程对文件的写入操作是分别各自写入、还是每次都在文件 末尾接续写入

有上述测试结果可知,此种情况下

父、子进程分别对同一个文件进行写入操作,结果是接续写

不管是父进程,还是子进程,在每次写入时都是从文件的末尾写入很像使用了 O_APPEND 标志的效果。

其原 因也非常简单

子进程继承了父进程的文件描述符,两个文件描述符都指向了一个相同的文件表,意味着它们的文件偏移量是同一个、绑定在了一起,相互影响,子进程改变了文件的位置 偏移量就会作用到父进程,同理,父进程改变了文件的位置偏移量就会作用到子进程

但是执行结果不一定是父进程先执行或者子进程先执行,这个不一定的,这个要看系统的调度了 
 

  • 28
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@ChenPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值