fork 与vfork

-----------------

缓冲区被复制引发的问题

其中的示例代码我跑了一下,没有得到文中的结果。

分析:printf("before fork!!\n");应该为printf("before fork!!");

           遇到\n就会马上输出了。  

  

 

-------------------------

一、fork系统调用

1、函数的声明:

[cpp]
  1.  #include <unistd.h> 
  2. pid_t fork(void);  

2、返回值:

    fork函数调用一次,将会返回两次(返回给主进程为新创建的子进程的进程ID,返回给子进程的是0)。当进程创建失败时候,fork返回值为-1。

  • 因为父进程种可能有多个子进程,但没有一个函数可以获得所有子进程的进程ID,所以我们通过fork调用时候将新创建的进程的进程ID返回给主进程。
  • 由于应用程序中创建的进程的进程ID不可能为0,且我们知道进程ID是一个非负值,所以为了在后续代码中区分当前进程是主进程还是新创建的子进程,我们需要通过返回值来区分,因此fork将在子进程中返回0;

3、函数详细说明

1、子进程是父进程的副本   

子进程通过fork创建成功以后。子进程和父进程都将继续执行fork调用之后l的指令,子进程是父进程的副本(子进程获得父进程的数据空间:堆、栈副本)。这里我们特别要注意的是,子进程获得的是父进程的副本,而不是父子进程共享数据空间。但父子进程将会共享正文段。如下程序所示:

[cpp]
 
  1. #include <unistd.h>  
  2. #include <stdio.h>   
  3. int main()  
  4. {  
  5.     int a = 9;  
  6.         pid_t pid = -1;  
  7.   
  8.     if((pid = fork()) < 0)  
  9.     {  
  10.         //如果进程ID小于零则报错,退出  
  11.         printf("fork error!!\n");  
  12.         return -1;  
  13.     }  
  14.     else if(pid == 0)  
  15.     {  
  16.          //我们在子进程中将会获得主进程中自动变量a的一个副本,我们在子进程中a++操作将操作的是子进程中的副本而不是父进程中的自动变量a的值。 因此父进程中的变量a的值将不会改变。  
  17.         a++;  
  18.     }  
  19.     return 0;  
  20. }  
2、缓冲区被复制引发的问题
    当我们在调用fork之前如果使用了标准I/O(带缓冲的I/O),则在使用fork之前最好刷新(或冲洗)标准输出流。因为在fork创建子进程后,系统会为子进程复制父进程数据空间以及标准输出缓冲区。而如果不刷新标准输出缓冲区,则调用fork时候,之前通过标准输出流输出的数据还在缓冲区中,则子进程也就拥有了一份缓冲区的副本。如下程序所示:
  1. #include <stdio.h>  
  2. #include <unistd.h>  
  3.   
  4. int main()  
  5. {  
  6.     pid_t pid = -1;  
  7.   
  8.     //printf 带缓冲的标准输出,当输出以后,数据将仍就存留在缓冲区中。随后,子进程会复制缓冲区中的数据。所以在子进程中将会再次输出: before fork  
  9.     printf("before fork!!\n");  
  10.   
  11.     if((pid = fork()) < 0)  
  12.     {  
  13.         //如果进程ID小于零则报错,退出  
  14.         printf("fork error!!\n");  
  15.         return -1;  
  16.     }  
  17.     else if(pid == 0)  
  18.     {  
  19.        //子进程中我们什么都没有做  
  20.     }  
  21.   
  22.     return 0;  
  23. }  
  如果没有改变标准输出(默认为终端)运行该程序,输出将为(该输出为调用fork之前,主进程中的printf输出):

  1. before fork!!  
而如果我们将该程序的标准输出重定向到文件中,我们将得到以下结果(第一个为调用fork之前,主进程中的printf输出,第二个为缓冲区复制后在子进程中的输出):

  1. before fork!!  
  2. before fork!!  
3、标准输出重定向:

如果我们重定向了父进程的标准输出,那么子进程的标准输出也会被重定向。即:fork会将父进程中所有打开的文件描述符都复制到子进程中。

4、文件共享

从上面所述,我们已经了解到调用fork后,系统会将父进程中所有打开的文件描述符复制到子进程中。父进程与子进程将会共享所打开的文件表项,则父进程与子进程共享同一个文件偏移量。如果我们要满足子进程与父进程之间文件访问的同步,则在fork调用之后处理文件描述符有两种常见的情况:

    • 父进程等待子进程完成。
    • 父子进程各自执行不同的程序段。即,虽然子进程会复制父进程的文件描述符,但可以在子进程程序段开始关闭这些打开但不再需要的文件描述符,则可以避免父子进程之间的互相影响。
5、fork的两种用法:
    • 一个父进程希望自己被复制,使得父子进程同时执行不同的程序段,这在网络程序设计中是常见的。例如:父进程一直等待客户端的连接请求。当请求到达时候,父进程调用fork,使子进程处理刚到达的请求,而父进程继续等待下一个请求的到达。
    • 一个进程要执行一个不同的程序,这对shell是常见的。子进程从fork返回后立即调用exec。
6、另外的一点话:

某些操作系统讲fork与exec合并在一起,并称其为spawn。UNIX将其分开,可以使在fork和 exec之间做一些其他的必要的操作。如,重定向标准输出、设置用户ID等。另外有些时候,在我们调用fork之后没有必要执行exec。所以,Unix系统中并没有将fork与exec合并(UNIX在高级实现时候,选项组中确实包括了spawn接口,但是该接口并不打算代替fork和exec)。

二、vfork函数

1、函数声明:

  1. #include <sys/types.h>  
  2. #include <unistd.h>  
  3.   
  4. pid_t vfork(void);  

2、返回值:

vfork函数的返回值与fork函数返回值相同,参见fork函数返回值部分的说明。

3、函数详细说明:

1、创建进程的目的

vfork函数用于创建一个新进程,新进程的目的是执行(exec)一个新程序。

2、与fork函数一样都创建一个子进程。但它并不将父进程的地址空间完全复制到子进程中。因为子进程会立即调用exec函数,于是也就没有必要保存父进程的地址空间了。

3、vfork函数创建的子进程中,在调用exec之前,子进程是在父进程空间中执行的。因此,子进程会与父进程共享堆、栈,子进程中改变父进程中的变量会引起父进程中变量值的修改,如下:

  1. #include <stdio.h>  
  2. #include <unistd.h>  
  3.   
  4. int main()  
  5. {  
  6.     int a = 9;  
  7.     pid_t pid = -1;  
  8.   
  9.     if((pid = vfork()) < 0)  
  10.     {  
  11.         //如果进程ID小于零则报错,退出  
  12.         printf("vfork error!!\n");  
  13.         return -1;  
  14.     }  
  15.     else if(pid == 0)  
  16.     {  
  17.         //我们在子进程中修改a的值,会改变父进程中自动变量a的值  
  18.         a++;  
  19.         printf("child process: a = %d  ppid = %d\n",a,getppid());  
  20.         _exit(0);  
  21.     }  
  22.     else  
  23.     {  
  24.         //我们在父进程中打印变量a的值。  
  25.         printf("parent process: a = %d  child_pid = %d\n",a,pid);  
  26.     }  
  27.   
  28.     return 0;  
  29.   
  30. }  
输出结果如下:
  1. child process: a = 10  ppid = 2893  
  2. parent process: a = 10  child_pid = 2894  
从上面例子我们可以看出父进程中自动变量a的值被改变了。

4、vfork与fork之间的另外一个区别:

前面我们已经提到vfork与fork之间的一个区别(是否会复制父进程的地址空间)。vfork与fork之间的另外一个区别是,vfork会保证子进程首先运行,在调用exec或者exit之后,父进程才可能被调度运行。如果在这些函数(exec、exit)之前有依赖于父进程的进一步动作,则会导致死锁

5、子进程中exit的使用

【3D应力敏感度分析拓扑优化】【基于p-范数全局应力衡量的3D敏感度分析】基于伴随方法的有限元分析和p-范数应力敏感度分析(Matlab代码实现)内容概要:本文档介绍了基于伴随方法的有限元分析p-范数全局应力衡量的3D应力敏感度分析,并结合拓扑优化技术,提供了完整的Matlab代码实现方案。该方法通过有限元建模计算结构在载荷作用下的应力分布,采用p-范数对全局应力进行有效聚合,避免传统方法中应力约束过多的问题,进而利用伴随法高效求解设计变量对应力的敏感度,为结构优化提供关键梯度信息。整个流程涵盖了从有限元分析、应力评估到敏感度计算的核心环节,适用于复杂三维结构的轻量化高强度设计。; 适合人群:具备有限元分析基础、拓扑优化背景及Matlab编程能力的研究生、科研人员工程技术人员,尤其适合从事结构设计、力学仿真多学科优化的相关从业者; 使用场景及目标:①用于实现高精度三维结构的应力约束拓扑优化;②帮助理解伴随法在敏感度分析中的应用原理编程实现;③服务于科研复现、论文写作工程项目中的结构性能提升需求; 阅读建议:建议读者结合有限元理论优化算法知识,逐步调试Matlab代码,重点关注伴随方程的构建p-范数的数值处理技巧,以深入掌握方法本质并实现个性化拓展。
下载前必看:https://pan.quark.cn/s/9f13b242f4b9 Android 平板设备远程操控个人计算机的指南 Android 平板设备远程操控个人计算机的指南详细阐述了如何运用 Splashtop Remote 应用程序达成 Android 平板设备对个人计算机的远程操控。 该指南被划分为四个环节:首先,在个人计算机上获取并部署 Splashtop Remote 应用程序,并设定客户端密码;其次,在 Android 平板设备上获取并部署 Splashtop Remote 应用程序,并之建立连接至个人计算机的通道;再次,在 Splashtop Remote 应用程序中识别已部署个人计算机端软件的设备;最后,运用平板设备对个人计算机实施远程操控。 关键点1:Splashtop Remote 应用程序的部署配置* 在个人计算机上获取并部署 Splashtop Remote 应用程序,可通过官方网站或其他获取途径进行下载。 * 部署结束后,必须输入客户端密码,该密码在平板控制计算机时用作验证,密码长度至少为8个字符,且需包含字母数字。 * 在配置选项中,能够设定是否在设备启动时自动运行客户端,以及进行互联网搜索设置。 关键点2:Splashtop Remote 应用程序的 Android 版本获取部署* 在 Android 平板设备上获取并部署 Splashtop Remote 应用程序,可通过 Google Play Store 或其他获取途径进行下载。 * 部署结束后,必须输入客户端密码,该密码用于连接至个人计算机端软件。 关键点3:运用 Splashtop Remote 远程操控个人计算机* 在 Splashtop Remote 应用程序中识别...
先看效果: https://pan.quark.cn/s/7baef05d1d08 在信息技术范畴内,语音识别是一项核心的技术,它赋予计算机或设备解析和处理人类语音输入的能力。 本研究项目运用了MFCC(Mel Frequency Cepstral Coefficients)VQ(Vector Quantization)算法,借助VC++6.0的MFC(Microsoft Foundation Classes)库,开发出一个图形用户界面(GUI),从而达成基础的语音识别功能。 接下来将具体分析这些技术及其应用。 **MFCC特征提取**MFCC是语音信号处理中的一个标准方法,用于将复杂的语音波形转换成一组便于处理的数据参数。 MFCC模拟人类听觉系统对声音频率的感知模式,通过梅尔滤波器组对声音频谱进行分段处理,进而计算每个滤波器组的倒谱系数。 该过程包含以下环节:1. **预加重**:旨在削弱人声的低频响应部分,同时增强高频成分的强度。 2. **分帧和窗函数**:将语音信号分割成多个短时帧,并应用窗函数以降低帧帧之间的相互干扰。 3. **梅尔尺度滤波**:采用梅尔滤波器组对每一帧进行剖析,获取梅尔频率谱。 4. **取对数**:鉴于人耳对声音强度的感知呈现非线性特征,因此对梅尔频率谱取对数操作以更好地符合人类听觉系统。 5. **离散余弦变换(DCT)**:对对数谱实施DCT运算,提取主要特征,通常选取前12-20个系数作为MFCC特征。 6. **动态特性**:为了捕捉语音的时域变化特征,还可计算MFCC特征的差分值和二阶差分值。 **VQ识别算法**VQ是一种数据压缩方法,在语音识别领域中常用于特征矢量的量化处理。 其基本理念是将高维度的MFCC特征向量映射到一个小型、预...
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值