一、php中安装添加自定义的C扩展程序-ext_skel工具
publish:May 13, 2019 -Monday 扩展程序就你是PHP内置的程序,和php代码执行不一样,扩展程序在php进程启动时便已自动加载,所以其执行效率和性能比PHP代码高很多。多年前在硬件资源很受限的时代,程序员总是要想尽办法省资源提升代码性能,但现在硬件越来越廉价,对代码性能的极致得升已经变得越来越少考虑的事情了,也许有一些大公司还在使用自己的C扩展吧,对于一般的公司而言,着实没有必要去搞自己的C扩展程序,毕竟使用自定义的C扩展程序维护起来会很麻烦。不过即便如此,我觉得仍然有必要了解一下自定义扩展程序的使用方法。注明一下我这里使用的php是PHP 7.1.3版本。
php的安装目录中有一个文件ext/ext_skel,ext_skel是PHP官方提供的用于生成php扩展骨架代码的工具。执行./ext_skel –extname=module_name,module_name就是扩展的名称,执行后生成module_name目录及目录下的各文件,如下:
[root@123 ext]# pwd
/opt/download/php-7.1.3/ext
[root@123 ext]# ./ext_skel --help
./ext_skel --extname=module [--proto=file] [--stubs=file] [--xml[=file]]
[--skel=dir] [--full-xml] [--no-help]
--extname=module module is the name of your extension
--proto=file file contains prototypes of functions to create
--stubs=file generate only function stubs in file
--xml generate xml documentation to be added to phpdoc-svn
--skel=dir path to the skeleton directory
--full-xml generate xml documentation for a self-contained extension
(not yet implemented)
--no-help dont try to be nice and create comments in the code
and helper functions to test if the module compiled
[root@123 ext]# ./ext_skel --extname=kermitcal
Creating directory kermitcal
Creating basic files: config.m4 config.w32 .gitignore kermitcal.c php_kermitcal.h CREDITS EXPERIMENTAL tests/001.phpt kermitcal.php [done].
To use your new extension, you will have to execute the following steps:
1. $ cd ..
2. $ vi ext/kermitcal/config.m4
3. $ ./buildconf
4. $ ./configure --[with|enable]-kermitcal
5. $ make
6. $ ./sapi/cli/php -f ext/kermitcal/kermitcal.php
7. $ vi ext/kermitcal/kermitcal.c
8. $ make
Repeat steps 3-6 until you are satisfied with ext/kermitcal/config.m4 and
step 6 confirms that your module is compiled into PHP. Then, start writing
code and repeat the last two steps as often as necessary.
[root@123 ext]# cd kermitcal/
[root@123 kermitcal]#
#开发PHP扩展C程序前要先配置这里。config.m4文件中dnl是注释语法
#如果你的扩展用到了外部依赖,就配置--with-hello选项,否则配置--enable-hello选项
#PHP_ARG_WITH和PHP_ARG_ENABLE这两个宏用来配置configure选项,一个配置需要外部依赖的,另一个配置不需要外部依赖的
#看英文注释:If your extension references something external, use with:Otherwise use enable:
[root@123 kermitcal]# vim config.m4
dnl PHP_ARG_WITH(kermitcal, for kermitcal support,
dnl Make sure that the comment is aligned:
dnl [ --with-kermitcal Include kermitcal support])
#删除这下面3行代码的dnl注释,上面的我们暂不涉及
dnl PHP_ARG_ENABLE(kermitcal, whether to enable kermitcal support,
dnl Make sure that the comment is aligned:
dnl [ --enable-kermitcal Enable kermitcal support])
#kermitcal.c文件是扩展的逻辑代码所在处
[root@123 kermitcal]# vim kermitcal.c
#找到以下代码,这里便是编写扩展程序的地方
148 const zend_function_entry kermitcal_functions[] = {
#以下这行代码即是创建一个confirm_kermitcal_compiled方法的示例
149 PHP_FE(confirm_kermitcal_compiled, NULL) /* For testing, remove later. */
#于是我们参照上面增加下面这行
PHP_FE(kermitcal, NULL)
150 PHP_FE_END /* Must be the last line in kermitcal_functions[] */
151 };
#参考PHP_FUNCTION(confirm_kermitcal_compiled)添加PHP_FUNCTION(kermitcal)如下:
PHP_FUNCTION(kermitcal)
{
char *arg = NULL;
size_t arg_len, len;
zend_string *strg;
strg = "this is kermitcal extend.";
RETURN_STR(strg);
}
#按部就班,执行扩展安装
[root@123 kermitcal]# pwd
/opt/download/php-7.1.3/ext/kermitcal
[root@123 kermitcal]# /opt/modules/php7/bin/phpize
Configuring for:
PHP Api Version: 20160303
Zend Module Api No: 20160303
Zend Extension Api No: 320160303
[root@123 kermitcal]# ./configure --with-php-config=/opt/modules/php7/bin/php-config
#安装完成,可以看到新增加了一个kermitcal.so文件,然后到php.ini中添加extension="kermitcal.so并重启php
[root@123 kermitcal]# make && make install
Installing shared extensions: /opt/modules/php7/lib/php/extensions/no-debug-non-zts-20160303/
[root@123 kermitcal]# ll /opt/modules/php7/lib/php/extensions/no-debug-non-zts-20160303/
total 6732
-rwxr-xr-x 1 root root 30616 May 13 15:51 kermitcal.so
-rwxr-xr-x 1 root root 531053 Mar 30 2017 msgpack.so
-rwxr-xr-x 1 root root 3020492 Mar 30 2017 opcache.a
-rwxr-xr-x 1 root root 1749933 Mar 30 2017 opcache.so
-rwxr-xr-x 1 root root 1552035 Mar 30 2017 redis.so
#最后执行自定义扩展中的php方法,可以看到扩展添加成功
[root@123 etc]# php -r "kermitcal();"
this is kermitcal extend!
二、php扩展程序开发中的参数接受及处理-zend_parse_parameters函数
通过上面这篇文章 已经了解了php扩展程序的整个过程,揭开了php扩展程序的神秘,之后实际应用中php扩展程序的重点还是在于扩展C程序的开发,实际就是PHP_FUNCTION(扩展名){}的函数的编写。在上篇文章中未涉及到参数接收及传递,这里将继续尝试在方法中加入参数。
在生成的原始.c文件中,可以看到此行方法示例代码如下:zend_parse_parameters函数就是用来接收参数的。
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &arg, &arg_len) == FAILURE) {
return;
}
#zend_parse_parameters函数使用格式
zend_parse_parameters(int num_args TSRMLS_DC, char *type_spec, &参数1, &参数1_len, &参数2, &参数2_len…);
#zend_parse_parameters函数示例
zend_parse_parameters(ZEND_NUM_ARGS(), "s", &arg, &arg_len)
#. 参数解释:
第一个参数int num_args TSRMLS_DC:传递给函数的参数个数。通常的做法是传给它ZEND_NUM_ARGS()。即取出传递进来全部参数个数。
第二个参数TSRMLS_DC可以不用理会,用不用上都行,据说是为了线程安全默认总是传递TSRMLS_CC宏,看原始的PHP_FUNCTION中就没有使用。注意TSRMLS_DC与前面的参数间没有逗号。
第三个参数是一个字符串,指定了函数期望的参数类型,后面紧跟着需要随参数值更新的变量列表。其个数与后面的参数个数对应起来,比如ss就表示接受两个字符串参数,参数对应列表:
第四第五直到第n个参数,都是要传进来的参数。包括两个数据,一个表示字符串内容,另一个是字符串长度。
#. zend_parse_parameters方法的使用:
如下示例,修改扩展目录下的C文件kermitcal.c中的PHP_FUNCTION(kermitcal)方法,使用zend_parse_parameters接受参数age和username并输出,注意在方法zend_parse_parameters中我们使用 sl 要求了第一个参数username是字符串,第二个参数是数值。接受后把这两个数据打印出来。代码如下:
[root@123 kermitcal]# pwd
/opt/download/php-7.1.3/ext/kermitcal
[root@123 kermitcal]# sudo vim kermitcal.c
PHP_FUNCTION(kermitcal)
{
char *username;
size_t username_len;
int *age;
size_t age_len;
zend_string *strg;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl",&username, &username_len,&age, &age_len) == FAILURE){
php_printf("need params username(string) and age(int).!");
RETURN_NULL();
}
strg = strpprintf(0, "大家好,我叫%s, 今年%d岁.\n", username, age);
RETURN_STR(strg);
}
[root@123 kermitcal]# /opt/modules/php7/bin/phpize && ./configure --with-php-config=/opt/modules/php7/bin/php-config && make && make install
[root@123 etc]# php -r "echo kermitcal( 'kermit', 20);"
大家好,我叫kermit, 今年20岁.
#需要传入字符串的地方也可以传入数字,会自动转为字符串
[root@123 etc]# php -r "echo kermitcal( 12, 20);"
大家好,我叫12, 今年20岁.
#但需要传入数字的地方如果传入字符串会报错如下:
[root@123 etc]# php -r "echo kermitcal( 'kermit', 'rewq');"
PHP Warning: kermitcal() expects parameter 2 to be integer, string given in Command line code on line 1
Warning: kermitcal() expects parameter 2 to be integer, string given in Command line code on line 1
need params username(string) and age(int).![root@123 etc]#
#不过这样也不会报错,可见要求输入的是数字字符串
[root@123 etc]# php -r "echo kermitcal( 'kermit', '20');"
大家好,我叫kermit, 今年20岁.
在使用中,我一开始直接使用php_printf("大家好,我叫%s , 今年 %d 岁", username, age);打印这句,但是在执行的时候会在正常输出结果之后报出一个Segmentation Fault错误。后来我就使用了上示例中的先定义一个zend_string *strg;赋值给它后返回此值。
三、[转存] nginx指令优化、内核参数优化、系统连接数的优化
注:第三篇文章应该是从某处转载记录,未发现转载来源,如作者有意,请联系我删除。
1、nginx指令中的优化(在nginx配置文件中)
worker_processes 8;
#nginx进程数,建议按照cpu数目来指定,一般填写CPU数即可。
worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000;
#为每个进程分配cpu,上例中将8个进程分配到8个cpu,当然可以写多个,或者将一个进程分配到多个cpu。
worker_rlimit_nofile 102400;
#这个指令是指当一个nginx进程打开的最多文件描述符数目,理论值应该是最多打开文件数(ulimit -n)与nginx进程数相除,但是nginx分配请求并不是那么均匀,所以最好与ulimit -n的值保持一致。
use epoll;
#使用epoll的I/O模型
worker_connections 102400;
#每个进程允许的最多连接数,理论上每台nginx服务器的最大连接数为worker_processes*worker_connections。
keepalive_timeout 60;
#keepalive超时时间。
client_header_buffer_size 4k;
#客户端请求头部的缓冲区大小,这个可以根据你的系统分页大小来设置,一般一个请求的头部大小不会超过1k,不过由于一般系统分页都要大于1k,所以这里设置为分页大小。分页大小可以用命令getconf PAGESIZE取得。
open_file_cache max=102400 inactive=20s;
#这个将为打开文件指定缓存,默认是没有启用的,max指定缓存数量,建议和打开文件数一致,inactive是指经过多长时间文件没被请求后删除缓存。
open_file_cache_valid 30s;
#这个是指多长时间检查一次缓存的有效信息。
open_file_cache_min_uses 1;
#open_file_cache指令中的inactive参数时间内文件的最少使用次数,如果超过这个数字,文件描述符一直是在缓存中打开的,如上例,如果有一个文件在inactive时间内一次没被使用,它将被移除。
2、内核参数的优化
net.ipv4.tcp_max_tw_buckets = 6000
#timewait的数量,默认是180000。
net.ipv4.ip_local_port_range = 1024 65000
#允许系统打开的端口范围。
net.ipv4.tcp_tw_recycle = 1
#启用timewait快速回收。
net.ipv4.tcp_tw_reuse = 1
#开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接。
net.ipv4.tcp_syncookies = 1
#开启SYN Cookies,当出现SYN等待队列溢出时,启用cookies来处理。
net.core.somaxconn = 262144
#web应用中listen函数的backlog默认会给我们内核参数的net.core.somaxconn限制到128,而nginx定义的NGX_LISTEN_BACKLOG默认为511,所以有必要调整这个值。也可以在nginx配置文件的linsten 80 后面加上这个backlog参数,但是不能超过内核里面设定的数值,如:Linsten 80 backlog=65533;
net.core.netdev_max_backlog = 262144
#每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目。
net.ipv4.tcp_max_orphans = 262144
#系统中最多有多少个TCP套接字不被关联到任何一个用户文件句柄上。如果超过这个数字,孤儿连接将即刻被复位并打印出警告信息。这个限制仅仅是为了防止简单的DoS攻击,不能过分依靠它或者人为地减小这个值,更应该增加这个值(如果增加了内存之后)。
net.ipv4.tcp_max_syn_backlog = 262144
#记录的那些尚未收到客户端确认信息的连接请求的最大值。对于有128M内存的系统而言,缺省值是1024,小内存的系统则是128。
net.ipv4.tcp_timestamps = 0
#时间戳可以避免序列号的卷绕。一个1Gbps的链路肯定会遇到以前用过的序列号。时间戳能够让内核接受这种“异常”的数据包。这里需要将其关掉。
net.ipv4.tcp_synack_retries = 1
#为了打开对端的连接,内核需要发送一个SYN并附带一个回应前面一个SYN的ACK。也就是所谓三次握手中的第二次握手。这个设置决定了内核放弃连接之前发送SYN+ACK包的数量。
net.ipv4.tcp_syn_retries = 1
#在内核放弃建立连接之前发送SYN包的数量。
net.ipv4.tcp_fin_timeout = 1
#如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间。对端可以出错并永远不关闭连接,甚至意外当机。缺省值是60秒。2.2 内核的通常值是180秒,你可以按这个设置,但要记住的是,即使你的机器是一个轻载的WEB服务器,也有因为大量的死套接字而内存溢出的风险,FIN- WAIT-2的危险性比FIN-WAIT-1要小,因为它最多只能吃掉1.5K内存,但是它们的生存期长些。
net.ipv4.tcp_keepalive_time = 30
#当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时。
3、关于内核配置文件
可以通过命令: sysctl -a查看所有内核配置值。内核配置文件位置:/etc/sysctl.conf。修改编辑后要使配置立即生效可使用sysctl -p命令。
4、关于系统连接数的优化
linux默认open files 和 max user processes 为 1024,可使用ulimit -n命令查看当前用户可以打开最大文件描述符的数量,ulimit -u查看当前用户最大可用的进程数。ulimit -a 可以查看当前系统的所有限制。如果这些配置过小,当负载较大时,服务器很容易报error: too many open files。
修改配置:
1,使用 ulimit –n 65535 可即时修改,但重启后就无效了。(注ulimit -SHn 65535 等效 ulimit -n 65535 ,-S 指soft ,-H 指hard)
2. 在/etc/rc.local 中增加一行 ulimit -SHn 65535
3. 在/etc/profile 中增加一行 ulimit -SHn 65535
4. 在/etc/security/limits.conf 最后增加:
* soft nofile 65535
* hard nofile 65535
* soft nproc 65535
* hard nproc 65535
ulimit的其它参数列表:
选项 [options] | 含义 | 例子 |
-H | 设置硬资源限制,一旦设置不能增加。 | ulimit – Hs 64;限制硬资源,线程栈大小为 64K。 |
-S | 设置软资源限制,设置后可以增加,但是不能超过硬资源设置。 | ulimit – Sn 32;限制软资源,32 个文件描述符。 |
-a | 显示当前所有的 limit 信息。 | ulimit – a;显示当前所有的 limit 信息。 |
-c | 最大的 core 文件的大小, 以 blocks 为单位。 | ulimit – c unlimited; 对生成的 core 文件的大小不进行限制。 |
-d | 进程最大的数据段的大小,以 Kbytes 为单位。 | ulimit -d unlimited;对进程的数据段大小不进行限制。 |
-f | 进程可以创建文件的最大值,以 blocks 为单位。 | ulimit – f 2048;限制进程可以创建的最大文件大小为 2048 blocks。 |
-l | 最大可加锁内存大小,以 Kbytes 为单位。 | ulimit – l 32;限制最大可加锁内存大小为 32 Kbytes。 |
-m | 最大内存大小,以 Kbytes 为单位。 | ulimit – m unlimited;对最大内存不进行限制。 |
-n | 可以打开最大文件描述符的数量。 | ulimit – n 128;限制最大可以使用 128 个文件描述符。 |
-p | 管道缓冲区的大小,以 Kbytes 为单位。 | ulimit – p 512;限制管道缓冲区的大小为 512 Kbytes。 |
-s | 线程栈大小,以 Kbytes 为单位。 | ulimit – s 512;限制线程栈的大小为 512 Kbytes。 |
-t | 最大的 CPU 占用时间,以秒为单位。 | ulimit – t unlimited;对最大的 CPU 占用时间不进行限制。 |
-u | 用户最大可用的进程数。 | ulimit – u 64;限制用户最多可以使用 64 个进程。 |
-v | 进程最大可用的虚拟内存,以 Kbytes 为单位。 | ulimit – v 200000;限制最大可用的虚拟内存为 200000 Kbytes。 |