应评论要求,今天发一篇swoole的文章。如果感觉有帮助,记得点个赞,转发分享评论。

进程隔离

官网上有那么一句话:

进程隔离也是很多新手经常遇到的问题。修改了全局变量的值,为什么不生效,原因就是全局变量在不同的进程,内存空间是隔离的,所以无效。

如果小伙伴们之前对进程没有概念,要理解起来还是有一定难度的。所以在这里,我有必要说一下进程是什么东西。

简单来说,进程是一个数据结构,数据结构里面存放了很多成员变量,这个是最本质的。很多书说:

进程是资源的集合

那么这句话所说的资源指的就是这些变量存放的东西,集合指的就是进程这个数据结构。

如果还是有一些抽象,不要紧,我们去看看Linux中进程这个数据结构的定义是怎样的。数据结构的定义在这里,大概是在593行的位置:

我列举几个这个数据结构里面我们听的比较多的成员变量:

struct task_struct {

操作系统会为每一个进程分配一个task_struct数据结构,一旦CPU执行某个进程的代码的时候,操作系统把当前进程的这些变量提供给CPU。因为每个进程都有自己的这个task_struct数据结构,所以每个进程的变量是在各自的进程里面的,因此不同进程的变量是隔离的,这些变量也包括全局变量、文件句柄(即上图中的fd)。

紧接着,官网又给出那么一句话:

不同进程的文件句柄是隔离的,所以在A进程创建的Socket连接或打开的文件,在B进程内是无效,即使是将它的fd发送到B进程也是不可用的

这句话什么意思呢?为了搞清楚,我们要知道文件句柄的作用。

当用户调用open系统调用(或者其他打开文件的系统调用)的时候,内核会创建一个打开文件对象来表示该文件的一个打开实例。内核同时也会分配一个文件描述符(也就是上图中的fd)作为打开文件对象的句柄。如下图所示:

drools从入门到精通(看了这篇文章你就懂了)(1)

open系统调用向进程返回这个文件描述符,然后这个文件描述符就被存放在file array里面了。然后进程就可以通过这个fd1来找到对应的文件。那么,图中的offset是什么意思呢(我们把offset叫做打开文件对象)?在Unix系统中,文件默认是顺序访问的。当用户打开文件的时候,内核初始化这个offset的偏移指针为0。举个例子,如果当前进程刚打开一个文件(文件内容是字符串abcdefghijklnm),那么此时offset的状态如下:

drools从入门到精通(看了这篇文章你就懂了)(2)

因为offset的偏移指针为0,所以指向字母a。所以,当我们通过fd去读取文件内容的时候,读取到的第一个字符就是字母a。假设我连续读了3个字节,那么此时的状态应该是这个样子的:

drools从入门到精通(看了这篇文章你就懂了)(3)

也就是说,当进程从文件里面读取3个字节之后,offset此时指向字母d。当进程下一次读取文件的时候,读取出来的第一个字母就是d了。这就是offset的概念。

同一个进程还可以多次打开同一个文件,如下图所示:

drools从入门到精通(看了这篇文章你就懂了)(4)

可以看出,虽然fd1和fd2都是指向同一个文件,但是,如果进程通过fd1去读取文件的话,读到的第一个字母是d;如果进程通过fd2去读取文件的话,读取到的第一个字母是a。所以,每个文件描述符代表着与文件的一个独立会话,对应的打开文件对象(即图中的offset)保存者该会话的内容,这包括了被打开文件的模式(只读、只写、读写)以及下一次读取或者写入时的偏移指针。

我们通过代码来直观感受一下:

$handle1 = fopen("file.txt", "r");

$handle2 = fopen("file.txt", "r");

$content = fread ($handle1 , 3);

echo "The process reads three bytes through handle1, the content is: " . $content . PHP_EOL;

$content1 = fread ($handle1 , 1);

$content2 = fread ($handle2 , 1);

echo "The process reads one bytes through handle1, the content is: " . $content1 . PHP_EOL;

echo "The process reads one bytes through handle2, the content is: " . $content2 . PHP_EOL;

结果如下:

drools从入门到精通(看了这篇文章你就懂了)(5)

验证了我们的说法。

正是因为offset这个打开文件对象对同一个文件的不同会话进行了隔离,所以,才有了官网的这句话:

不同进程的文件句柄是隔离的,所以在A进程创建的Socket连接或打开的文件,在B进程内是无效,即使是将它的fd发送到B进程也是不可用的

也就是说,就算fd是一样的,但是offset是不一样的。

其实到这里算是讲完了隔离,但是,我还想再讲一点其他的东西。

即,同一个进程是否可以让多个fd指向同一个offset,从而达到多个fd共享同一个offset的效果呢?答案是可以做到。

进程可以通过dup系统调用来复制一个文件描述符fd,这样一来,两个文件描述符共享着相同的会话:

drools从入门到精通(看了这篇文章你就懂了)(6)

代码如下:

$handle1 = fopen("file.txt", "r");

$handle2 = dup($handle1);

$content = fread ($handle1 , 3);

echo "The process reads three bytes through handle1, the content is: " . $content . PHP_EOL;

$content1 = fread ($handle1 , 1);

$content2 = fread ($handle2 , 1);

echo "The process reads one bytes through handle1, the content is: " . $content1 . PHP_EOL;

echo "The process reads one bytes through handle2, the content is: " . $content2 . PHP_EOL;

(注意,这段代码运行不了,因为PHP没有提供dup这个函数)

假设,这个函数是可以运行,那么输出的结果应该是

The process reads three bytes through handle1, the content is: abc

The process reads one bytes through handle1, the content is: d

The process reads one bytes through handle2, the content is: e

drools从入门到精通(看了这篇文章你就懂了)(7)

,