多路复用I/O模型的理解

什么是I/O:

不管是文件还是管道、套接字都可以被看作标准的文件描述符或者流,我们针对文件描述符的读和写操作(输入输出)就是I/O (input/ouput)。

多路复用:

指多个文件描述符的I/O的操作可以在同一个线程内顺序交替完成,复用的是同一个线程。

I/O的阻塞:

说起阻塞和非阻塞,就要提一下同步和异步,同步和异步是在一个进程或者线程的角度(或者更宏观的调用周期)看待请求和结果的时间问题,一次请求直到同一线程将需要的数据返回就是同步,否则就是请求后立马返回部分结果,最终结果被其它线程处理,通知的方式告知想要的结果。异步的进程间通信的方式有很多种,比如管道,队列,套接字,信号,共享内存等。

阻塞, 指线程在处理一个流时,需要等待一个新的I/O事件的发生而处于一种挂起状态,该状态下的线程无法继续工作,直到内核对该流产生一个新的I/O事件。阻塞模型下要想处理更多的任务,就需要创建新的进程或者线程,不过这种方式非常的低效。

非阻塞,在一个线程中,可以打开多个文件描述符,而且一旦一个流需要等待I/O事件时,不会导致整个线程的挂起,这些流被放到一个队列中,会被不断的轮询,每次轮询找出有新的I/O事件产生的流。

非阻塞轮询:

上面讲到的非阻塞模型中,内核会不断的轮询所有的流,即使任何流都没有I/O事件发生也会轮询一遍,这个样子就会非常浪费资源。为了更高效的处理非阻塞的情况,引入了更好的处理方式select和epoll

select:

该模型的产生让非阻塞有了一个比较理性的轮询方式,线程中所有的流如果没有I/O事件都会被select接管,有任何的流产生I/O事件的时候,select都会知道再去轮询,不用再去空轮询了。不过select这个时候并不知道具体哪个流上产生事件了,所以需要针对所有的流都轮询一次。时间复杂度O(n),也就是说随着要处理的流的增加,处理时间变长,效率降低。select一个线程中能打开的文件描述符的个数通常限制为1024(系统参数,可配置)

epoll:

针对select无差别轮询的问题,epoll有了更好的处理方式,epoll在所有的流上都注册有回调函数,一旦有I/O事件的产生就会触发回调,接下来就只需要处理这些已知发生I/O事件的流就可以了。而且epoll没有文件描述符打开的数量限制。

 

参考:

https://www.cnblogs.com/ajianbeyourself/p/5859989.html

 

 

合并两个有序数组

问题

合并两个有序的整数数组A和B变成一个新的有序数组

样例
给出A=[1,2,3,4],B=[2,4,5,6],返回 [1,2,2,3,4,4,5,6]

解析

1、从第一个元素拿出两个数组的元素比较,小的放到结果集中并从原数组中剔除,大的不做处理

2、如果其中一个数组元素取尽, 那么另一个数组剩下的值全部追加上结果集中 

 

PHP的引用计数

思考一下下面这段代码的执行过程和结果

$a和$b应该有相同的值

首先,PHP的变量和值在底层实现上是分开存储的, 变量的名称保存在符号表中,变量的值则是通过一个zval的结构来保存,然后他们通过指针关联起来。PHP中在创建变量$a时,为“dog”这个值分配了内存空间,$a则存在符号表中,通过指针指向“dog”内存位置;$a的值又赋值给$b,  所以$a和$b拥有了相同的值,为了节约内存, 这个时候PHP内部是做了特殊处理的,这种赋值其实只是在符号表中加入了$b, 然后也指向了“dog”这个内存位置。减少了因复制导致的内存浪费。

$a的销毁不应该影响$b的值

这个应该是一个合理的思考,$a的销毁不应该影响$b的值,那么销毁$a都做了哪些操作呢?根据上面的逻辑,$a的销毁其实是把变量名称从符号表中删除,并且去掉了其到“dog”这个内存地址的指针。

“dog”这个值的内存空间如何释放

这个时候如果我们销毁掉$b, 那么按照上面的逻辑符号表中去掉$b,  那么值的内存空间什么时候释放呢?

引用计数

通过以上的思考我们终于可以正大光明的引入这个概念了,说到引用计数,需要首先了解一下zval结构:

其中refcount__gc就是用来实现变量的引用计数的,每引用一次+1, 减少一次引用就-1,结合上面的例子,创建$a时refcount__gc 初始化为 1, $b的创建使的引用计数增加到2,接着$a的销毁计数减小到1, 如果再有$b的销毁,那么引用计数就变为了0, 这个时候就触发了垃圾回收操作,将值的内存空间回收。

变量的修改操作会不会相互影响

根据以上的原理,$a和$b都指向了同一个内存地址,那么如果修改其中任何一个值,另一个会不会跟着发生变化呢?我们当然是期望不会相互影响。

写时复制

在修改$a的值的时候,PHP底层做了一个特别的处理,它会判断一下refcount__gc的值,如果是大于1,会复制一份zval并分配一个新的内存地址给它,当然$a指向的地址要发生变化了,那么原来的zval的引用计数就需要-1。

直接引用赋值

在PHP中我们经常做一些引用赋值的操作

这种情况和上面我们讨论的区别是什么呢?

引用标记

为了区分这种引用赋值,在zval的结构中引入了is_ref__gc, 如果是引用赋值那么该值会设置为1, 否则默认为0。在修改$a的时候,如果是是引用赋值,那么就不需要复制zval了, 直接改变原来的zval对应的值,那么也就是说这个时候$b的值也会保持和$a同步变为’cat’。

变量复制和引用赋值同时存在的情况

按照顺序,首先$a和$b共同指向一个zval, 后面$c的引用赋值使得$a不得不和$b分开,否则就乱了, 这个时候zval强制复制出新的zval和$a,$c 关联上,而$b还是用原来的zval。

n的阶乘(n!)结果中尾部0的个数有多少?

问题描述

设计一个算法,计算出n阶乘中尾部零的个数

样例
11! = 39916800,因此应该返回 2

解析

统计0的个数,就要考虑一下怎么才能产生0,5乘以任何的偶数都会产生0,也就转换成统计5的个数

 

求第k大元素

问题描述

在数组中找到第k大的元素

样例

给出数组 [9,3,2,4,8],第三大的元素是 4

给出数组 [1,2,3,4,5],第一大的元素是 5,第二大的元素是 4,第三大的元素是 3,以此类推

解析

利用分治原理,我们来求解 

1 、取数组中的任意位置的元素,比如第一个元素作为参考值

2、遍历数组中的元素,比参考值大的放到一组, 小的放到一组,这里我们把等于参考值的也放到大值组。

3、k的值如果比大值组的长度还大就在小值组, 小就在大值组, 如果等于大值组的长度那么参考值就是我们要找的k大元素

4、3步骤得到的数组重复以上步骤(递归操作),直到3步骤得到我们需要的值

 

PHP 命令行交互模式的开启 [ php -a ]

很多时候我们想简单的做个代码调试, 可以写一个.php的文件,然后

php  test.php

这个样子,但其实有个更简单的方式就是开启命令行交互模式,如下:

不过有的时候我们输入命令后会卡在如下位置:

原因很简单,我们需要安装PHP的扩展readline, 如果一开始想到这点的话,在安装php的时候就可以使用 –with-readline,第二种方法就是通过后期的扩展安装方式

我们按照这个安装readline扩展,不过你也许并不顺利,

解决办法,官网下载最新的libedit源码包,附上地址

http://thrysoee.dk/editline/libedit-20170329-3.1.tar.gz

这里我也附上安装命令,

安装完libedit ,就可以继续安装readline扩展了,最后记得在php.ini里引入扩展

重启php, 享受php -a  带来的快感吧 !

 

 

KVM配置vnc连接虚拟机

如果虚拟机无法直接连接, 有以下2中方式可以尝试

1 console

但是会遇到console后卡在登录的地方, 搜了很多资料说是需要配置虚拟机的一些东西, 但是虚拟机压根登录不进去, 于是可以尝试第二种方式

2 vnc

vnc 是一款远程控制工具软件, 通过IP+端口的方式可以直接连接到目标机器

接下来需要对kvm的每台虚拟机配置vnc, 使得每台虚拟机都绑定到宿主机器的一个端口上, 这样就可以通过宿主的IP+端口访问到虚拟机, 修改配置可以通过:

也可以直接去修改xml文件

<graphics type=’vnc’ port=’5100′ autoport=’no’ listen=’0.0.0.0′>

<listen type=’address’ address=’0.0.0.0’/>

</graphics>

修改后保存, 然后重新定义一下:

注意:  端口的指定需要是系统空闲且有效的, 如果设置冲突或者无效, 将导致无法正常启动虚拟机

    error: Failed to start domain test

error: Unable to read from monitor: Connection reset by peer

vnc viewer, 是连接需要用到的工具, 可以在官网下载

https://www.realvnc.com/download/viewer/

在使用过程中如果出现闪退的情况, 需要按照一下步骤更改一下配置:

Option–>Advanced–>Expert找到ColourLevel,默认值是pal8,修改为rgb222或full

此方案参考:

VNC Viewer 连接成功后闪退的解决方法

KVM修改虚拟机CPU和内存

KVM虚拟机的CPU和内存是可以动态分配的, 这个可分配的范围需要在停机状态时提前配置好.

配置最大的CPU和内存

启动虚拟机并动态配置

 

Python时间类型转换

日期字符串,时间戳,时间对象等相互转换

字符串转时间戳

字符串转datetime

时间戳转字符串

datetime转字符串

datetime转时间戳

date转datetime

KVM重命名虚拟机

1 、关闭虚拟机

# 查看所有的虚拟机列表

Id    名称                         状态
—————————————————-
8     dev1                        running
–     dev2                           关闭
–     dev3                           关闭
–     dev4                           关闭
–     dev5                           关闭

# 如果正常关闭不掉, 可以尝试destroy

2 、导出xml配置到目标虚拟机文件

# 进入xml配置文件目录

#  导出xml

3 、修改xml

# 修改<name> 节点的值为 dev1_new:

# <name>dev1_new</name>

4 、删除虚拟机定义并重新定义新的xml

5、 启动

Id    名称                         状态
—————————————————-
8     dev1_new                    关闭
–     dev2                           关闭
–     dev3                           关闭
–     dev4                           关闭
–     dev5                           关闭