由fork创建的新进程被称为子进程(child process)。
该函数被调用一次,但返回两次。两次返回的区别是子进程的返回值是0
,而
父进程的返回值则是新进程(子进程)的进程 id。将子进程id返回给父进程的理由是:因为一个进程的子进程可以多于一个,没有一个函数使一个进程可以获得其所有子进程的进程id
。对子进程来说,之所以fork返回0给它,是因为它随时可以调用
getpid()来获取自己的pid;也可以调用getppid()来获取父进程的id
。(进程id 0总是由交换进程使用,所以一个子进程的进程id不可能为0 )。
fork之后,
操作系统会复制一个与父进程完全相同的子进程,虽说是父子关系,但是在操作系统看来,他们更像兄弟关系,这2个进程共享代码空间,但是数据空间是互相独立的,子进程数据空间中的内容是父进程的完整拷贝,指令指针也完全相同,子进程拥有父进程当前运行到的位置
(两进程的程序计数器pc值相同,也就是说,子进程是从fork返回处开始执行的),但有一点不同,如果fork成功,子进程中fork的返回值是0,父进程中fork的返回值是子进程的进程号,如果fork不成功,父进程会返回错误。
可以这样想象,2个进程一直同时运行,而且步调一致,在fork之后,他们分别作不同的工作,也就是分岔了。这也是fork为什么叫fork的原因
至于那一个最先运行,可能与操作系统(调度算法)有关,而且这个问题在实际应用中并不重要,如果需要父子进程协同,可以通过原语的办法解决。
-
include <stdio.h>
-
#include <unistd.h>
-
#include <stdlib.h>
-
#include <errno.h>
-
-
int
main(
void
)
-
{
-
pid_t pid=fork();
-
if
(pid==0)
-
{
-
int
j ;
-
for
(j=0;j<10;j++)
-
{
-
printf(
"child: %d\n"
,j);
-
sleep(1);
-
}
-
}
-
else
if
(pid>0)
-
{
-
int
i;
-
for
(i=0;i<10;i++)
-
{
-
printf(
"parent: %d\n"
,i);
-
sleep(1);
-
}
-
}
-
else
-
{
-
fprintf(stderr,
"can't fork ,error %d\n"
,errno);
-
exit(1);
-
}
-
printf(
"This is the end !"
);
-
}
二、fork出的子进程和父进程的继承关系
fork出来的子进程,基本上除了进程号之外父进程的所有东西都有一份拷贝,基本就意味着不是全部,下面我们要说的是子进程从父进程那里继承了什么东西,什么东西没有继承。还有一点需要注意,子进程得到的只是父进程的拷贝,而不是父进程资源的本身。
由子进程自父进程继承到:
1.进程的资格(真实(real)/有效(effective)/已保存(saved)用户号(UIDs)和组号(GIDs))
2.环境(environment)
5.打开文件的描述符(注意对应的文件的位置由父子进程共享,这会引起含糊情况)
6.执行时关闭(close-on-exec) 标志 (译者注:close-on-exec标志可通过fnctl()对文件描述符设置,POSIX.1要求所有目录流都必须在exec函数调用时关闭。更详细说明,参见《UNIX环境高级编程》 W. R. Stevens, 1993, 尤晋元等译(以下简称《高级编程》), 3.13节和8.9节)
7.信号(signal)控制设定
8.nice值 (译者注:nice值由nice函数设定,该值表示进程的优先级,数值越小,优先级越高)
进程调度类别(scheduler class)(译者注:进程调度类别指进程在系统中被调度时所属的类别,不同类别有不同优先级,根据进程调度类别和nice值,进程调度程序可计算出每个进程的全局优先级(Global process prority),优先级高的进程优先执行)
8.进程组号
9.对话期ID(Session ID) (译者注:译文取自《高级编程》,指:进程所属的对话期(session)ID, 一个对话期包括一个或多个进程组, 更详细说明参见《高级编程》9.5节)
10.当前工作目录
11.根目录 (译者注:根目录不一定是“/”,它可由chroot函数改变)
12.文件方式创建屏蔽字(file mode creation mask (umask))(译者注:译文取自《高级编程》,指:创建新文件的缺省屏蔽字)
13.资源限制
14.控制终端
子进程所独有:
1.不同的父进程号(译者注:即子进程的父进程号与父进程的父进程号不同, 父进程号可由getppid函数得到)
2.自己的文件描述符和目录流的拷贝(译者注:目录流由opendir函数创建,因其为顺序读取,顾称“目录流”)
3.子进程不继承父进程的进程,正文(text), 数据和其它锁定内存(memory locks)(译者注:锁定内存指被锁定的虚拟内存页,锁定后,4.不允许内核将其在必要时换出(page out),详细说明参见《The GNU C Library Reference Manual》 2.2版, 1999, 3.4.2节)
5.在tms结构中的系统时间(译者注:tms结构可由times函数获得,它保存四个数据用于记录进程使用中央处理器 (CPU:Central Processing Unit)的时间,包括:用户时间,系统时间, 用户各子进程合计时间,系统各子进程合计时间)
6.资源使用(resource utilizations)设定为0
8.阻塞信号集初始化为空集(译者注:原文此处不明确,译文根据fork函数手册页稍做修改)
9.不继承由timer_create函数创建的计时器
10.不继承异步输入和输出
这里写的是对于小白来说更多的了解系统编程的文章,有写的不对的地方还恳请各位大佬指
出
错误,小编一定会多多采纳[手动多谢]。
那么,上一次我们稍微了解了一下关于系统编程的一些主要内容[没有看到的童鞋还请去上一篇文章稍微复习一下噢]。
这节课,我们先来想一想,我们为什么要学系统编程呢?原因很简单,我们要充分的利用CPU的性能,CPU和我们人类不太一样,我们人类大多数情况下,在同一时间,只能完成一件事,而CPU作为无数科学家的心血当然不会这么简单,CPU能够同时进行多个
进程
,这里的
进程
我们可以理解成任务,
设置完
子进程
的vruntime之后,检查sched_child_runs_first参数,如果为1的话,就比较
父
进程
和
子进程
的vruntime,若是
父
进程
的vruntime更小,就对换
父
、
子进程
的vruntime,这样就保证了
子进程
会在
父
进程
之前运行。如果一个
进程
从min_vruntime更小的CPU (A) 上迁移到min_vruntime更大的CPU (B) 上,可能就会占便宜了,因为CPU (B) 的运行队列中
进程
的vruntime普遍比较大,迁移过来的
进程
就会获得更多的CPU时间片。
操作系统的
进程
概述主要是介绍了
进程
的概念,
进程
的组成(
进程
实体)、
进程
的特征、
进程
的五状态模型、
进程
控制,其中重点掌握PCB、五状态模型及其状态转换。
主要的重点冷月做
出
了标识,知识点如下图(pdf版或xmind源文件请关注公众号:学长冷月,回复操作系统)。
程序是静态的存储在计算机硬盘里面的计算机代码,而
进程
是程序在数据上的一次动态执行。
进程
实体也叫
进程
映像,包括程序段、数据段、PCB。是
进程
的静态组成。
PCB,
进程
控制块,
进程
存在的唯一标识,常驻内存中。
进程
最主要、最基本的特征是动态性
ps命令一般用来显示终端信息和
进程
信息,运行命令ps -eo pid,comm,cmd可以输
出
所有
进程
的信息,e代表所有
进程
,后面三个参数是需要输
出
的信息。
第一列PID是一个整数,每一个
进程
都有一个唯一的PID来代表自己的身份,
进程
也可以根据PID来识别其他的
进程
。第二列COMMAND是这个
进程
的简称。第三列CMD是
进程
所对应的程序以及运行时所带的参数。
PID为1的
进程
是systemd...
父
子进程
之间在刚
fork
后:
相同点:全局变量、.data、.text、栈、堆、环境变量、用户 ID、宿主目录、
进程
工作目录、信号处理方式…
不同点:1.
进程
ID 2.
fork
返回值 3.
父
进程
ID 4.
进程
运行时间 5.闹钟(定时器) 6.未决信号集
进程
共享遵循原则: 读时共享,写时复制。——————全局变量
【重点】:
父
子进程
共享:1. 文件描述符(打开文件的结构体) 2. mmap 建立的映射区
特别的,
fork
之后
父
进程
先执行还是
子进程
先执行不确定。取决于内
一、
fork
入门知识
一个
进程
,包括代码、数据和分配给
进程
的资源。
fork
()函数通过系统调用创建一个与原来
进程
几乎完全相同的
进程
,也就是两个
进程
可以做完全相同的事,但如果初始参数或者传入的变量不同,两个
进程
也可以做不同的事。 一个
进程
调用
fork
()函数后,系统先给新的
进程
分配资源,例如存储数据和代码的空间。然后把原来的
进程
的所有值都复制到新的新
进程
中,只有少数值与...
MacOS/Linux下
fork
父
进程
与
子进程
的关系,在不同操作系统下是不一样的,先讨论在 MacOS 或者 Linux 系统下,我们可以使用
fork
()来探索
父
子进程
的关系。
fork
可以生成一个与当前
进程
完全一样的
子进程
,如果当前
进程
是
父
进程
,
fork
()返回的是
进程
的pid,如果是
子进程
则会返回0
import os
print('Process (%s) start...' % os.getpid())
# Only works on Unix/Linux/Mac:
pid = os.for
为什么在使用
fork
来实现管道之间的通信的时候,通常
父
子进程
会关闭一个自己用不到的文件描述符
如何使用两次
fork
来解决僵尸
进程
的问题
还有一个对使用了
fork
的程序重定向输
出
后某条语句打印两遍...
fork
创建
进程
fork
系统调用是用于创建
进程
的,并且
fork
创建的
进程
初始化状态与
父
进程
一样。(
进程
空间中的变量)系统会为
fork
的
进程
分配新的资源
fork
系统调用无参数。
fork
返回两次,(在
父
进程
中)第一次返回
子进程
id,(在
子进程
中)第二次返回0 .换句话说就是返回
子进程
id的是
父
进程
,返回0的是
子进程
。