创建进程 fork()函数的基本使用 父子进程之间的关系
来源:互联网 发布:linux sudo命令 编辑:程序博客网 时间:2024/03/29 16:30
摘要:本文详解介绍fork()函数的基本使用,以及父子进程之间的关系.子进程对变量的改变不会影响到父进程、子进程对父进程文件流缓冲区的处理和子进程对父进程打开的文件描述符的处理.
创建进程
1.fork()函数
函数定义:#include <unistd.h>
pid_t fork(void);
返回值:如果返回值大于零,表明处于父进程上下文环境中,返回值是子进程的ID.如果返回值是零,表明处于子进程上下文环境中.其他返回值(小于零)表明调用fork()函数出错,仍处于父进程上下文环境中.
函数说明:
由fork()函数创建的新进程被称为子进程.fork()函数被调用一次,但返回两次,两次的返回值不同,子进程的返回值是0,父进程的返回值是新进程的进程ID.
一个进程的子进程可以有多个,并且没有一个函数使一个进程可以获得其所有子进程的进程ID.
一个进程只会有一个父进程,所以任意一个子进程都可以通过调用getppid()函数获取其父进程的进程ID.
fork()函数调用成功后,将为子进程申请PCB和用户内存空间.子进程是父进程的副本.在用户空间将复制父进程用户空间所有数据(代码段、数据段、BBS、堆、栈),复制父进程内核空间PCB中的绝大多数信息.子进程从父进程继承下例属性:有效用户、组号、进程组号、环境变量、对文件的执行时关闭标志、信号处理方式设置、信号屏蔽集合、当前工作目录、根目录、文件模式掩码、文件大小限制和打开的文件描述符(特别注意:共用同一文件表项).
子进程在创建后和父进程同时执行,竞争系统资源,谁先谁后,取决于内核所使用调度算法.子进程的执行位置为fork返回位置.
2.创建子进程
例子1:演示fork函数的基本使用方法.#include <stdio.h>#include <unistd.h>#include <sys/types.h>int main(){ pid_t pid; if((pid=fork())==-1) printf("fork error\n"); printf("a example of fork,pid = %d\n",getpid()); return 0;}
输出:
:a example of fork,pid = 2798:a example of fork,pid = 2797
从例子1可以看出,fork()函数后的代码在子进程中也被执行.实际上,其他代码也在子进程的代码段中,只是子进程执行的位置为fork返回位置,其之前的代码无法执行罢了.
例子2:返回值大于0(返回PID)的代码在父进程执行,返回值为0则在子进程执行.
#include <stdio.h>#include <sys/types.h>#include <unistd.h>int main(){ pid_t pid; if((pid=fork())==-1) { printf("fork error\n"); } else if(pid == 0 ) { printf("pid:%d in the child process \n",pid); } else { printf("pid:%d in the parent process\n",pid); } return 0;}
输出:
:pid:2923 in the parent process:pid:0 in the child process
3.子进程对变量的改变不会影响到父进程
例子3:子进程和父进程各有一份变量的副本#include <unistd.h>#include <stdio.h>#include <stdlib.h>int glob = 10;int main(){ int var = 100; pid_t pid = getpid(); printf("before fork:\n"); printf("pid=%d, glob=%d, var=%d\n",pid,glob,var); printf("after fork:\n"); if((pid=fork())<0) { printf("error fork:%m\n"); exit(-1); } else if(pid==0) { glob++; var++; } else { sleep(2); } printf("pid = %d, glob = %d, var = %d\n",getpid(),glob,var); return 0;}
输出:
:before fork::pid=2664, glob=10, var=100
:after fork:
:pid = 2665, glob = 11, var = 101
:pid = 2664, glob = 10, var = 100
可以看出,对于变量glob和var,在子进程中进行了自加,但是在父进程中,变量的值没有改变;显然,父子进程各自拥有这一变量的副本,互不影响.
4.子进程对父进程文件流缓冲区的处理
文件流缓冲区的资源位于用户空间,因此,在创建子进程时,子进程的用户空间将复制父进程的用户空间所有信息,显然,也包含流缓冲区的内容.如果留缓冲区中有临时的信息,则通同样复制到子进程的用户空间流缓冲中.例子4:子进程对父进程文件流缓冲区的处理.
#include <stdio.h>#include <unistd.h>#include <stdlib.h>int main(){ pid_t pid; //有回车,先输出 printf("before fork,have enter\n"); //没有回车,先输出到缓冲区 printf("before fork,no enter:pid=%d\t",getpid()); pid = fork(); if(pid == 0) { printf("\nchild,after fork:pid=%d\n",getpid()); } else { printf("\nparent,after fork: pid=%d\n",getpid()); } return 0;}
输出:
:before fork,have enter:before fork,no enter:pid=2977
:parent,after fork: pid=2977
:before fork,no enter:pid=2977
:child,after fork:pid=2978
为什么“before fork,have enter”只输出一次?
首先明确一点,如果标准输出连到终端设备,则它是行缓冲或者全缓冲.“before fork,have enter”只输出一次,是因为标准输出缓冲区由换行符冲洗,在下面创建子进程时,复制的缓冲区已经没有该数据了.
为什么“before fork,no enter:pid=2977”输出两次?
子进程和父进程都输出了“before fork,no enter:pid=2977”,但是,为什么子进程会输出呢?子进程开始执行的位置是在fork函数返回处,不会执行fork函数之前的代码,虽然该代码被复制到子进程中.之所以出现两次输出,是因为父进程中的“printf("before fork,no enter:pid=%d\t",getpid());”没有回车,就是这条语句输出的结果还在缓冲区,缓冲区没有冲洗,没有真正输出.在创建子进程时,这父进程的缓冲区也会被复制到子进程的进程空间中,所以子进程在输出时,刷新缓冲区时,也会将“printf("before fork,no enter:pid=%d\t",getpid());”的结果输出.
如果程序是这样子运行 ./a.out >out.txt.查看out.txt文件,可得到以下结果.
before fork,have enter
before fork,no enter:pid=2716
parent,after fork: pid=2716
before fork,have enter ====》多了这一行
before fork,no enter:pid=2716
child,after fork:pid=2717
为什么?“before fork,have enter”输出了两次.
标准输出重定向到文件时,在调用fork函数时,该数据还在缓冲区,没有输出,这里的换行符没有像上面那样起到刷新缓冲区的作用,只是简单的换行,所以当父进程将数据复制到子进程时,该缓冲区的数据也被复制到子进程了.
5.子进程对父进程打开的文件描述符的处理
fork函数创建子进程后,子进程将复制父进程的数据段、BBS段.代码段.堆空间、栈空间和文件描述符,而对于文件描述符关联的内核文件表项,则是此采用共享的方式.例子5: 子进程对父进程打开的文件描述符的处理
#include <sys/types.h>#include <unistd.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <fcntl.h>int main(){ pid_t pid; int fd; int i=1; int status; char *ch1="advanced"; char *ch2=" programming"; char *ch3=" int the unix Environment"; fd = open("test.txt",O_RDWR|O_CREAT,0644); if(fd==-1) { printf("open or creat file error:%m\n"); exit(-1); } write(fd,ch1,strlen(ch1)); pid=fork(); if(pid==-1) { printf("error fork\n"); exit(-1); } else if(pid==0) { i=2; printf("in child process\n"); printf("i=%d\n",i); if(write(fd,ch2,strlen(ch2))==-1) { printf("child write error:%m\n"); exit(-1); } } else { sleep(1); printf("int parent process\n"); printf("i=%d\n",i); if(write(fd,ch3,strlen(ch3))==-1) { printf("parent wirte error%m\n"); exit(-1); } wait(&status); } return 0;}
输出:cat test.txt
:advanced programming int the unix Environment可以看出,父子进程共同对一个文件操作,且写入数据不交叉覆盖,说明父子进程共享同一个文件偏移量,共享文件表项.如图1子进程对打开文件的处理方式.
图1 子进程对打开文件的处理方式
笔者:个人能力有限,只是学习参考...读者若发现文中错误,敬请提出.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------勿在浮沙筑高台,静下心来,慢慢地沉淀---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1 0
- 创建进程 fork()函数的基本使用 父子进程之间的关系
- fork 父子进程变量之间的关系
- 父子进程之间的关系
- 关于fork()函数父子进程之间的问题
- fork之后父子进程的内存关系
- fork()父子进程变量之间的关系与信号的响应
- 进程创建/退出父子关系的调整
- Linux中fork()函数详解 父子进程变量的关系
- vfork函数创建出的父子进程
- 20170907_关于 fork函数和父子进程的理解
- 有意思的进程创建函数fork() && fork() || fork() .
- malloc 之后的父子进程之间的空间关系如何
- for的父子进程关系
- 父子进程之间的区别
- wait函数在fork创建子进程后的使用
- wait函数在fork创建子进程后的使用
- wait函数在fork创建子进程后的使用
- wait函数在fork创建子进程后的使用
- Android解决没有AVD的方法
- xcode6+ios8最新真机调试教程
- struct 和typedef struct的区别
- 使用apach cxf+spring+tomcat搭建提供json格式的对外接口入门Demo
- HTML<input>标签的name和id属性区别
- 创建进程 fork()函数的基本使用 父子进程之间的关系
- CSDN第一篇
- SQL中的case when then else end用法
- C++ lambda表达式
- PNG图片尺寸小于2x2,导致小尺寸屏幕机型会缩放为0像素导致crash
- extjs5 更改主题
- Eclipse 中打不开android sdk managerf
- 【分享】迅为4412开发板移植Android后,安装QQ不能视频通信的问题,但照相机可用
- Linux tcpdump命令详解