各位同学,转换下思维,这里说的是“进程”, 不是“线程”,OK,我们开始

“进程”二字似乎总有那么些“只可意会不可言传”的韵味,维基百科是这样来解释的:

进程(英语:Process,台湾译作行程)是计算机中已运行程序的物理。进程本身不会运行,是线程的容器。程序本身只是指令的集合,进程才是程序(那些指令)的真正运行。若干进程有可能与同一个程序相关系,且每个进程皆可以同步(循序)或不同步(平行)的方式独立运行(多线程即每一个线程都代表一个进程)。现代计算机系统可在同一段时间内加载多个程序和进程到存储器中,并借由时间共享(或称多任务),以在一个处理器上表现出同时(平行性)运行的感觉。同样的,使用多线程技术的操作系统或计算机架构,同样程序的平行进程,可在多 CPU 主机或网络上真正同时运行(在不同的 CPU 上)。进程为现今分时系统的基本运作单位。

也有朋友如此来阐述,

一个可以执行的程序;

和该进程相关联的全部数据(包括变量,内存空间,缓冲区等等);

程序的执行上下文(execution context)

我更希望将这些简化一下(或许不太准确):指令和执行指令所需的环境,指令可以理解成“代码”,环境可以理解成“上下文”

系统用一个叫做“进程表”的东西来维护中系统中的进程,进程表中的一个条目维护着存储着一个进程的相关信息,比如进程号,进程状态,寄存器值等等...

当分配给进程A的“时间片”使用完时,CPU会进行上下文切换以便运行其他进程,比如进程B,这里所谓的“上下文切换”,主要就是在操作那个“进程表”,其将进程A的相关信息(上下文)保存到其对应的进程表项中, 与之相反,其会从对应于进程B的进程表项中读取相关信息并运行之。

那么,如果进程A新建了一个进程C呢?教程表会多这样一个表项,并且该表项拥有一个唯一的ID,也就是进程号(PID),进程表项的其他值大部分与进程A的相同,具体说来,就是C和A共享代码段,并且C将A的数据空间,堆栈等复制一份 ,然后从A创建C的地方开始运行。The new process (child process) is an exact copy of the calling process (parent process) except as detailed below.

The child process has a unique process ID.

The child process ID also does not match any active process group ID.

The child process has a different parent process ID (that is, the process ID of the parent process).

The child process has its own copy of the parent's file descriptors. Each of the child's file descriptors refers to the same open file description with the corresponding file descriptor of the parent.

The child process has its own copy of the parent's open directory streams. Each open directory stream in the child process may share directory stream positioning with the corresponding directory stream of the parent.

The child process may have its own copy of the parent's message catalogue descriptors.

The child process' values of tms_utime, tms_stime, tms_cutime and tms_cstime are set to 0.

The time left until an alarm clock signal is reset to 0.

All semadj values are cleared.

File locks set by the parent process are not inherited by the child process.

The set of signals pending for the child process is initialised to the empty set.

Interval timers are reset in the child process.

If the Semaphores option is supported, any semaphores that are open in the parent process will also be open in the child process.

If the Process Memory Locking option is supported, the child process does not inherit any address space memory locks established by the parent process via calls to mlockall() or mlock().

Memory mappings created in the parent are retained in the child process. MAP_PRIVATE mappings inherited from theparent will also be MAP_PRIVATE mappings in the child, and any modifications to the data in these mappings made by the parent prior to calling fork()will be visible to the child. Any modifications to the data in MAP_PRIVATE mappings made by the parent after fork() returns will be visible only to the parent. Modifications to the data in MAP_PRIVATE mappings made by the child will be visible only to the child.

If the Process Scheduling option is supported, for the SCHED_FIFO and SCHED_RR scheduling policies, the child process inherits the policy and priority settings of the parent process during a fork() function. For other scheduling policies, the policy and priority settings on fork()are implementation-dependent.

If the Timers option is supported, per-process timers created by the parent are not inherited by the child process.

If the Message Passing option is supported, the child process has its own copy of the message queue descriptors of the parent. Each of the message descriptors of the child refers to the same open message queue description as thecorresponding message descriptor of the parent.

If the Asynchronous Input and Output option is supported, no asynchronous input or asynchronous output operations are inherited by the child process.

从代码角度来看,创建一个新进程的函数声明如下:

pid_t fork(void);

其包含在 unistd.h 头文件中,其中pid_t是表示“type of process id”的32位整数, 至于函数的返回值,取决于在哪个进程中来检测该值,如果是在新创建的进程中,其为0;如果是在父进程中(创建新进程的进程),其为新创建的进程的id; 如果创建失败,则返回负值。

我们看下面的代码:

#include#includeintmain ()

{

printf("app start...\n");

pid_t id=fork();if(id<0) {

printf("error\n");

}elseif(id==0) {

printf("hi, i'm in new process, my id is %d \n", getpid());

}else{

printf("hi, i'm in old process, the return value is %d\n", id);

}return0;

}

为了方便理解,我在上面使用了getpid函数,其返回当前进程的id。

程序输出为:

app start...hi,i'm in old process,the return value is5429hi,i'm in new process,my id is5429

另外,看到不少资料上说“fork函数是少数返回两个值的函数”,我不赞成该说法,我猜想,其之所以看上去有着不同的值,是系统创建新进程并复制父进程相关资源时,故意根据创建状态放入了不同的值。

fork函数失败的原因主要是没有足够的资源来进行创建或者进程表满,如果是非root权限的账户,则可能被管理员设置了最大进程数。一个用户所能创建的最大进程数限制是很重要的,否则一句代码就可能把主机搞当机:for(;;) fork();

再看下面的代码:

#include#includeintmain ()

{

printf("app start...\n");intcounter=0;

fork();

counter++;

printf("the counter value %d\n", counter);return0;

}

输出如下:

app start...

the counter value1the counter value1

之所以会这样,画个图就明白了:

并且,新进程得到的是父进程的副本,所以,父子进程counter变量不会相互影响。

再来一个demo:

#include#includeintmain ()

{

printf("app start...");

fork();return0;

}

输出为:

app start...app start...

好奇怪是吧?情况是这样的:

当你调用printf时,字符串被写入stdout缓冲区(还没刷到屏幕上的哦),然后fork,子进程复制了父进程的缓冲区,所以子进程的stdout缓冲区中也包含了“app start ...”这个字符串,然后父子进程各自运行,当他们遇到return语句时,缓冲器会被强制刷新,然后就分别将“app start...”刷到了屏幕上。如果想避免,在fork前,调用fflush强制刷新下缓冲区就可以了,在字符串后面加上“\n”也可以,因为stdout是按行缓冲的。

linux fork 目录,linux fork()理解相关推荐

  1. linux inode目录,Linux目录与inode 深入理解

    1. 关于Linux目录,文件,数据块 对于使用计算机的人而言,经常有一种 错误的认知:目录(或者说,文件夹)里面存放着文件.实际上,目录里面并不存放文件,以及文件数据. 实际上,目录是一个特殊的文件 ...

  2. linux rc目录,linux /etc/rc.d/目录及rc.local的详解

    Linux的引导过程 运行级别 /etc/rc.d/ 与/etc/rc.d/init.d的关系 "/etc/rc.d/init.d/目录下的脚本就类似与windows中的注册表,在系统启动的 ...

  3. linux简单目录,Linux下各目录简单整理

    实习之后一直在用Linux,但是对于每个目录并不是很了解,上次阿里的面试问到了,只能靠着自己的使用经验稍作回答,不全面也不准确,所以今天简单整理一下方便以后自己查看- 1./bin 重要的二进制应用程 ...

  4. linux rpm目录,Linux修改RPM的安装目录的方法

    Linux系统下大多数人都是使用rpm -i xxxx.rpm命令来安装RPM包,这种情况下RPM会被安装在默认的目录当中.如果不想把RPM安装在这个目录下应该怎么办呢?下面就来介绍一下Linux如何 ...

  5. linux 自定义目录,linux – cron命令的自定义目录

    我在Debian / Ubuntu上使用vixie cron.如何设置cron要读取的自定义目录?我想引导crontab运行在一系列文件中找到的命令,例如 /home/cron/*.cron 或完美 ...

  6. linux spyder 目录,linux下创建spyder桌面图标的方法

    linux下创建spyder桌面图标的方法 发布时间:2020-07-20 11:08:41 来源:亿速云 阅读:96 作者:清晨 小编给大家分享一下linux下创建spyder桌面图标的方法,希望大 ...

  7. rm linux 复制目录,linux学习(四)复制(cp)移动(mv)删除(rm)查找(find)文件、文件夹操作、软硬链接的区别...

    复制文件 cp命令用于复制文件到制定的目录 语法格式为: cp [选项] 源文件或目录 目标文件或目录 命令选项 含义 cp -d 当复制符号链接时,把目标文件或目录页建立为符号链接,并指向与源文件或 ...

  8. linux 格式化 目录,Linux 磁盘分区、格式化、目录挂载

    实验环境: Citrix虚拟化, RHEL6.4系统环境 本文目的: 熟悉Linux的基本磁盘分区.格式化.目录挂载. 1.显示当前主机目录 2.磁盘分区 3.磁盘格式化 4.挂载目录 1. 显示当前 ...

  9. lrzsz linux 安装目录,Linux下lrzsz软件的安装与使用

    Linux上使用lrzsz上传下载文件是非常方便的,特别是要经过中转才能登录的服务器上就不言而喻了,用过都说好,下面简单介绍一下的安装和使用 1.安装wget http://www.ohse.de/u ...

最新文章

  1. 教你如何运用python实现学生信息管理系统
  2. 一禁了之还是放开应用?面部识别“人红是非多”
  3. 文件不能断点 webstorm_详解python使用金山词霸的翻译功能(调试工具断点的使用)...
  4. 使用asp.net中的跟踪功能
  5. 12.15模拟:总结
  6. python笔记-1(import导入、time/datetime/random/os/sys模块)
  7. 久其报表大厅_天下苦“数”久矣,大数据分析平台解决物流数据孤岛
  8. thrift java first demo
  9. Kubernetes 小白学习笔记(8)--kubernetes的基础概念
  10. 八、Linux文本编辑器vim/vi
  11. python模拟ssh登录
  12. 如何优雅地使用迅雷下载百度云大文件?
  13. 《thor过滤器 thor过滤规则合集资源》500+
  14. 三洋p6系列伺服电机说明书_兰州同步伺服电机维修-川其实业
  15. 我国芯片各细分领域龙头名单
  16. window远程桌面无法复制粘贴文件到本地
  17. 显示器刷新率30hz_如何使您的120Hz或144Hz显示器使用其广告刷新率
  18. oracle关联表查询记录表最新一条记录
  19. Collection集合入门
  20. OpenAcc的使用

热门文章

  1. torch.nn.Embedding理解
  2. GPU—加速数据科学工作流程
  3. [JavaScript] 探索JS中的函数秘密
  4. Android 人民币符号在布局中实现的效果不一样的处理方法
  5. getIntExtra() 获取传递过来的int 值总是默认值
  6. This version of Android Studio cannot open this project, please retry with Android Studio 3.5 or new
  7. CountDownTimer 实现验证码倒计时
  8. Glide 加载图片不显示(Android9.0无法加载图片)
  9. GridView使用的技巧
  10. 上一篇的js处理失真数据存在问题换了种方法