fork函数和子进程

合集下载

fork 子进程不继承父进程打开的文件描述符

fork 子进程不继承父进程打开的文件描述符

fork 子进程不继承父进程打开的文件描述符1.引言1.1 概述概述在操作系统中,进程是指正在执行的程序的实例。

当一个进程创建子进程时,子进程会继承父进程的一些属性和资源,以便能够继续执行相同的操作。

然而,在某些情况下,子进程并不会继承父进程的所有属性和资源,其中一个重要的例子就是父进程打开的文件描述符。

文件描述符是用来标识一个文件或者文件流的抽象概念。

当一个进程需要对文件进行操作时,它必须首先打开该文件,并获得一个文件描述符。

父进程通过打开文件来获取文件描述符,而子进程在创建时则不会继承这些父进程已打开的文件描述符。

这种设计有其合理性和必要性。

首先,父进程打开的文件描述符可能会包含一些敏感信息,比如数据库连接信息、加密密钥等。

如果这些信息被子进程继承,有可能会导致安全风险。

其次,大多数情况下,子进程并不需要继承父进程的文件描述符。

子进程是作为独立的实体运行的,通常具有自己独立的文件操作需求。

虽然子进程不继承父进程的文件描述符,但是子进程可以通过其他方式来获取和打开文件。

举例来说,子进程可以使用文件路径来打开需要的文件,或者通过网络传输文件描述符等方式来获取父进程已打开的文件描述符。

因此,对于子进程来说,并不存在无法获取文件描述符的问题。

总之,子进程不继承父进程打开的文件描述符是有意而为的设计。

这一设计使得父子进程之间的资源隔离得以实现,同时也增强了系统的安全性。

在实际应用中,开发人员需要考虑到这一点,并正确处理子进程的文件操作需求。

1.2文章结构文章结构部分的内容可以描述文章的整体框架和组织结构,包括各个章节的主题和内容概要。

可以按照以下方式编写文章结构部分的内容。

文章结构部分:本文主要围绕着fork子进程不继承父进程打开的文件描述符展开讨论,旨在探讨子进程不继承父进程文件描述符的原因以及对应的影响和应用。

在引言部分,我们将通过概述对fork子进程和文件描述符的概念进行简要介绍,并给出本文的目的。

进程的创建实验报告

进程的创建实验报告

进程的创建实验报告进程的创建实验报告引言:在计算机科学领域中,进程是一个非常重要的概念。

进程是计算机程序的执行实例,它具有独立的内存空间和执行环境。

进程的创建是操作系统中一个关键的操作,本实验旨在通过编写一个简单的程序来演示进程的创建过程。

实验目的:通过实验,我们的目标是深入理解进程的创建过程,并了解操作系统是如何管理进程的。

实验步骤:1. 引入必要的头文件:在开始编写代码之前,我们需要引入一些必要的头文件。

这些头文件包括<sys/types.h>、<sys/wait.h>和<unistd.h>。

这些头文件提供了创建进程所需的函数和数据类型。

2. 创建一个子进程:在主程序中,我们使用fork()函数来创建一个子进程。

fork()函数会在当前进程的基础上创建一个新的进程,这个新进程称为子进程。

子进程和父进程几乎完全相同,只有在返回值上有所区别。

如果fork()函数返回0,表示当前进程是子进程;如果返回一个正整数,表示当前进程是父进程。

3. 子进程的执行:在子进程中,我们可以编写任意的代码来执行特定的任务。

子进程可以使用exec()函数来执行其他程序,或者执行一系列的操作。

在本实验中,我们简单地输出一条信息,以展示子进程的执行过程。

4. 父进程的执行:在父进程中,我们可以编写代码来执行其他任务,或者等待子进程的结束。

在本实验中,我们使用wait()函数来等待子进程的结束。

wait()函数会暂停父进程的执行,直到子进程结束为止。

5. 编译和运行程序:在完成代码编写后,我们需要将程序编译成可执行文件,并运行它。

我们可以使用gcc编译器来编译程序,然后运行生成的可执行文件。

实验结果:在运行程序后,我们可以观察到以下结果:子进程开始执行。

父进程等待子进程结束。

子进程结束。

父进程继续执行。

结论:通过本实验,我们成功地演示了进程的创建过程。

我们了解了操作系统是如何管理进程,并且掌握了使用fork()函数来创建子进程的方法。

exec族函数详解及循环创建子进程

exec族函数详解及循环创建子进程

exec族函数详解及循环创建⼦进程 前⾔:之前也知道exec族函数,但没有完全掌握,昨天⼜重新学习了⼀遍,基本完全掌握了,还有⼀些⽗⼦进程和循环创建⼦进程的问题,还要介绍⼀下环境变量,今天分享⼀下。

⼀、环境变量 先介绍下环境的概念和特性,再举例⼦吧。

环境变量,是指在中⽤来指定操作系统运⾏环境的⼀些参数。

通常具备以下特征: ①字符串(本质) ②有统⼀的格式:名=值[:值] ③值⽤来描述进程环境信息。

存储形式:与命令⾏参数类似。

char *[]数组,数组名environ,内部存储字符串,NULL作为哨兵结尾。

使⽤形式:与命令⾏参数类似。

引⼊环境变量表:须声明环境变量。

extern char ** environ; 环境变量跟很多东西有关系,例如接下来的exec族函数,这也是为什么要先介绍下环境变量的原因,对理解exec族函数很有帮助;例如,Linux是什么样的系统?多⽤户多任务开源系统,每个⽤户的登录信息环境变量都会记录。

举例⼀下常⽤的环境变量:PATH 可执⾏⽂件的搜索路径。

ls命令也是⼀个程序,执⾏它不需要提供完整的路径名/bin/ls,然⽽通常我们执⾏当前⽬录下的程序a.out却需要提供完整的路径名./a.out,这是因为PATH环境变量的值⾥⾯包含了ls命令所在的⽬录/bin,却不包含a.out所在的⽬录。

PATH环境变量的值可以包含多个⽬录,⽤:号隔开。

在Shell中⽤echo命令可以查看这个环境变量的值: $ echo $PATHSHELL 当前Shell,它的值通常是/bin/bash。

TERM 当前终端类型,在图形界⾯终端下它的值通常是xterm,终端类型决定了⼀些程序的输出显⽰⽅式,⽐如图形界⾯终端可以显⽰汉字,⽽字符终端⼀般不⾏。

LANG 语⾔和locale,决定了字符编码以及时间、货币等信息的显⽰格式。

HOME 当前⽤户主⽬录的路径,很多程序需要在主⽬录下保存配置⽂件,使得每个⽤户在运⾏该程序时都有⾃⼰的⼀套配置 介绍跟环境变量相关的函数: char *getenv(const char *name); //获取环境变量 int setenv(const char *name, const char *value, int overwrite); //添加或改变环境变量 int unsetenv(const char *name); //删除 ⼆、fork函数及循环创建⼦进程 先说⼀个问题,学会fork并写程序时,可能都会遇到⼀个问题如下: ./a.out的输出跑到终端上了,想过为什么?接下来我会解释这个问题。

fork函数超详解及其用法

fork函数超详解及其用法

3. 进程控制上一页第30 章进程下一页3. 进程控制3.1. fork函数#include <sys/types.h>#include <unistd.h>pid_t fork(void);fork调用失败则返回-1,调用成功的返回值见下面的解释。

我们通过一个例子来理解fork是怎样创建新进程的。

例30.3. fork#include <sys/types.h>#include <unistd.h>#include <stdio.h>#include <stdlib.h>int main(void){pid_t pid;char *message;int n;pid = fork();if (pid < 0) {perror("fork failed");exit(1);}if (pid == 0) {message = "This is the child\n"; n = 6;} else {message = "This is the parent\n"; n = 3;}for(; n > 0; n--) {printf(message);sleep(1);}return 0;}$ ./a.outThis is the childThis is the parentThis is the childThis is the parentThis is the childThis is the parentThis is the child$ This is the childThis is the child这个程序的运行过程如下图所示。

图30.4. fork父进程初始化。

父进程调用fork,这是一个系统调用,因此进入内核。

内核根据父进程复制出一个子进程,父进程和子进程的PCB信息相同,用户态代码和数据也相同。

Shell脚本编写的高级技巧使用多进程提高并发处理能力

Shell脚本编写的高级技巧使用多进程提高并发处理能力

Shell脚本编写的高级技巧使用多进程提高并发处理能力Shell脚本是一种用于自动化任务和批处理的脚本语言。

随着计算机处理能力的提升和数据量的增加,对脚本的并发处理能力的要求也越来越高。

在本文中,我们将介绍一些Shell脚本编写的高级技巧,可以利用多进程来提高并发处理能力。

一、并发处理的概念在计算机领域,当多个任务能够同时进行时,就称为并发处理。

而在Shell脚本中,利用多进程技术可以实现并发处理,提高任务的执行效率。

二、使用fork()函数创建子进程在Linux环境下,通过在Shell脚本中使用fork()函数,可以创建子进程来实现并发处理。

fork()函数会复制当前进程,使得原有进程变为父进程,而复制出的子进程可以独立执行其他的任务。

示例代码:```shell#!/bin/bashfor i in {1..5}do# 创建子进程{# 子进程执行的任务echo "子进程 $i 正在执行任务..."sleep 5} &done# 等待所有子进程执行完毕waitecho "所有任务执行完毕。

"```在上述示例中,首先通过for循环创建了5个子进程,并且每个子进程都执行了一个简单的任务。

通过在任务代码的末尾加上`&`符号,可以使得该任务在后台执行,不会阻塞其他子进程的执行。

最后通过wait命令等待所有子进程执行完毕,并输出“所有任务执行完毕”。

三、控制并发处理的数量在实际应用中,我们可能需要控制并发处理的数量,以避免资源浪费和系统负荷过大。

可以通过设置信号量来控制同时执行的子进程数量。

示例代码:#!/bin/bash# 定义并发处理的数量MAX_PROCESSES=3SEMAPHORE=0for i in {1..5}do(# 子进程执行的任务echo "子进程 $i 正在执行任务..."sleep 5# 任务执行完毕时释放信号量((SEMAPHORE--))) &# 控制并发处理的数量((SEMAPHORE++))if [ $SEMAPHORE -ge $MAX_PROCESSES ]; then waitfi# 等待剩余子进程执行完毕waitecho "所有任务执行完毕。

进程控制之fork函数

进程控制之fork函数

进程控制之fork函数⼀个现有进程可以调⽤fork函数创建⼀个新进程。

#include <unistd.h>pid_t fork( void );返回值:⼦进程中返回0,⽗进程中返回⼦进程ID,出错返回-1由fork创建的新进程被称为⼦进程(child process)。

fork函数被调⽤⼀次,但返回两次。

两次返回的唯⼀区别是⼦进程的返回值是0,⽽⽗进程的返回值则是新⼦进程的进程ID。

将⼦进程ID返回给⽗进程的理由是:因为⼀个进程的⼦进程可以有多个,并且没有⼀个函数使⼀个进程可以获得其所有⼦进程的进程ID。

fork使⼦进程得到返回值0的理由是:⼀个进程只会有⼀个⽗进程,所以⼦进程总是可以调⽤getppid以获得其⽗进程的进程ID(进程ID 0总是由内核交换进程使⽤,所以⼀个⼦进程的进程ID不可能为0)。

⼦进程和⽗进程继续执⾏fork调⽤之后的指令。

⼦进程是⽗进程的副本。

例如,⼦进程获得⽗进程的数据空间、堆和栈的副本。

注意,这是⼦进程所拥有的副本。

⽗、⼦进程并不共享这些存储空间部分。

⽗、⼦进程共享正⽂段(text,代码段)。

由于在fork之后经常跟随着exec,所以现在的很多实现并不执⾏⼀个⽗进程数据段、栈和堆的完全复制。

作为替代,使⽤了写时复制(Copy-On-Write,COW)技术。

这些区域由⽗、⼦进程共享,⽽且内核将它们的访问权限改变为只读的。

如果⽗、⼦进程中的任⼀个试图修改这些区域,则内核只为修改区域的那块内存制作⼀个副本,通常是虚拟存储器系统中的⼀“页”。

Linux 2.4.22提供了另⼀种新进程创建函数——clone(2)系统调⽤。

这是⼀种fork的泛型,它允许调⽤者控制哪些部分由⽗、⼦进程共享。

程序清单8-1中的程序演⽰了fork函数,从中可以看到⼦进程对变量所作的改变并不影响⽗进程中该变量的值。

程序清单8-1 fork函数⽰例[root@localhost apue]# cat prog8-1.c#include "apue.h"int glob = 6; /* external variable in initialized data */char buf[] = "a write to stdout\n";intmain(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 */{glob++; /* modify variables */var++;}else{sleep(2); /* parent */}printf("pid = %d, glob = %d, var = %d\n", getpid(), glob, var);exit(0);}如果执⾏此程序则得到:[root@localhost apue]# ./prog8-1a write to stdoutbefore forkpid = 13367, glob = 7, var = 89⼦进程的变量值改变了pid = 13366, glob = 6, var = 88⽗进程的变量值没有改变[root@localhost apue]# ./prog8-1 > tmp.out[root@localhost apue]# cat tmp.outa write to stdoutbefore forkpid = 13369, glob = 7, var = 89before forkpid = 13368, glob = 6, var = 88⼀般来说,在fork之后是⽗进程先执⾏还是⼦进程先执⾏是不确定的。

新手如何理解fork函数_华清远见

新手如何理解fork函数_华清远见

对全部高中资料试卷电气设备,在安装过程中以及安装结束后进行高中资料试卷调整试验;通电检查所有设备高中资料电试力卷保相护互装作置用调与试相技互术关,系电,力通根保1据过护生管高产线中工敷资艺设料高技试中术卷资0配不料置仅试技可卷术以要是解求指决,机吊对组顶电在层气进配设行置备继不进电规行保范空护高载高中与中资带资料负料试荷试卷下卷问高总题中体2资2配,料置而试时且卷,可调需保控要障试在各验最类;大管对限路设度习备内题进来到行确位调保。整机在使组管其高路在中敷正资设常料过工试程况卷中下安,与全要过,加度并强工且看作尽护下可关都能于可地管以缩路正小高常故中工障资作高料;中试对资卷于料连继试接电卷管保破口护坏处进范理行围高整,中核或资对者料定对试值某卷,些弯审异扁核常度与高固校中定对资盒图料位纸试置,卷.编保工写护况复层进杂防行设腐自备跨动与接处装地理置线,高弯尤中曲其资半要料径避试标免卷高错调等误试,高方要中案求资,技料编术试写5交、卷重底电保要。气护设管设装备线备置4高敷、调动中设电试作资技气高,料术课中并3试、中件资且卷管包中料拒试路含调试绝验敷线试卷动方设槽技作案技、术,以术管来及架避系等免统多不启项必动方要方式高案,中;为资对解料整决试套高卷启中突动语然过文停程电机中气。高课因中件此资中,料管电试壁力卷薄高电、中气接资设口料备不试进严卷行等保调问护试题装工,置作合调并理试且利技进用术行管,过线要关敷求运设电行技力高术保中。护资线装料缆置试敷做卷设到技原准术则确指:灵导在活。分。对线对于盒于调处差试,动过当保程不护中同装高电置中压高资回中料路资试交料卷叉试技时卷术,调问应试题采技,用术作金是为属指调隔发试板电人进机员行一,隔变需开压要处器在理组事;在前同发掌一生握线内图槽部纸内 故资,障料强时、电,设回需备路要制须进造同行厂时外家切部出断电具习源高题高中电中资源资料,料试线试卷缆卷试敷切验设除报完从告毕而与,采相要用关进高技行中术检资资查料料和试,检卷并测主且处要了理保解。护现装场置设。备高中资料试卷布置情况与有关高中资料试卷电气系统接线等情况,然后根据规范与规程规定,制定设备调试高中资料试卷方案。

对fork函数的理解

对fork函数的理解

对fork函数的理解前言:对于刚刚接触Unix/Linux操作系统,在Linux下编写多进程的人来说,fork 是最难理解的概念之一:它执行一次却返回两个值。

因此,本文着重从以下几个方面来使初学者加深对fork函数的理解和应用:fork函数的机制与特性、fork 函数的两次返回和父子进程的执行顺序介绍、关键字:fork函数、返回值、父进程、子进程正文:一、fork函数的机制与特性1 #include<stdio.h>2 #include<unistd.h>3 #include<stdlib.h>45 int main(void)6 {7 pid_t pid;8 if ((pid = fork()) == 00) {9 getchar();10 exit(0);11 }12 getchar ();13}14父进程成功的调用fork(8行)后将会产生一个子进程。

此时会有两个问题:1、子进程的代码从哪里来?2、子进程首次被OS调用时,执行的第一条代码是哪条代码?子进程的代码是父进程代码的一个完全相同拷贝。

事实上不仅仅是text 段,子进程中的全部进程空间都是(包括:text/data/bss/heap/commandline/envir onment)父进程空间的一个完全拷贝。

下一个问题是谁为子进程分配了内存空间?谁复制了父进程空间的内容到子空间?fork当仁不让。

事实上,fork 实现的源代码,由四部分组成:首先,为子进程分配内存空间;然后,将父进程空间的全部内容复制到分配给子进程的内存空间;接着在内核数据结构中创建并正确初始化子进程的PCB (包括两个重要信息:子进程pid,PC 的值=善后代码的第一条指令地址);最后是一段善后代码。

由于子进程的PCB已经产生,因此子进程可以被OS调度子进程首次被OS调度时,执行的第一条代码在fork 内部,不过从引用程序的角度来看,子进程首次被OS调度时,执行的第一条代码是从fork返回。

fork

fork
1)fork()系统调用是创建一个新进程的首选方式,fork的返回值要么是0,要么是非0,父进程与子进程的根本区别在于fork函数的返回值.
2)vfork()系统调用除了能保证用户空间内存不会被复制之外,它与fork几乎是完全相同的.vfork存在的问题是它要求子进程立即调用exec,
而不用修改任何内存,这在真正实现的时候要困难的多,尤其是考虑到exec调用有可能失败.
./test
count= 1
count= 1
Segmentation fault (core dumped)
分析:
通过将fork()换成vfork(),由于vfork()是共享数据段,为什么结果不是2呢,答案是:
vfork保证子进程先运行,在它调用 exec 或 exit 之后父进程才可能被调度运行.如果在调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁.
示例代码:
#include <unistd.h>
#include <stdio.h>
int main(int argc, char ** argv )
{
int pid = fork();
if(pid == -1 )
{
// print("error!");
} else if( pid = =0 ) {
3)做最后的修改,在子进程执行时,调用_exit(),程序如下:
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
int main(void)

C语言fork函数解析

C语言fork函数解析

首先看下fork的基本知识:函数原型:pid_t fork( void);返回值:若成功调用一次则返回两个值,子进程返回0,父进程返回子进程ID;否则,出错返回-1一个现有进程可以调用fork函数创建一个新进程。

由fork创建的新进程被称为子进程(child process)。

fork函数被调用一次但返回两次。

两次返回的唯一区别是子进程中返回0值而父进程中返回子进程ID。

注意要点:1、子进程是父进程的副本,它将获得父进程数据空间、堆、栈等资源的副本。

此处先简要介绍下COW(Copy-on-write)机制,大致原理如下:在复制一个对象的时候并不是真正的把原先的对象复制到内存的另外一个位置上,而是在新对象的内存映射表中设置一个指针,指向源对象的位置,并把那块内存的Copy-On-Write位设置为1.这样,在对新的对象执行读操作的时候,内存数据不发生任何变动,直接执行读操作;而在对新的对象执行写操作时,将真正的对象复制到新的内存地址中,并修改新对象的内存映射表指向这个新的位置,并在新的内存位置上执行写操作。

linux内核下fork使用COW机制工作原理:进程0(父进程)创建进程1(子进程)后,进程0和进程1同时使用着共享代码区内相同的代码和数据内存页面, 只是执行代码不在一处,因此他们也同时使用着相同的用户堆栈区。

在为进程1(子进程)复制其父进程(进程0)的页目录和页表项时,进程0的640KB页表项的属性没有改动过(仍然可读写),但是进程1的640KB对应的页表项却被设置成只读。

因此当进程1(子进程)开始执行时,对用户堆栈的入栈操作将导致页面写保护异常,从而使得内核的内存管理程序为进程1在主内存区中分配一内存页面,并把进程0中的页面内容复制到新的页面上。

从此时开始,进程1开始有自己独立的内存页面,由于此时的内存页面在主内存区,因此进程1中继续创建新的子进程时也可以采用COW技术。

内核调度进程运行时次序是随机的,进程0创建进程1后,可能先于进程1修改共享区,进程0是可读写的,在未分开前,进程1是只读的,由于两个进程共享内存空间,为了不出现冲突问题,就必须要求进程0在进程1执行堆栈操作(进程1的堆栈操作会导致页面保护异常,从而使得进程1在主内存区得到新的用户页面区,此时进程1和进程0才算是真正独立,如前面所述)之前禁止使用用户堆栈区。

Linux系统编程_8_进程控制之fork_wait_waitpid函数

Linux系统编程_8_进程控制之fork_wait_waitpid函数

Linux系统编程_8_进程控制之fork_wait_waitpid函数fork函数:#include <unistd.h>pid_t fork(void);fork⽤来创建⼀个⼦进程;特点:fork调⽤后会返回两次,⼦进程返回0,⽗进程返回⼦进程的进程ID;fork返回后,⼦进程和⽗进程都从fork函数的下⼀条语句開始运⾏;注意:fork之后,操作系统会复制⼀个与⽗进程全然同样的⼦进程,虽说是⽗⼦关系。

可是在操作系统看来,他们更像兄弟关系,这两个进程共享代码空间,可是数据空间是互相独⽴的,⼦进程数据空间中的内容是⽗进程的完整拷贝。

指令指针也全然同样。

⼦进程拥有⽗进程当前执⾏到的位置(两进程的程序计数器pc值同样,也就是说,⼦进程是从fork返回处開始执⾏的),但有⼀点不同,假设fork成功,⼦进程中fork的返回值是0,⽗进程中fork的返回值是⼦进程的进程号,假设fork不成功,⽗进程会返回错误。

能够这样想象,2个进程⼀直同⼀时候执⾏,并且步调⼀致,在fork之后,他们分别作不同的⼯作,也就是分岔了。

这也是fork为什么叫fork的原因。

⾄于⼦进程和⽗进程哪个先执⾏。

这是不确定的,取决于操作系统。

假设⽤vfork,则能够保证⼦进程先执⾏完毕后⽗进程在执⾏。

上⾯的注意中我们知道。

⼦进程数据空间中的内容是⽗进程的完整拷贝。

就是说⼦进程中对数据的操作是不会影响⽗进程的,以下的样例能够说明这⼀个特点:#include <stdio.h>#include <unistd.h>int main(){int i = 10;pid_t pid;printf("Father's pid:%d\n", getpid());pid = fork();if(pid < 0){perror("fork failure!");return -1;}else if(pid == 0){while(1){i++;printf("Child's i = %d\n", i);sleep(1);}}else{printf("Child's pis:%d\n", pid);while(1){printf("Father's i = %d\n", i);sleep(1);}sleep(1);}return 0;}执⾏结果:Father's pid:12148Child's pis:12149Father's i = 10Child's i = 11Father's i = 10Child's i = 12Father's i = 10Child's i = 13........另⼀点要注意,假设⽗进程中打开了⽂件,即内核给应⽤程序返回⼀个⽂件描写叙述符,⼦进程和⽗进程的⽂件描写叙述符所相应的⽂件表项是共享的,这意味着⼦进程对⽂件的读写直接影响⽗进程的⽂件位移量(反之同理)。

2、fork函数与进程ID

2、fork函数与进程ID

2、fork函数与进程ID1. fork函数 fork函数⽤于克隆⼀份当前的进程资源,调⽤fork函数之后,进程⼀分为⼆,并且两个进程的资源是⼀样的(只是资源内容完全⼀样,并不是同⼀份资源)。

fork函数的函数原型为:pid_t fork(void); 需要包含unistd.h,返回值pid_t类型实际上就是int型。

在调⽤fork函数之后,进程被⼀分为⼆,他们的资源都是相同的,如果在调⽤fork前,程序打开了某个⽂件,那么fork之后会出现两个⼀样的⽂件描述符(并⾮出现两个相同的⽂件),如果在调⽤fork前程序缓冲区中保存了某些数据,那么fork之后就会出现两个相同的缓冲区,⾥⾯存放的数据都是⼀模⼀样的。

在fork之后他们可以进⾏不同的操作,他们可以去修改同⼀个⽂件,也可以改变⾃⼰的缓冲区。

这个机制就好像克隆⼈⼀样,从本体克隆出来⼀个克克隆体,在克隆体被克隆出来那⼀刻,克隆⼈与本体是⼀模⼀样的,克隆⼈与本体有着相同的记忆(虽然他并没有真正经历过这些事情)。

在克隆⼈被克隆出来之后,他们经历的事情就会不⼀样了,⽐如本体去了A地,他记住了A第发⽣的事情,⽽克隆体去了B地,他便记住了B地发⽣的事情,就好像fork函数执⾏完的那⼀刻,缓冲区的数据是⼀样的,⽽之后程序的⾛向可以让缓冲区的数据不⼀样。

如果本体在以前有⼀个⼥朋友,那她同样也是克隆体的⼥朋友,但是“⼥朋友”这个⼈只有⼀个,这就好像fork之前打开了某个⽂件,在fork之后⽂件描述符变成了两份,但⽂件其实只有⼀份是相同的道理。

实际上在fork函数返回之后,两个程序已经不⼀样了,区别就是fork的返回值。

返回值⼤于0的是⽗进程,其返回值是⼦进程的ID,返回值等于0的是⼦进程(但这并不是说⼦进程的ID是0),返回值⼩于0表⽰fork失败。

通过geipid函数可以得到⾃⼰的进程ID(PID),通过getppid函数可以得到⽗进程的ID,这两个函数是不会执⾏失败的,因为在函数的描述⾥写道:“These functions are always successful.”,这两个函数的原型如下:pid_t getpid(void);pid_t getppid(void); 通过⼀个简单的例⼦说明:1 #include <stdio.h>2 #include <sys/types.h>3 #include <unistd.h>45int main(int argc, const char *argv[])6 {7int pid = 0; /* fork函数的返回值 */8int mypid = 0; /* PID */9int myppid = 0; /* PPID */1011 pid = fork();1213if (pid > 0) { /* ⽗进程 */14 sleep(1);15 printf("##################\n");16 printf("I'm parent\n");17 printf("pid = %d\n", pid);18 printf("mypid = %d\n", getpid());19 printf("myppid = %d\n\n", getppid());20 } else if (pid == 0) { /* ⼦进程 */21 printf("##################\n");22 printf("I'm child\n");23 printf("pid = %d\n", pid);24 printf("mypid = %d\n", getpid());25 printf("myppid = %d\n", getppid());26 }27 }执⾏结果如下图:2. 进程ID 由上图可知,⼦进程打印pid的值为0,⽽他⾃⼰的PID为4244,他的⽗进程ID为4243,⽗进程打印的pid值为4244,这恰好是⼦进程通过getpid函数得到的PID值,⽽⽗进程的PID为4243,⽗进程的⽗进程PID为2732。

fork()子进程与父进程之间的文件描述符问题

fork()子进程与父进程之间的文件描述符问题

在C程序中,文件由文件指针或者文件描述符表示。

ISO C的标准I/0库函数(fopen, fclose, fread, fwrite, fscanf, fprintf等)使用文件指针,UNIX 的I/O函数(open, close, read, write, ioctl)使用文件描述符。

下面重点来说下,文件描述符是如何工作的。

文件描述符相当于一个逻辑句柄,而open,close等函数则是将文件或者物理设备与句柄相关联。

句柄是一个整数,可以理解为进程特定的文件描述符表的索引。

先介绍下面三个概念,后面讲下open、close等操作以后,文件和文件描述符产生什么关系,以及fork后文件描述符的继承等问题。

文件描述符表:用户区的一部分,除非通过使用文件描述符的函数,否则程序无法对其进行访问。

对进程中每个打开的文件,文件描述符表都包含一个条目。

系统文件表:为系统中所有的进程共享。

对每个活动的open, 它都包含一个条目。

每个系统文件表的条目都包含文件偏移量、访问模式(读、写、or 读-写)以及指向它的文件描述符表的条目计数。

内存索引节点表:对系统中的每个活动的文件(被某个进程打开了),内存中索引节点表都包含一个条目。

几个系统文件表条目可能对应于同一个内存索引节点表(不同进程打开同一个文件)。

1、举例: 执行myfd = open( "/home/lucy/my.dat", O_RDONLY); 以后,上述3个表的关系原理图如下:系统文件表包含一个偏移量,给出了文件当前的位置。

若2个进程同时打开一个文件(如上图A,B)做读操作,每个进程都有自己相对于文件的偏移量,而且读入整个文件是独立于另一个进程的;如果2个进程打开同一个文件做写操作,写操作是相互独立的,每个进程都可以重写另一个进程写入的内容。

如果上面进程在open以后又执行了close()函数,操作系统会删除文件描述符表的第四个条目和系统文件表的对应条目(若指向它的描述符表唯一),并对内存索引节点表条目中的计数减1,如果自减以后变为0,说明没有其他进程链接此文件,将索引节点表条目也删除,而这里进程B也在open这个文件,所以索引节点表条目保留。

fork()函数总结

fork()函数总结

printf("n=[%d]\n", n++); return 0; }
这个例子在linux下用gcc编译,运行结果如下:
fork! [child]i am child! [child]getpid=[7422] [child]pid=[0] n=[0] [parent]i am parent! [parent]getpid=[7421] [parent]pid=[7422] n=[0]
表,该文件表是由内核维护的,两个进程共享文件状态,偏移量等。这一点很重要,当在父进程中关闭文件时,子进程的文件描述符仍然
有用,相应的文件表也不会被释放。(3)为了提高效率,fork后并不立即复制父进程空间,采用了COW (Copy -On-W rite);当父子进程
任意之一,要修改数据段、堆、栈时,进行复制操作,但仅复制修改区域;)。父子进程间共享的存储空间只有代码段(只读的,且仅共
享fork()后面的代码段)。子进程和父进程继续执行fork调用之后的指令。(4)fork之后,这两个进程执行没有固定的先后顺序,哪个进程先
执行要看系lude <SYS types.h> #include <UNISTD.H> #include <STDIO.H> int main() { pid_t pid; static int n = 0; printf("fork!\n"); switch (pid = fork()) { case -1: { /* ..pid.-1.fork.... */ /* ........ */ /* .......... */ perror("The fork failed!"); break; } case 0: { /* pid.0.... */ printf("[child]i am child!\n"); printf("[child]getpid=[%d]\n", getpid() ); printf("[child]pid=[%d]\n", pid ); break; } default: { /* pid..0.... */ printf("[parent]i am parent!\n" ); printf("[parent]getpid=[%d]\n",getpid() ); printf("[parent]pid=[%d]\n",pid ); break; } }

操作系统 fork()

操作系统 fork()
#include<unistd.h>//函数fork().getpid()定义
void main (void)
{
int x=5;
if( fork( ) )
{
x+=30;
printf ("%d\n",x);
}
else
printf("%d\n",x);
printf("%d\n",x);
}
1.2预测结果:
administrator@ubuntu:~/yanhong$ ./a.out
5
35
35
5
5
将第一个printf("%d\n",x);----printf("%d ",x);后得到B程序
B:
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int y=9;
if( fork( ) )
{
x+=30;
printf ("%d\n",x);
}
else
{
printf("%d\n",y);
printf("%d\n",x);
}
printf("%d\n",x);
}
结果::
administrator@ubuntu:~/yanhong$ vi 4.c
administrator@ubuntu:~/yanhong$ cc 4.c
实验心得:
(一)对进程的理解:
一个进程包含三个元素:

Fork函数

Fork函数

Fork函数计算机程序设计中的分叉函数。

一个进程,包括代码、数据和分配给进程的资源。

fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。

进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。

然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。

相当于克隆了一个自己。

返回值:若成功调用一次则返回两个值,子进程返回0,父进程返回子进程标记;否则,出错返回-1一个现有进程可以调用fork函数创建一个新进程。

由fork创建的新进程被称为子进程(child process)。

fork函数被调用一次但返回两次。

两次返回的唯一区别是子进程中返回0值而父进程中返回子进程ID。

子进程是父进程的副本,它将获得父进程数据空间、堆、栈等资源的副本。

注意,子进程持有的是上述存储空间的“副本”,这意味着父子进程间不共享这些存储空间。

UNIX将复制父进程的地址空间内容给子进程,因此,子进程有了独立的地址空间。

在不同的UNIX (Like)系统下,我们无法确定fork之后是子进程先运行还是父进程先运行,这依赖于系统的实现。

所以在移植代码的时候我们不应该对此作出任何的假设。

一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。

然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。

相当于克隆了一个自己。

我们来看一个例子:int main (){pid_t fpid; //fpid表示fork函数返回的值int count=0;fpid=fork();if (fpid < 0)printf("error in fork!");else if (fpid == 0) {printf("i am the child process, my process id is %d/n",getpid());printf("我是爹的儿子/n");//对某些人来说中文看着更直白。

linux创建进程的方法

linux创建进程的方法

linux创建进程的方法
在Linux系统中,创建进程的方法有多种,其中最常用的方法是使用fork()系统调用。

下面是详细的创建进程的步骤:
1. 导入头文件
在程序中导入头文件<unistd.h>,该头文件中包含了fork()系统调用的声明。

2. 调用fork()系统调用
使用fork()系统调用创建一个新的进程。

fork()系统调用会返回两次,一次在父进程中返回子进程的PID,另一次在子进程中返回0。

3. 判断进程类型
根据fork()系统调用的返回值判断当前进程是父进程还是子进程。

如果返回值大于0,则表示当前进程是父进程,返回值为子进程的PID;如果返回值为0,则表示当前进程是子进程。

4. 编写父进程代码
在父进程中编写需要执行的代码。

通常情况下,父进程会等待子进程执行完毕后再继续执行。

5. 编写子进程代码
在子进程中编写需要执行的代码。

通常情况下,子进程会执行一些与父进程不同的操作。

6. 退出进程
在进程执行完毕后,使用exit()系统调用退出进程。

在父进程中,可以使用wait()系统调用等待子进程执行完毕后再退出。

以上就是在Linux系统中创建进程的详细步骤。

需要注意的是,创建进程时需要
注意进程间的通信和同步问题,以确保程序的正确性和稳定性。

fork()的用法

fork()的用法

fork()的用法
fork() 是一个用于创建新进程的系统调用。

具体来说,它会复制当前进程,然后创建一个与原进程几乎完全相同的新进程。

新进程(子进程)会继承父进程的所有资源,包括代码、数据和系统资源。

fork() 的基本用法如下:
1. 调用 fork() 函数,它会返回两次:一次是在父进程中,返回新创建子进程的 PID;另一次是在子进程中,返回 0。

2. 在父进程中,fork() 返回新创建子进程的 PID,可以通过这个 PID 对子进程进行操作。

3. 在子进程中,fork() 返回 0,可以通过返回值来区分当前是父进程还是子进程。

fork() 的常见用法包括:
1. 创建新的子进程:通过调用 fork() 函数,可以创建一个与原进程几乎完全相同的新进程。

新进程会继承父进程的所有资源,包括代码、数据和系统资源。

2. 实现多线程:fork() 可以用来实现多线程编程。

在每个线程中调用 fork() 函数,可以创建多个子进程,从而实现并发执行。

3. 实现并行计算:通过 fork() 函数创建多个子进程,每个子进程执行不同的任务,可以实现并行计算,提高程序的执行效率。

需要注意的是,fork() 函数的使用需要谨慎,因为它涉及到进程的创建和复制。

如果使用不当,可能会导致资源泄漏、竞争条件等问题。

因此,在使用fork() 函数时需要仔细考虑程序的逻辑和安全性。

父子进程的说法

父子进程的说法

父子进程的说法
"父子进程"是指在操作系统中,由一个进程创建(通常是通过fork系统调用)的进程关系。

这种关系通常用于实现并发执行或并行计算,允许父进程和子进程在独立的执行空间中运行。

以下是关于父子进程的一些说明:
1.父进程:执行fork系统调用的进程称为父进程。

父进程在创建子进程后,通常会继续执行一些任务,或者等待子进程完成执行。

2.子进程:通过fork系统调用创建的新进程称为子进程。

子进程是父进程的副本,拥有独立的内存空间,但通常会继承父进程的代码段、数据段、文件描述符等信息。

3.进程独立性:父进程和子进程之间是相对独立的,它们可以并发执行,互不影响。

子进程的修改通常不会影响父进程,反之亦然。

4.通信机制:父子进程之间可以通过进程间通信(Inter-Process Communication,IPC)来进行数据交换。

常见的IPC方法包括管道、共享内存、消息队列等。

5.等待子进程:父进程通常会使用wait系统调用等待子进程的结束,并获取子进程的退出状态。

这样可以确保父进程在子进程执行完毕后进行进一步的处理。

在多进程的环境中,父子进程的概念非常重要,它们共同构成了并发执行的基础。

在Unix/Linux等操作系统中,fork系统调用是实现多进程的一种常见方式。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

Fork函数
函数pid_t fork(void)
正确返回:在父进程中返回子进程的进程号,在子进程中返回0
错误返回:-1
子进程是父进程的一个拷贝。

即,子进程从父进程得到了数据段和堆栈段的拷贝,这些需要分配新的内存;而对于只读的代码段,通常使用共享内存的方式访问。

fork返回后,子进程和父进程都从调用fork函数的下一条语句开始执行。

父进程与子进程的不同之处在于:fork的返回值不同——父进程中的返回值为子进程的进程号,而子进程为0。

以下是fork的两个示例程序:
//fork.c
#include <stdio.h>
#include <unistd.h>
void main ()
{
int pid;
//printf("Process [%d] begin",getpid()); //print twice
printf("Process [%d] begin\n",getpid()); //print once
//由于fork时pc等值的拷贝,子进程只会从fork处开始执行
pid = fork();
if (pid < 0)
printf("error in fork!");
else if (pid == 0)
printf("I'm child process, my pid is %d\n", getpid());
else
printf("I'm parent process, my pid is %d\n", getpid());
printf("Process [%d] end\n",getpid());
return;
}
输出结果:
使用printf("Process [%d] begin\n",getpid())时
Process [11155] begin
I'm parent process, my pid is 11155
Process [11155] end
I'm child process, my pid is 11156
Process [11156] end
使用printf("Process [%d] begin",getpid())时
Process [11054] beginI'm parent process, my pid is 11054
Process [11054] end
Process [11054] beginI'm child process, my pid is 11055
Process [11055] end
不同的输出结果是因为Printf的缓冲机制。

printf某些内容时,操作系统仅仅是把该内容放到stdout的缓冲队列里,并没有实际写到屏幕上。

但是,只要看到有"\n"则会立即刷新stdout,因此就马上能够打印了。

运行了printf("string") 后,string 仅仅被放到了缓冲里,再运行到fork时,缓冲里的string 被子进程继承了,因此在子进程度stdout缓冲里面就也有了string。

而运行printf("string\n")后,string 被立即打印到了屏幕上,之后fork到的子进程里的stdout缓冲里不会有string 内容。

实际上,fork语句之前的指令在子进程中不会再执行。

//son_2_father.c
#include <stdio.h>
#include <unistd.h>
int main()
{
int i;
printf("Process\t[%d] begin\n",getpid());
for( i = 0; i < 3; i++)
{
int pid = fork();
if(pid == 0)
printf("son\t[%d] create [%d]\n", getpid(), pid);
else
printf("father\t[%d] create [%d]\n", getpid(), pid);
}
printf("Process\t[%d] finish\n",getpid());
return 0;
}
输出结果如下:
Process [10026] begin
father [10026] create [10027]
son [10027] create [0]
father [10026] create [10028]
father [10027] create [10029]
father [10026] create [10030]
Process [10026] finish
father [10027] create [10031]
Process [10027] finish
son [10028] create [0]
father [10028] create [10032]
Process [10028] finish
son [10032] create [0]
Process [10032] finish
son [10030] create [0]
Process [10030] finish
son [10031] create [0]
son [10029] create [0]
Process [10031] finish
father [10029] create [10033]
Process [10029] finish
son [10033] create [0]
Process [10033] finish
我们可以将此进程的执行过程表示为上图,子进程fork返回0,但是fork之后便成为了父进程,再次fork就可以得到子进程。

最后,需要注意,派生子进程的进程,即父进程,其pid不变,fork之后父子进程除非采用了同步手段,否则不能确定谁先运行,也不能确定谁先结束。

参考文献:
/guichen83/article/details/4160697
/blog/static/18056918120113134516506/。

相关文档
最新文档