1. 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>
4
5 int main(int argc, const char *argv[])
6 {
7 int pid = 0; /* fork函数的返回值 */
8 int mypid = 0; /* PID */
9 int myppid = 0; /* PPID */
10
11 pid = fork();
12
13 if (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。

  上边的代码在父进程处加一个sleep(1)的目的是让子进程先运行,之后子进程退出,父进程再运行,然后父进程退出,如果父进程退出了,子进程再获取父进程的的ID会发生什么呢,代码作如下改动:

 1 #include <stdio.h>
2 #include <sys/types.h>
3 #include <unistd.h>
4
5 int main(int argc, const char *argv[])
6 {
7 int pid = 0; /* fork函数的返回值 */
8 int mypid = 0; /* PID */
9 int myppid = 0; /* PPID */
10
11 pid = fork();
12
13 if (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 sleep(2);
27 printf("I'm child\n");
28 printf("myppid = %d\n", getppid());
29 }
30 }

  运行结果如下图:

  从运行结果发现,如果父进程退出而子进程还没退出,那么子进程的父进程PID将变为1,子进程此时变为孤儿进程,PID为1的进程就是linux系统下负责收留孤儿进程的进程。

  通过前面的例子可以看出,fork函数虽然将资源一分为二了,但分出来的两部分还是不能当成完全相同的两部分来看待,他们之间存在父子关系。如果父进程退出,那么子进程会变成孤儿进程,如果子进程退出,那么子进程会给父进程发一个信号,这在下一章讨论。因此当使用fork的时候要考虑进程是否会退出,从而选择合适的分支。

2、fork函数与进程ID的更多相关文章

  1. 关于fork( )函数父子进程返回值的问题

    fork()是linux的系统调用函数sys_fork()的提供给用户的接口函数,fork()函数会实现对中断int 0x80的调用过程并把调用结果返回给用户程序. fork()的函数定义是在init ...

  2. fork()函数、进程表示符、进程位置

    linux.centos6.5 fork()函数:作用于创建子进程.返回值有两个,一个是向父进程返回它的pid,一个是返回0: eg1: #include<stdio.h> #includ ...

  3. linux多进/线程编程(2)—— fork函数和进程间“共享”数据

    参考: 1.博客1:https://www.pianshen.com/article/4305691855/ fork:在原进程的基础上"分叉"出一个子进程,即创建一个子进程. N ...

  4. 通过fork函数创建进程的跟踪,分析linux内核进程的创建

    作者:吴乐 山东师范大学 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.实验过程 1.打开gdb, ...

  5. Linux编程之fork函数

    在Linux中,fork函数的功能就是在一个进程中创建一个新的进程,当前调用fork函数的进程就是产生的新进程的父进程,新进程在以下也称为子进程.在新进程生成之后就会在系统中开始执行. 函数原型:pi ...

  6. [转帖]Linux下fork函数及pthread函数的总结

    Linux下fork函数及pthread函数的总结 https://blog.csdn.net/wangdd_199326/article/details/76180514 fork Linux多进程 ...

  7. 深入浅出--UNIX多进程编程之fork()函数

    0前言 上周都在看都在学习unix环境高级编程的第八章--进程控制.也就是这一章中.让我理解了unix中一些进程的原理.以下我就主要依照进程中最重要的三个函数来进行解说.让大家通过阅读这一篇文章彻底明 ...

  8. C/C++网络编程7——多进程服务器端之fork函数

    通过前面几节的内容,我们已经可以实现基本的C/S结构的程序了,但是当多个客户端同时向服务器端请求服务时,服务器端只能按顺序一个一个的服务,这种情况下,客户端的用户是无法忍受的.所以虚实现并发的服务器端 ...

  9. 进程控制之fork函数

    一个现有进程可以调用fork函数创建一个新进程. #include <unistd.h> pid_t fork( void ); 返回值:子进程中返回0,父进程中返回子进程ID,出错返回- ...

随机推荐

  1. Java Int类型与字符,汉字之间的转换

    /** * java 中的流主要是分为字节流和字符流 * 再一个角度分析的话可以分为输入流和输出流 * 输入和输出是一个相对的概念 相对的分别是jvm虚拟机的内存大小 * 从另一个角度讲Java或者用 ...

  2. python-OpenCV 使用GrabCut来实现图片的前景与后景的分割

    先上一个效果图: 使用Python3.7+OpenCV 3.x. 需要引入 numpy库. 在图上用鼠标左键和右键标记前景和后景即可.如果需要重新标记图像,关闭程序重新运行. 以下是具体实现代码. # ...

  3. 原生小程序中实现将scss文件实时编译为wxss文件

    参考链接 全局安装gulp,方便以后直接执行gulp命令 npm install gulp -g 用原生小程序新建一个项目 在小程序根目录(app.js同级目录)中新建package.json文件 n ...

  4. 单细胞分析实录(3): Cell Hashing数据拆分

    在之前的文章里,我主要讲了如下两个内容:(1) 认识Cell Hashing:(2): 使用Cell Ranger得到表达矩阵.相信大家已经知道了cell hashing与普通10X转录组的差异,以及 ...

  5. 基于http的netty demo

    1.引入netty的pom <dependency> <groupId>io.netty</groupId> <artifactId>netty-all ...

  6. ABP框架中短信发送处理,包括阿里云短信和普通短信商的短信发送集成

    在一般的系统中,往往也有短信模块的需求,如动态密码的登录,系统密码的找回,以及为了获取用户手机号码的短信确认等等,在ABP框架中,本身提供了对邮件.短信的基础支持,那么只需要根据自己的情况实现对应的接 ...

  7. SQL Server 数据库还原进度查看

    SQL Server 数据库还原进度查看 关键字:数据库,还原,进度,查看 文档说明: 本文档受某实际需求启发,某约500G大小数据库还原,由于对应服务器性能较差(内存仅4G且可用内存仅2.8G),数 ...

  8. Android-SDK接入-YSDK(应用宝1.7.0)

    SDK接入-YSDK(应用宝1.7.0)-2021-01-07 大家好,近期在多渠道打包平台,会定期遇到第三方SDK升级,所以很被动的是,我们也要跟随他们的步伐,及时升级.否则将面临第三方开发者站无法 ...

  9. git版本回滚

    本地版本回滚 git reset --hard <版本号> (git log 可查看版本号,版本号不用写全) 远程仓库版本回滚 先在本地将版本回滚 ,然后git push -f 强制提交

  10. i5 11300H 怎么样 相当于什么水平

    i5-11300H 为 4 核 8 线程,主频 3.1GHz,睿频 4.4GHz,三级缓存 8MBi5-11300H 怎么样看完你就知道了 https://list.jd.com/list.html?