一面(40min)

0. 自我介绍

1. 介绍 ImageOCR 的项目

2. 知道有哪些排序算法?

冒泡、插入、shell、归并、快排、堆排序、选择排序等等

有没有不会写的,排雷排掉了堆排序

3. 手写插入排序并解释

4. 上面的排序算法哪些是稳定的,哪些是不稳定的

还有一些忘了

二面(1.2h)

0. 介绍简历上的另外一个项目

1. PHP 的基本数据类型以及基本数据类型内核如何实现?

基本数据类型

boolean(布尔型)	布尔型是最简单的数据类型,只有两个值 false(假) 和true(真)
string(字符串型)	字符串就是连续的字符序列,如 ehco "string";
integer(整型)	    整型数据类型只能包含整数,这些数据类型可以是负数或者正数
folat(浮点型)	浮点型类型用于存储数字,和整型不同的是浮点型可以有小数点
array(数组)	一组相同类型的集合
object(对象)	对象是一个实力,使用new命令创建一个对象
resource(资源)	资源是一种特殊的变量,保存在外部资源的一个应用,资源是通过函数来进行建立的
null( 空白)	特殊的值,表示变量没有值,任何变量的初始值都是null

变量的在内核当中的保存方式:

typedef union _zvalue_value {
    long lval;                  /* long value */
    double dval;                /* double value */
    struct {
        char *val;
        int len;
    } str;
    HashTable *ht;              /* hash table value */
    zend_object_value obj;
    zend_ast *ast;
} zvalue_value;

struct _zval_struct {
    /* Variable information */
    zvalue_value value;     /* value */
    zend_uint refcount__gc;
    zend_uchar type;    /* active type */
    zend_uchar is_ref__gc;
};
PHP 语言层类型存在 zvalue_value 的成员变量
long,bool,resourelval
doubledval
stringstr(len 保存字符串的长度,val 保存字符串的值)
arrayht(哈希表)
objectobj
//存储变量的结构
struct _zval_struct {
    zend_value        value;            /* value */
    union {
        struct {
            ZEND_ENDIAN_LOHI_4(
                zend_uchar    type,         /* active type */
                zend_uchar    type_flags,
                zend_uchar    const_flags,
                zend_uchar    reserved)     /* call info for EX(This) */
        } v;
        uint32_t type_info;
    } u1;
    union {
        uint32_t     var_flags;
        uint32_t     next;                 /* hash collision chain */
        uint32_t     cache_slot;           /* literal cache slot */
        uint32_t     lineno;               /* line number (for ast nodes) */
        uint32_t     num_args;             /* arguments number for EX(This) */
        uint32_t     fe_pos;               /* foreach position */
        uint32_t     fe_iter_idx;          /* foreach iterator index */
    } u2;
};

//变量的值
//通过上面的代码我们可以看到。变量是通过一个_zval_struct结构体方式存储的。其中结构体中的value存储的是变量的值。这个成员是zend_value类型的。zend_value类型的定义如下:

typedef union _zend_value {
    zend_long         lval;             /* long value */
    double            dval;             /* double value */
    zend_refcounted  *counted;
    zend_string      *str;
    zend_array       *arr;
    zend_object      *obj;
    zend_resource    *res;
    zend_reference   *ref;
    zend_ast_ref     *ast;
    zval             *zv;
    void             *ptr;
    zend_class_entry *ce;
    zend_function    *func;
    struct {
        uint32_t w1;
        uint32_t w2;
    } ww;
} zend_value;

2. PHP 如何与 nginx 通信,通信方式有什么异同

CGI 模式

CGI CGI(common gateway interface)通常翻译为共同网关接口,是 HTTP 服务器与机器上的其他程序进行通信的一个接口,让 Web 服务器必要时启动额外的程序处理动态内容。CGI 是一种协议,它定义了 Webserver 与 CGI 程序的通信方式。Webserver 接受客户端的 HTTP 请求,然后建立进程执行 CGI 程序,客户端的请求被传递给 CGI 程序,CGI 执行后结果再返回 Webserver。 CGI 的出现让 WEB 从静态变为为动态,随着 Web 的越来越普及,很多的网站的都需要有动态的页面,以便与浏览者互交。随着网络技术的发展,CGI 方式的缺点也越来越突出。每次客户端请求都需要建立和销毁进程。因为 HTTP 要生成一个动态页面,系统就必须启动一个新的进程以运行 CGI 程序,不断地 fork 是一项很消耗时间和资源的工作。

FastCGI

众所周知,CGI 解释器的反复加载是 CGI 性能低下的主要原因,如果 CGI 解释器保持在内存中 并接受 FastCGI 进程管理器调度,则可以提供良好的性能、伸缩性、Fail-Over 特性等等。

FastCGI 是一个常驻型的 CGI,可以一直执行,只要激活后,不会每次都花时间去 fork 一次,而且还支持分布式运算(使得 php 程序解释执行可以单独交给 php 服务器),即可以在网站服务器以外的主机上执行并且接受来自其它网站服务器来的请求。

1、Web Server 启动时载入 FastCGI 进程管理器(IIS ISAPI 或 Apache Module);
2、FastCGI 进程管理器自身初始化,启动多个 CGI 解释器进程 (在任务管理器中可见多个 php-cgi.exe)并等待来自 Web Server 的连接。
3、当客户端请求到达 Web Server 时,FastCGI 进程管理器选择并连接到一个 CGI 解释器。Web server 将 CGI 环境变量和标准输入发送到 FastCGI 子进程 php-cgi.exe。
4、FastCGI 子进程完成处理后将标准输出和错误信息从同一连接返回 Web Server。当 FastCGI 子进程关闭连接时,请求便告处理完成。FastCGI 子进程接着等待并处理来自 FastCGI 进程管理器(运行在 WebServer 中)的下一个连接。 在正常的 CGI 模式中,php-cgi.exe 在此便退出了

Apache 模块

MPM Multi Path Modules (多道处理模块)用于定义 apache 在响应多个用户请求时所工作的模型。有三种 MPM 模式:

prefork(一个请求一个进程响应)

worker(一个请求用一个线程响应,启动多个进程每个进程生成多个线程)

event(一个进程处理多个请求)

以模块安装的 php 没有独立的进程,是作为 apache 的模块和 apache 一起启动的。

以上三种 MPM 模式,worker 模式会比 prefork 模式占据更少的内存,高并发下的表现更好。而且使用多进程和多线程混合模式,即使有一个线程挂了,也只影响和该线程同进程的其他线程,不会影响到其他的进程。但是如果有特别多的线程都使用 keep-alive 的长连接方式,则线程会一直被占据直到超时才释放,导致在高并发场景下无可用线程。而 event 模式使用了一个专门的线程来处理这些 keep-alive 类线程,较好的解决了这个问题。

Nginx

Nginx 处理 PHP 文件 只有 FastCGI 一种
不过 Nginx 连接 fastcgi 的方式有 2 种:TCP 和 unix domain socket
Unix domain socket 或者 IPC socket 是一种终端,可以使同一台操作系统上的两个或多个进程进行数据通信。与管道相比,Unix domain sockets 既可以使用字节流和数据队列,而管道通信则只能通过字节流。Unix domain sockets 的接口和 Internet socket 很像,但它不使用网络底层协议来通信。Unix domain socket 的功能是 POSIX 操作系统里的一种组件。
Unix domain sockets 使用系统文件的地址来作为自己的身份。它可以被系统进程引用。所以两个进程可以同时打开一个 Unix domain sockets 来进行通信。不过这种通信方式是发生在系统内核里而不会在网络里传播。
TCP 和 unix domain socket 方式对比
TCP 是使用 TCP 端口连接 127.0.0.1:9000
Socket 是使用 unix domain socket 连接套接字/dev/shm/php-cgi.sock(很多教程使用路径/tmp,而路径/dev/shm 是个 tmpfs,速度比磁盘快得多)
测试机是个 1 核的 centos5.4,2 用户并发时系统资源消耗 50%左右,10 用户资源就跑得很满了

结论是在服务器压力不大的情况下,tcp 和 socket 差别不大,但在压力比较满的时候,用套接字方式,效果确实比较好。
下面是 php 5.3 以上版本将 TCP 改成 socket 方式的配置方法:
修改 php-fpm.conf(/usr/local/php/etc/php-fpm.conf)
;listen = 127.0.0.1:9000
listen = /dev/shm/php-cgi.sock
修改 nginx 配置文件 server 段的配置,将 http 的方式改为 socket 方式

从原理上来说,unix socket 方式肯定要比 tcp 的方式快而且消耗资源少,因为 socket 之间在 nginx 和 php-fpm 的进程之间通信,而 tcp 需要经过本地回环驱动,还要申请临时端口和 tcp 相关资源。
当然还是从原理上来说,unix socket 会显得不是那么稳定,当并发连接数爆发时,会产生大量的长时缓存,在没有面向连接协议支撑的情况下,大数据包很有可能就直接出错并不会返回异常。而 TCP 这样的面向连接的协议,多少可以保证通信的正确性和完整性。

3. PHP 的执行模式有哪些

运行模式

关于 PHP 目前比较常见的五大运行模式:
1)CGI(通用网关接口/ Common Gateway Interface)
2)FastCGI(常驻型 CGI / Long-Live CGI)
3)CLI(命令行运行 / Command Line Interface)
4)Web 模块模式(Apache 等 Web 服务器运行的模式)
5)ISAPI(Internet Server Application Program Interface)

备注:在 PHP5.3 以后,PHP 不再有 ISAPI 模式,安装后也不再有 php5isapi.dll 这个文件。要在 IIS6 上使用高版本 PHP,必须安装 FastCGI 扩展,然后使 IIS6 支持 FastCGI。

1.1、CGI 模式

CGI 即通用网关接口(Common Gateway Interface),它是一段程序,通俗的讲 CGI 就象是一座桥,把网页和 Web 服务器中的执行程序连接起来,它把 HTML 接收的指令传递给服务器的执行程序,再把服务器执行程序的结果返还给 HTML 页。CGI 的跨平台性能极佳,几乎可以在任何操作系统上实现。CGI 已经是比较老的模式了,这几年都很少用了。

每有一个用户请求,都会先要创建 CGI 的子进程,然后处理请求,处理完后结束这个子进程,这就是 Fork-And-Execute 模式。 当用户请求数量非常多时,会大量挤占系统的资源如内存,CPU 时间等,造成效能低下。所以用 CGI 方式的服务器有多少连接请求就会有多少 CGI 子进程,子进程反复加载是 CGI 性能低下的主要原因。

如果不想把 PHP 嵌入到服务器端软件(如 Apache)作为一个模块安装的话,可以选择以 CGI 的模式安装。或者把 PHP 用于不同的 CGI 封装以便为代码创建安全的 chroot 和 setuid 环境。这样每个客户机请求一个 PHP 文件,Web 服务器就调用 php.exe(win 下是 php.exe,linux 是 php)去解释这个文件,然后再把解释的结果以网页的形式返回给客户机。 这种安装方式通常会把 PHP 的可执行文件安装到 web 服务器的 cgi-bin 目录。CERT 建议书 CA-96.11 建议不要把任何的解释器放到 cgi-bin 目录。 这种方式的好处是把 Web Server 和具体的程序处理独立开来,结构清晰,可控性强,同时缺点就是如果在高访问需求的情况下,CGI 的进程 Fork 就会成为很大的服务器负担,想 象一下数百个并发请求导致服务器 Fork 出数百个进程就明白了。这也是为什么 CGI 一直背负性能低下,高资源消耗的恶名的原因。

1.2、FastCGI 模式

FastCGI 是 CGI 的升级版本,FastCGI 像是一个常驻 (long-live)型的 CGI,它可以一直执行着,只要激活后,不会每次都要花费时间去 Fork 一次 (这是 CGI 最为人诟病的 fork-and-execute 模式)。
FastCGI 是一个可伸缩地、高速地在 HTTP server 和动态脚本语言间通信的接口。多数流行的 HTTP server 都支持 FastCGI,包括 Apache、Nginx 和 lighttpd 等,同时,FastCGI 也被许多脚本语言所支持,其中就有 PHP。
FastCGI 接口方式采用 C/S 结构,可以将 HTTP 服务器和脚本解析服务器分开,同时在脚本解析服务器上启动一个或者多个脚本解析守护进程。当 HTTP 服务器每次遇到动态程序时,可以将其直接交付给 FastCGI 进程来执行,然后将得到的结果返回给浏览器。这种方式可以让 HTTP 服务器专一地处理静态请求或者将动态脚本服务器的结果返回给客户端,这在很大程度上提高了整个应用系统的性能。

【原理】
1)Web Server 启动时载入 FastCGI 进程管理器(IIS ISAPI 或 Apache Module);
2)FastCGI 进程管理器自身初始化,启动多个 CGI 解释器进程 (可见多个 php-cgi.exe 或 php-cig)并等待来自 Web Server 的连接;
3)当客户端请求到达 Web Server 时,FastCGI 进程管理器选择并连接到一个 CGI 解释器。Web server 将 CGI 环境变量和标准输入发送到 FastCGI 子进程 php-cgi;
4)FastCGI 子进程完成处理后将标准输出和错误信息从同一连接返回 Web Server。当 FastCGI 子进程关闭连接时,请求便告处理完成。FastCGI 子进程接着等待并处理来自 FastCGI 进程管理器(运行在 WebServer 中)的下一个连接。在正常的 CGI 模式中,php-cgi.exe 在此便退出了。
在 CGI 模式中,你可以想象 CGI 通常有多慢。每一个 Web 请求 PHP 都必须重新解析 php.ini、重新载入全部 dll 扩展并重初始化全部数据结构。使用 FastCGI,所有这些都只在进程启动时发生一次。一个额外的好处是,持续数据库连接(Persistent database connection)可以工作。

备注:PHP 的 FastCGI 进程管理器是 PHP-FPM(PHP-FastCGI Process Manager)
【优点】
1)从稳定性上看,FastCGI 是以独立的进程池来运行 CGI,单独一个进程死掉,系统可以很轻易的丢弃,然后重新分配新的进程来运行逻辑;
2)从安全性上看,FastCGI 支持分布式运算。FastCGI 和宿主的 Server 完全独立,FastCGI 怎么 down 也不会把 Server 搞垮;
3)从性能上看,FastCGI 把动态逻辑的处理从 Server 中分离出来,大负荷的 IO 处理还是留给宿主 Server,这样宿主 Server 可以一心一意作 IO,对于一个普通的动态网页来说, 逻辑处理可能只有一小部分,大量的是图片等静态。

【缺点】
说完了好处,也来说说缺点。从我的实际使用来看,用 FastCGI 模式更适合生产环境的服务器。但对于开发用机器来说就不太合适。因为当使用 Zend Studio 调试程序时,由于 FastCGI 会认为 PHP 进程超时,从而在页面返回 500 错误。这一点让人非常恼火,所以我在开发机器上还是换回了 ISAPI 模式。对某些服务器的新版本支持不好,对分布式负载均衡没要求的模块化安装是否是更好的选择。目前的 FastCGI 和 Server 沟通还不够智能,一个 FastCGI 进程如果执行时间过长会被当成是死进程杀掉重起,这样在处理长时间任务的时候很麻烦,这样做也使得 FastCGI 无法允许联机调试。因为是多进程,所以比 CGI 多线程消耗更多的服务器内存,PHP-CGI 解释器每进程消耗 7 至 25 兆内存,将这个数字乘以 50 或 100 就是很大的内存数。

1.3 CLI 模式

PHP-CLI 是 PHP Command Line Interface 的简称,如同它名字的意思,就是 PHP 在命令行运行的接口,区别于在 Web 服务器上运行的 PHP 环境(PHP-CGI,ISAPI 等)。 也就是说,PHP 不单可以写前台网页,它还可以用来写后台的程序。 PHP 的 CLI Shell 脚本适用于所有的 PHP 优势,使创建要么支持脚本或系统甚至与 GUI 应用程序的服务端,在 Windows 和 Linux 下都是支持 PHP-CLI 模式的。
【优点】
1)使用多进程,子进程结束以后,内核会负责回收资源;
2)使用多进程,子进程异常退出不会导致整个进程 Thread 退出,父进程还有机会重建流程;
3)一个常驻主进程,只负责任务分发,逻辑更清楚。
我们在 Linux 下经常使用”php –m”查找 PHP 安装了那些扩展就是 PHP 命令行运行模式;有兴趣的同学可以输入”php –h”去深入研究该运行模式。

1.4 模块模式

模块模式是以 mod_php5 模块的形式集成,此时 mod_php5 模块的作用是接收 Apache 传递过来的 PHP 文件请求,并处理这些请求,然后将处理后的结果返回给 Apache。如果我们在 Apache 启动前在其配置文件中配置好了 PHP 模块
(mod_php5), PHP 模块通过注册 apache2 的 ap_hook_post_config 挂钩,在 Apache 启动的时候启动此模块以接受 PHP 文件的请求。
除了这种启动时的加载方式,Apache 的模块可以在运行的时候动态装载,这意味着对服务器可以进行功能扩展而不需要重新对源代码进行编译,甚至根本不需要停止服务器。我们所需要做的仅仅是给服务器发送信号 HUP 或者 AP_SIG_GRACEFUL 通知服务器重新载入模块。但是在动态加载之前,我们需要将模块编译成为动态链接库。此时的动态加载就是加载动态链接库。 Apache 中对动态链接库的处理是通过模块 mod_so 来完成的,因此 mod_so 模块不能被动态加载,它只能被静态编译进 Apache 的核心。这意味着它是随着 Apache 一起启动的。
Apache 是如何加载模块的呢?我们以前面提到的 mod_php5 模块为例。首先我们需要在 Apache 的配置文件 httpd.conf 中添加一行:
LoadModule php5_module modules/mod_php5.so

这里我们使用了 LoadModule 命令,该命令的第一个参数是模块的名称,名称可以在模块实现的源码中找到。第二个选项是该模块所处的路径。如果需要在服务器运行时加载模块,可以通过发送信号 HUP 或者 AP_SIG_GRACEFUL 给服务器,一旦接受到该信号,Apache 将重新装载模块,而不需要重新启动服务器。
该运行模式是我们以前在 windows 环境下使用 apache 服务器经常使用的,而在模块化(DLL)中,PHP 是与 Web 服务器一起启动并运行的。(它是 apache 在 CGI 的基础上进行的一种扩展,加快 PHP 的运行效率)。

1.5 ISAPI 模式

ISAPI(Internet Server Application Program Interface)是微软提供的一套面向 Internet 服务的 API 接口,一个 ISAPI 的 DLL,可以在被用户请求激活后长驻内存,等待用户的另一个请求,还可以在一个 DLL 里设置多个用户请求处理函数,此外,ISAPI 的 DLL 应用程序和 WWW 服务器处于同一个进程中,效率要显著高于 CGI。(由于微软的排他性,只能运行于 windows 环境)
PHP 作为 Apache 模块,Apache 服务器在系统启动后,预先生成多个进程副本驻留在内存中,一旦有请求出现,就立即使用这些空余的子进程进行处理,这样就不存在生成子进程造成的延迟了。这些服务器副本在处理完一次 HTTP 请求之后并不立即退出,而是停留在计算机中等待下次请求。对于客户浏览器的请求反应更快,性能较高。

4. PHP 代码的执行流程

1.Scanning(Lexing) ,将PHP代码转换为语言片段(Tokens)
2.Parsing, 将Tokens转换成简单而有意义的表达式
3.Compilation, 将表达式编译成Opocdes
4.Execution, 顺次执行Opcodes,每次一条,从而实现PHP脚本的功能。

5. PHP 与 Golang 的区别

参考博文

面试的时候基本上上面说到的都说到了一些但是面试的时候逻辑还是不够清晰

6. 深度优先遍历与广度优先遍历

a. DFS

(1)访问顶点 v;
(2)从 v 的未被访问的邻接点中选取一个顶点 w,从 w 出发进行深度优先遍历;
(3)重复上述两步,直至图中所有和 v 有路径相通的顶点都被访问到。

b. BFS

(1)顶点 v 入队列。
(2)当队列非空时则继续执行,否则算法结束。
(3)出队列取得队头顶点 v;访问顶点 v 并标记顶点 v 已被访问。
(4)查找顶点 v 的第一个邻接顶点 col。
(5)若 v 的邻接顶点 col 未被访问过的,则 col 入队列。
(6)继续查找顶点 v 的另一个新的邻接顶点 col,转到步骤(5)。
直到顶点 v 的所有未被访问过的邻接点处理完。转到步骤(2)。
广度优先遍历图是以顶点 v 为起始点,由近至远,依次访问和 v 有路径相通而且路径长度为 1,2,……的顶点。为了使“先被访问顶点的邻接点”先于“后被访问顶点的邻接点”被访问,需设置队列存储访问的顶点。

7. 浏览器输入一个 URL 之后发生了什么事情

8. 遍历一个目录下面所有的文件以及文件夹

面试的时候写了一个 DFS,一下被问到 BFS 怎么写的时候短路了,没有想起了,其实实现的方法不难,参考问题 6

其实还有其他的问题,不过忘了

三面[20min]

三面没有被问到技术细节

0. 自我介绍

1. 简单的介绍了一个项目

2. 在平时做项目的时候遇到的难点

几个方面简单讲了一下,团队协作的难点,技术难点,心理难点

3. 团队合作当中如果碰到一个 BUG,但是开发人员觉得不重要,你觉得重要怎么班?

坚持不放过为底线,换位思考,以产品和用户的角度劝说开发

4. 对加班有什么看法?

正常现象,完全可以接受

5. 为什么想去深圳?


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

选择排序 上一篇
堆排序 下一篇