目录
前言
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 不创建子进程,并设置 errno
- 父进程的返回值pid为子进程的pid号,子进程的返回值为0
在诸多的应用中,创建多个进程是任务分解时行之有效的方法
- 譬如,某一网络服务器进程可在监听客 户端请求的同时,为处理每一个请求事件而创建一个新的子进程,与此同时,服务器进程会继续监听更多的 客户端连接请求
- 在一个大型的应用程序任务中,创建子进程通常会简化应用程序的设计,同时提高了系统 的并发性(即同时能够处理更多的任务或请求多个进程在宏观上实现同时运行)。
理解 fork()系统调用的关键在于,完成对其调用后将存在两个进程,
一个是原进程(父进程)、另一个 则是创建出来的子进程,
并且每个进程都会从 fork()函数的返回处继续执行,会导致调用 fork()返回两次值,
子进程返回一个值、父进程返回一个值,所以fork返回值后面的代码块也会调用两次
在程序代码中,可通过返回值来区分是子进程还是父进程。
fork()调用成功后,将会在父进程中返回子进程的 PID,而在子进程中返回值是 0;
如果调用失败,父进 程返回值-1,不创建子进程,并设置 errno。
fork()调用成功后,子进程和父进程会继续执行 fork()调用之后的指令,子进程、父进程各自在自己的进程空间中运行。
事实上,子进程是父进程的一个副本
- 譬如子进程拷贝了父进程的数据段、堆、栈以及继承 了父进程打开的文件描述符
- 父进程与子进程并不共享这些存储空间
- 这是子进程对父进程相应部分存储 空间的完全复制
- 执行 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 语句被执行了两次
- 第一次进入 到了"case 0"分支,表示进入子进程
- 第二次进入到了 default 分支,表示当前处于父进程
- 父进程的返回值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 标志的效果。
其原 因也非常简单
子进程继承了父进程的文件描述符,两个文件描述符都指向了一个相同的文件表,意味着它们的文件偏移量是同一个、绑定在了一起,相互影响,子进程改变了文件的位置 偏移量就会作用到父进程,同理,父进程改变了文件的位置偏移量就会作用到子进程
但是执行结果不一定是父进程先执行或者子进程先执行,这个不一定的,这个要看系统的调度了