SegmentFault 一行代码上青天最新的文章
2015-09-16T18:29:53+08:00
https://segmentfault.com/feeds/blogs
https://creativecommons.org/licenses/by-nc-nd/4.0/
RPC-PHP-SDK 设计
https://segmentfault.com/a/1190000003755443
2015-09-16T18:29:53+08:00
2015-09-16T18:29:53+08:00
丁靖
https://segmentfault.com/u/samt42
7
<h2>Feature</h2>
<ul>
<li><p>服务端可自定义方法供客户端远程调用</p></li>
<li><p>服务端远程调用函数参数(顺序,数量)变化, 不会导致客户端服务端版本不兼容问题</p></li>
<li><p>支持多种传输协议 (protocolbuffer, msgpack, json, serialize)</p></li>
<li><p>支持多种通讯方式 (阻塞, 非阻塞, SSL阻塞, SSL非阻塞等)</p></li>
<li><p>支持自定义传输协议 (引入Rpc\Protocol\Interface接口)</p></li>
<li><p>高冷, 一定要高冷, API设计一定要高冷</p></li>
</ul>
<h2>API</h2>
<pre><code class="php">/**
* 传输相关
*/
// 服务端非阻塞socket
class rpc_transport_server_socket
{
protected $_port; //通讯端口
final public function __construct(int $port){
$this->_port = $port;
}
}
// 客户端阻塞socket
class rpc_transport_client_socket
{
protected $_host; //通讯地址
protected $_port; //通讯端口
final public function __construct(string $host, int $port) {
$this->_host = $host;
$this->_port = $port;
}
}
// 客户端非阻塞socket
class rpc_transport_async_client_socket extends rpc_transport_client_socket
{
// do something
}
// 传输层抽象类
abstract class rpc_transport
{
protected $_timeout; //传输超时, 支持浮点数, 单位:sec
protected $_socket; //传输句柄
abstract public function on(string $event, mixed $callback);
}
// 服务端传输
class rpc_transport_server extends rpc_transport
{
final public function __construct(rpc_transport_server_socket $socket, float $timeout){
$this->_socket = $socket;
$this->_timeout = $timeout;
}
public static function factory(int $port, float $timeout){
$this->_socket = new rpc_transport_server_socket($port);
$this->_timeout = $timeout;
}
public function on(string $event, mixed $callback){
SERVER->on(string $event, mixed $callback);
}
}
// 客户端传输
class rpc_transport_client extends rpc_transport
{
final public function __construct(rpc_transport_client_socket $socket, float $timeout){
$this->_socket = $socket;
$this->_timeout = $timeout;
}
public static function factory(string $host, int $port, float $timeout){
$this->_socket = new rpc_transport_client_socket($host, $port);
$this->_timeout = $timeout;
}
}
// 客户端传输
class rpc_transport_async_client extends rpc_transport
{
final public function __construct(rpc_transport_async_client_socket $socket, float $timeout){
$this->_socket = $socket;
$this->_timeout = $timeout;
}
public static function factory(string $host, int $port, float $timeout){
$this->_socket = new rpc_transport_async_client_socket($host, $port);
$this->_timeout = $timeout;
}
public function on(string $event, mixed $callback){
CLIENT->on(string $event, mixed $callback);
}
}
/**
* 协议相关
*/
//传输协议工厂类
class rpc_protocol
{
protected $_transport = null; //传输
protected $_protocol = null; //协议(protocolbuffer, msgpack, json, serialize)
private function __construct($protocol, rpc_transport $transport = NULL){
return self::factory($protocol, $transport);
}
public static function factory($protocol, rpc_transport $transport = NULL){
if(!isset($transport)) {
$this->_transport = $transport;
}
if(class_exists('rpc_protocol_' . $protocol)) {
$this->_protocol = new 'rpc_protocol_' . $protocol;
}
return $this;
}
public function getTransport(){
return $this->_transport;
}
public function setTransport($transport){
$this->_transport = $transport;
}
public function getProtocol(){
return $this->_protocol;
}
public function setProtocol($protocol){
$this->_protocol = $protocol;
}
public function pack($message) {
return $this->_protocol->pack($message);
}
public function unpack($message){
return $this->_protocol->unpack($message);
}
}
//传输协议接口
interface rpc_protocol_interface
{
public function pack($message);
public function unpack($message);
}
//JSON传输协议(打个样)
class rpc_protocol_json implements rpc_protocol_interface
{
public function pack($message){
...
}
public function unpack($message){
...
}
}
...
/**
* 服务端
*/
// 服务端抽象类
abstract class rpc_server_service
{
public function __call(){
// do something
}
}
// 服务端
class rpc_server
{
final public function __construct(rpc_server_interface $server_interface, rpc_protocol $protocol);
}
/**
* 客户端
*/
// 客户端
class rpc_client
{
private $_protocol = null;
public function __call(string $method, array $parameters){
// call $server_interface
}
final public function __construct(rpc_protocol $protocol){
$this->_protocol = $protocol;
}
}
//客户端回调函数抽象类
abstract class rpc_async_client_callback
{
private $_response = null;
public function getResult() {
// 返回结果值
return $this->_response;
}
public function onComplete($response){
$this->_response = $response;
}
public function onError($error){
throw new Exception($error);
}
}
// 非阻塞客户端
class rpc_async_client
{
private $_protocol = null;
private $_callback = null;
public function __call(string $method, array $parameters){
$callback = array_pop($parameters);
$response = $this->_protocol->getTransport()->receive();
$this->_protocol->getTransport()->on('complete', $callback->onComplete($response));
$this->_protocol->getTransport()->on('error', $callback->onError($error));
}
final public function __construct(rpc_protocol $protocol){
$this->_protocol = $protocol;
}
}</code></pre>
<h2>Example</h2>
<pre><code>// server
class server_hello extends rpc_server_service {
public function hello($message) {
echo $message;
}
}
$service = server_hello();
$server_transport = new rpc_transport_server(8080, 0.1);
$server_transport->on('connect', function(){
echo "Server:Connect.\n";
});
$server_protocol = new rpc_protocol::factory('json', $server_transport);
$server = new rpc_server($server_protocol, $service);
$server->serve();
// client
$client_transport = new rpc_transport_client('127.0.0.1', 8080, 0.1);
$client_transport->on('connect', function(){
echo "Client:Connect.\n";
});
$client_protocol = new rpc_protocol::factory('json', $client_transport);
$client = new rpc_client($client_protocol);
$client_transport->open();
$client->hello('world');
$client_transport->close();</code></pre>
[转] Linux Core Dump
https://segmentfault.com/a/1190000002888060
2015-06-09T11:48:26+08:00
2015-06-09T11:48:26+08:00
丁靖
https://segmentfault.com/u/samt42
0
<h2>原文地址</h2>
<p><a rel="nofollow" href="http://www.cnblogs.com/hazir/p/linxu_core_dump.html"></a><a rel="nofollow" href="http://www.cnblogs.com/hazir/p/linxu_core_dump.html">http://www.cnblogs.com/hazir/p/linxu_core_dump.html</a></p>
<h2>简介</h2>
<p>当程序运行的过程中异常终止或崩溃,操作系统会将程序当时的内存状态记录下来,保存在一个文件中,这种行为就叫做 Core Dump(中文有的翻译成“核心转储”)。我们可以认为 Core Dump 是“内存快照”,但实际上,除了内存信息之外,还有些关键的程序运行状态也会同时 Dump 下来,例如寄存器信息(包括程序指针、栈指针等)、内存管理信息、其他处理器和操作系统状态和信息。<br>
Core Dump 对于编程人员诊断和调试程序是非常有帮助的,因为对于有些程序错误是很难重现的,例如指针异常,而 Core Dump 文件可以再现程序出错时的情景。</p>
<h2>名词解释</h2>
<p>在半导体作为电脑内存材料之前,电脑内存使用的是<a rel="nofollow" href="http://whatis.techtarget.com/definition/core-dump">磁芯内存</a>(Magnetic Core Memory),Core Dump 中的 Core 沿用了磁芯内存的 Core 表达。图为磁芯内存的一个单元,来自 <a rel="nofollow" href="http://whatis.techtarget.com/definition/core-dump">Wikipedia</a>.</p>
<p>在 <strong>APUE</strong> 一书中作者有句话这样写的:</p>
<blockquote>
<p>Because the file is named core, it shows how long this feature has<br>
been part of the Unix System.</p>
</blockquote>
<p>这里的 core 就是沿用的是早期电脑磁芯内存中的表达,也能看出 Unix 系统 Core Dump 机制的悠久历史。</p>
<p>Dump 指的是拷贝一种存储介质中的部分内容到另一个存储介质,或者将内容打印、显示或者其它输出设备。dump 出来的内容是格式化的,可以使用一些工具来解析它。</p>
<p>现代操作系统中,用 Core Dump 表示当程序异常终止或崩溃时,将进程此时的内存中的内容拷贝到磁盘文件中存储,以方便编程人员调试。</p>
<h2>如何产生 core dump</h2>
<p>上面说当程序运行过程中异常终止或崩溃时会发生 core dump,但还没说到什么具体的情景程序会发生异常终止或崩溃,例如我们使用 kill -9 命令杀死一个进程会发生 core dump 吗?实验证明是不能的,那么什么情况会产生呢?</p>
<p>Linux 中信号是一种异步事件处理的机制,每种信号对应有其默认的操作,你可以在 <a rel="nofollow" href="http://man7.org/linux/man-pages/man5/core.5.html">这里</a> 查看 Linux 系统提供的信号以及默认处理。默认操作主要包括忽略该信号(Ingore)、暂停进程(Stop)、终止进程(Terminate)、终止并发生core dump(core)等。如果我们信号均是采用默认操作,那么,以下列出几种信号,它们在发生时会产生 core dump:</p>
<pre><code>bash</code><code>Signal Action Comment
SIGQUIT Core Quit from keyboard
SIGILL Core Illegal Instruction
SIGABRT Core Abort signal from abort
SIGSEGV Core Invalid memory reference
SIGTRAP Core Trace/breakpoint trap
</code></pre>
<p>当然不仅限于上面的几种信号。这就是为什么我们使用 Ctrl+z 来挂起一个进程或者 Ctrl+C 结束一个进程均不会产生 core dump,因为前者会向进程发出 SIGTSTP 信号,该信号的默认操作为暂停进程(Stop Process);后者会向进程发出SIGINT 信号,该信号默认操作为终止进程(Terminate Process)。同样上面提到的 kill -9 命令会发出 SIGKILL 命令,该命令默认为终止进程。而如果我们使用 Ctrl+\ 来终止一个进程,会向进程发出 SIGQUIT 信号,默认是会产生 core dump 的。还有其它情景会产生 core dump, 如:程序调用 abort() 函数、访存错误、非法指令等等。</p>
<h2>打开 core dump 功能</h2>
<p>在终端中输入命令 ulimit -c ,输出的结果为 0,说明默认是关闭 core dump 的,即当程序异常终止时,也不会生成 core dump 文件。<br>
我们可以使用命令 ulimit -c unlimited 来开启 core dump 功能,并且不限制 core dump 文件的大小; 如果需要限制文件的大小,将 unlimited 改成你想生成 core 文件最大的大小,注意单位为 blocks(KB)。<br>
用上面命令只会对当前的终端环境有效,如果想需要永久生效,可以修改文件 /etc/security/limits.conf文件,关于此文件的设置参看 <a rel="nofollow" href="http://whatis.techtarget.com/definition/core-dump">这里</a> 。增加一行:</p>
<pre><code>#/etc/security/limits.conf
#
#Each line describes a limit for a user in the form:
#
#<domain> <type> <item> <value>
* soft core unlimited
</code></pre>
<h2>修改 core 文件保存路径</h2>
<p>默认生成的 core 文件保存在可执行文件所在的目录下,文件名就为 core。<br>
通过修改 <code>/proc/sys/kernel/core_uses_pid</code> 文件可以让生成 core 文件名是否自动加上 pid 号。<br>
例如 <code>echo 1 > /proc/sys/kernel/core_uses_pid</code> ,生成的 core 文件名将会变成 core.pid,其中 pid 表示该进程的 PID。<br>
还可以通过修改 <code>/proc/sys/kernel/core_pattern</code> 来控制生成 core 文件保存的位置以及文件名格式。<br>
例如可以用 <code>echo "/tmp/corefile-%e-%p-%t" > /proc/sys/kernel/core_pattern</code> 设置生成的 core 文件保存在 “/tmp/corefile” 目录下,文件名格式为 “core-命令名-pid-时间戳”。<a rel="nofollow" href="http://man7.org/linux/man-pages/man5/core.5.html">这里</a> 有更多详细的说明!</p>
<h2>使用 gdb 调试 core 文件</h2>
<p>产生了 core 文件,我们该如何使用该 core 文件进行调试呢?Linux 中可以使用 GDB 来调试 core 文件,步骤如下:</p>
<ul>
<li>使用 gcc 编译源文件,加上 -g 以增加调试信息; 按照上面打开 core dump 以使程序异常终止时能生成 core 文件;</li>
<li>运行程序,当 core dump 之后,使用命令 gdb {program} {core} 来查看 core 文件,其中 {program} 为可执行程序名,{core} 为生成的 core 文件名。</li>
</ul>
<p>下面用一个简单的例子来说明:</p>
<pre><code>c</code><code>#include <stdio.h>
int func(int *p)
{
int y = *p;
return y;
}
int main()
{
int *p = NULL;
return func(p);
}
</code></pre>
<p>编译加上调试信息, 运行之后core dump, 使用 gdb 查看 core 文件.</p>
<pre><code>bash</code><code>guohailin@guohailin:~$ gcc core_demo.c -o core_demo -g
guohailin@guohailin:~$ ./core_demo
Segmentation fault (core dumped)
guohailin@guohailin:~$ gdb core_demo core_demo.core.24816
...
Core was generated by './core_demo'.
Program terminated with signal 11, Segmentation fault.
#0 0x080483cd in func (p=0x0) at core_demo.c:5
5 int y = *p;
(gdb) where
#0 0x080483cd in func (p=0x0) at core_demo.c:5
#1 0x080483ef in main () at core_demo.c:12
(gdb) info frame
Stack level 0, frame at 0xffd590a4:
eip = 0x80483cd in func (core_demo.c:5); saved eip 0x80483ef
called by frame at 0xffd590c0
source language c.
Arglist at 0xffd5909c, args: p=0x0
Locals at 0xffd5909c, Previous frame's sp is 0xffd590a4
Saved registers:
ebp at 0xffd5909c, eip at 0xffd590a0
(gdb)
</code></pre>
<p>从上面可以看出,我们可以还原 core_demo 执行时的场景,并使用 where 可以查看当前程序调用函数栈帧, 还可以使用 gdb 中的命令查看寄存器,变量等信息.</p>
<h2>参考资料</h2>
<p><a rel="nofollow" href="http://whatis.techtarget.com/definition/core-dump"></a><a rel="nofollow" href="http://whatis.techtarget.com/definition/core-dump">http://whatis.techtarget.com/definition/core-dump</a><br><a rel="nofollow" href="http://man7.org/linux/man-pages/man5/core.5.html"></a><a rel="nofollow" href="http://man7.org/linux/man-pages/man5/core.5.html">http://man7.org/linux/man-pages/man5/core.5.html</a></p>
制作 RPM 包
https://segmentfault.com/a/1190000002539129
2015-02-05T19:25:22+08:00
2015-02-05T19:25:22+08:00
丁靖
https://segmentfault.com/u/samt42
1
<h2>介绍</h2>
<h3>缩写</h3>
<ul>
<li>前世:RPM为Red Hat Package Manager的缩写(Red Hat软件包管理),顾名思义是Red Hat贡献出来的软件包管理。</li>
<li>今身:RPM为RPM Package Manager的缩写(GNU的既视感)。</li>
</ul>
<h3>特点</h3>
<ul>
<li>RPM包中除了包括程序运行时所需要的文件,也有其它的文件。</li>
<li>RPM包中的应用程序,有时除了自身所带的附加文件保证其正常以外,还需要其它特定版本文件,这就是软件包的依赖关系。</li>
<li>RPM可以让用户直接以binary方式安装软件包,并且可替用户查询是否已经安装了有关的库文件。</li>
<li>RPM删除程序时,它又会聪明地询问用户是否要删除有关的程序。</li>
<li>RPM升级软件时,RPM会保留原先的配置文件,这样用户就不用重新配置新的软件了。</li>
<li>RPM保留一个数据库,这个数据库中包含了所有的软件包的资料,通过这个数据库,用户可以进行软件包的查询。</li>
<li>RPM虽然是为Linux而设计的,但是它已经移值到SunOS、Solaris、AIX、Irix等其它UNIX系统上了。RPM遵循GPL版权协议,用户可以在符合GPL协议的条件下自由使用及传播RPM。</li>
</ul>
<h3>类型</h3>
<ul>
<li>二进制类包,包括rpm安装包(一般分为i386和x86等几种)和调式信息包等。</li>
<li>源码类包,源码包和开发包应该归位此类。</li>
</ul>
<h3>总结</h3>
<p>RPM制作就是指改造软件源代码使之符合RPM打包要求的过程,这也可以等价为RPM源码包的制作过程,因为当你有了源码包就可以直接编译得到二进制安装包和其他任意包。</p>
<p>依赖:</p>
<pre><code>yum install rpmdevtools
</code></pre>
<h2>开始</h2>
<h3>创建目录结构</h3>
<p>执行 <code>rpmdev-setuptree</code> 将在当前用户主目录下创建一个RPM构建根目录结构</p>
<p>注:如需改变默认位置,可以修改配置文件:<code>~/.rpmmacros</code>中变量<code>_topdir</code>对应的值。</p>
<pre><code>.
├── BUILD (打包过程中的工作目录)
├── RPMS (存放生成的二进制包, 不同硬件平台存放在不同文件夹)
├── SOURCES (存放打包资源, 包括源码打包文件和补丁文件等)
├── SPECS (存放SPEC文档)
└── SRPMS (存放生成的源码包)
</code></pre>
<h3>撰写SPEC文档</h3>
<p>SPEC撰写是打包RPM的核心, 下面是一个简单的SPEC文档, 其中包括了一些说明信息, 假设我们需要创建一个软件项目devrpm。</p>
<p>创建 devrpm.spec 文档:</p>
<pre><code>#软件包简要介绍
Summary: build develop environment。
#软件包的名字
Name: develop environment
#软件包的主版本号
Version: 0.0.1
#软件包的次版本号
Release: 1
#源代码包,默认将在上面提到的SOURCES目录中寻找
Source0: %{name}-%{version}.tar.gz
#授权协议
License: GPL
#定义临时构建目录,这个地址将作为临时安装目录在后面引用
BuildRoot:%{_tmppath}/%{name}-%{version}-%{release}-root
#软件分类
Group: Development/Tools
#软件包的内容介绍
%description
build local develop environment.
#表示预操作字段,后面的命令将在源码代码BUILD前执行
%prep
#构建BUILD环境,将解压源码压缩包到BUILD目录
%setup -q
#BUILD字段,将通过直接调用源码目录中自动构建工具完成源码编译操作
%build
#调用源码目录中的configure命令
./configure
#在源码目录中执行自动构建命令make
make
#安装字段
%install
#调用源码中安装执行脚本
make DESTDIR=$RPM_BUILD_ROOT install
#文件说明字段,声明多余或者缺少都将可能出错
%files
#设置文件权限属性
%defattr(-,root,root)
#声明/usr/local/bin/devrpm将出现在软件包中
/usr/local/bin/devrpm
#声明并设置文件属性
%doc %attr(0444,root,root) /usr/local/man/man1/devrpm.1
#同上,声明文档文件
%doc README
</code></pre>
<p>文档说明:</p>
<ul>
<li>
<code>BuildRoot:%{_tmppath}/%{name}-%{version}-%{release}-root</code>上面BuildRoot变量表示的是源码的临时按照目录,rpmbuild就是通过此目录获得将要按照到系统中的所有文件,而在SPEC文档后面<code>make install</code>命令中的参数。</li>
<li>
<code>DESTDIR=$RPM_BUILD_ROOT</code> 即是对该参数的引用,这个参数将传给Makefile文件一告诉自动构建工具应该安装文件那里。</li>
</ul>
<p>实际上我再前文提到过的Makefile需要作一些改造以适应RPM的构建就包括此操作,你的Makefile文件中至少要知道在RPM构建过程中引用此参数的值去控制安装操作的目标。</p>
<h3>放置源代码</h3>
<p>把一个名为<code>devrpm-0.0.1.tar.gz</code>的源码压缩文件放到<code>rpmbuild</code>根目录下的SOURCES目录下(注,确保此归档文件解压后的目录为devrpm-0.0.1,否则会有问题)。</p>
<p>到此一个完整的rpm打包环境已经构建完成,下面我们就可以开始构建二进制和源代码RPM包。</p>
<h3>构建RPM包</h3>
<p>构建RPM包是有命令rpmbuild在SPEC的指导下完成。</p>
<p>开始构建操作,首先进入到当前用户的rpmbuild根目录(即上面提到的目录环境)。</p>
<pre><code>#cd ~/rpmbuild/
</code></pre>
<p>执行如何命令,-ba表示build all,即生成包括二进制包和源代码包的所有RPM包,下来如果正常的话,rpmbuild将正常退出,同时在RPMS目录和SRPMS目录中将生成对应的RPM包。</p>
<pre><code>#rpmbuild -ba SPECS/hellorpm.spec
</code></pre>
typedef
https://segmentfault.com/a/1190000002532473
2015-02-03T15:43:33+08:00
2015-02-03T15:43:33+08:00
丁靖
https://segmentfault.com/u/samt42
0
<p>在原来的声明里逐步用别名替换一部分复杂声明,如此循环,把带变量名的部分留到最后替换,得到的就是原声明的最简化版。</p>
<p>举例:<br>
原声明:<code>void (*b[10]) (void (*)())</code></p>
<p>变量名为b,先替换右边部分括号里的,pFunParam为别名一:</p>
<p><code>typedef void (*pFunParam)()</code></p>
<p>再替换左边的变量b,pFunx为别名二:</p>
<p><code>typedef void (*pFunx)(pFunParam)</code></p>
<p>原声明的最简化版:</p>
<p><code>pFunx b[10]</code>;</p>
<p>原声明:<code>doube(*)() (*e)[9]</code></p>
<p>变量名为e,先替换左边部分,pFuny为别名一:</p>
<p><code>typedef double(*pFuny)()</code></p>
<p>再替换右边的变量e,pFunParamy为别名二</p>
<p><code>typedef pFuny (*pFunParamy)[9]</code></p>
<p>原声明的最简化版:</p>
<p><code>pFunParamy e</code></p>
<p>理解复杂声明可用的“右左法则”:从变量名看起,先往右,再往左,碰到一个圆括号</p>
<p>就调转阅读的方向;括号内分析完就跳出括号,还是按先右后左的顺序,如此循环,直</p>
<p>到整个声明分析完。举例:</p>
<p><code>int (*func)(int *p)</code></p>
<p>首先找到变量名func,外面有一对圆括号,而且左边是一个<em>号,这说明func是一个指针;然后跳出这个圆括号,先看右边,又遇到圆括号,这说明(</em>func)是一个函数,所以func是一个指向这类函数的指针,即函数指针,这类函数具有int*类型的形参,返回值类型是int。</p>
<p><code>int (*func[5])(int *)</code></p>
<p>func右边是一个[]运算符,说明func是具有5个元素的数组;func的左边有一个*,说明</p>
<p>func的元素是指针(注意这里的*不是修饰func,而是修饰func[5]的,原因是[]运算符</p>
<p>优先级比*高,func先跟[]结合)。跳出这个括号,看右边,又遇到圆括号,说明func数</p>
<p>组的元素是函数类型的指针,它指向的函数具有int*类型的形参,返回值类型为int。</p>
<p>这种用法是比较复杂的,出现的频率也不少,往往在看到这样的用法却不能理解,相信以上的解释能有所帮助。</p>
如何优雅地在github上贡献代码
https://segmentfault.com/a/1190000000736629
2014-10-23T13:35:37+08:00
2014-10-23T13:35:37+08:00
丁靖
https://segmentfault.com/u/samt42
96
<p><a rel="nofollow" href="http://zh.wikipedia.org/wiki/GitHub">Github</a> 相信已经成为家喻户晓的代码托管工具, 但访问了多位周围编程爱好者后发现, 对其的使用还仅限于 <strong>下载项目源码</strong> 和 <strong>备份项目源码</strong> 的程度, 今天我就来介绍一下一个比较重要的使用场景 <strong>贡献代码</strong></p>
<p>以 <a rel="nofollow" href="https://github.com/swoole/swoole-src">swoole</a> 为例:</p>
<h2>Fork 项目</h2>
<ul>
<li>首先需要fork这个项目, 进入项目页面, 点击右上角的Fork按钮</li>
<li>你的 github 帐号中会出现 swoole/swoole-src 这个项目</li>
<li>在本地电脑(Linux)上使用以下命令: 得到一个 swoole-src 文件夹</li>
</ul>
<pre><code>git clone git@github.com:samt42/swoole-src.git
</code></pre>
<h2>获取原项目代码</h2>
<ul>
<li>进入 swoole-src 文件夹, 添加 swoole 的远程地址</li>
</ul>
<pre><code>git remote add upstream https://github.com/swoole/swoole-src.git
</code></pre>
<ul>
<li>获取 swoole 最新源码</li>
</ul>
<pre><code>git pull upstream master
</code></pre>
<p>现在我们在 fork 来的 master 分支上, 这个 master 留作跟踪 upstream 的远程代码...</p>
<h2>创建分支</h2>
<ul>
<li>
<p>好了, 现在可以开始贡献我们的代码了<br>
按照国际惯例, 我们一般不在 master 上提交新代码, 而需要为新增的功能或者fixbug建立新分支, 再合并到 master 上, 使用以下代码创建分支</p>
<pre><code>git checkout -b branch1
</code></pre>
<p>现在我们可以在分支上更改代码了</p>
</li>
<li>
<p>假设我们已经添加了一些代码, 提交到代码库</p>
<pre><code>git commit -a -m "new commit"
</code></pre>
</li>
</ul>
<h2>合并修改</h2>
<ul>
<li><p>一个常见的问题是远程的 upstream (swoole/swoole-src) 有了新的更新, 从而会导致我们提交的 Pull Request 时会导致冲突, 因此我们可以在提交前先把远程其他开发者的commit和我们的commit合并.</p></li>
<li>
<p>使用以下代码切换到 master 分支:</p>
<pre><code>git checkout master
</code></pre>
</li>
<li>
<p>使用以下代码拉出远程的最新代码:</p>
<pre><code>git pull upstream master
</code></pre>
</li>
<li>
<p>切换回 branch1:</p>
<pre><code>git checkout branch1
</code></pre>
<pre><code>> 如果忘记自己之前建的分支名可以用 `git branch` 查看
</code></pre>
</li>
<li>
<p>把 master 的 commit 合并到 branch1:</p>
<pre><code>git rebase master
</code></pre>
</li>
<li>
<p>把更新代码提交到自己的 branch1 中:</p>
<pre><code>git push origin branch1
</code></pre>
</li>
</ul>
<h2>Pull Request</h2>
<ul>
<li>提交 Pull Request<br>
你可以在你的 github 代码仓库页面切换到 branches 页面点击 branch1 分支后点击 <code>New pull request</code> 按钮, 添加相关注释后提交.<br>
OR<br>
切换到 branch1 分支的代码仓库点击 <code>Compare & pull request</code> 按钮, 添加相关注释后提交.</li>
</ul>
typedef的一些高级用法
https://segmentfault.com/a/1190000000521561
2014-05-27T17:39:42+08:00
2014-05-27T17:39:42+08:00
丁靖
https://segmentfault.com/u/samt42
1
<p>在原来的声明里逐步用别名替换一部分复杂声明,如此循环,把带变量名的部分留到最后替换,得到的就是原声明的最简化版。</p>
<p>举例: <br>原声明:<code>void (*b[10]) (void (*)())</code></p>
<p>变量名为b,先替换右边部分括号里的,pFunParam为别名一:</p>
<p><code>typedef void (*pFunParam)()</code></p>
<p>再替换左边的变量b,pFunx为别名二:</p>
<p><code>typedef void (*pFunx)(pFunParam)</code></p>
<p>原声明的最简化版:</p>
<p><code>pFunx b[10]</code></p>
<p>原声明:<code>doube(*)() (*e)[9]</code></p>
<p>变量名为e,先替换左边部分,pFuny为别名一:</p>
<p><code>typedef double(*pFuny)()</code></p>
<p>再替换右边的变量e,pFunParamy为别名二:</p>
<p><code>typedef pFuny (*pFunParamy)[9]</code></p>
<p>原声明的最简化版:</p>
<p><code>pFunParamy e</code></p>
<p>理解复杂声明可用的“右左法则”:从变量名看起,先往右,再往左,碰到一个圆括号</p>
<p>就调转阅读的方向;括号内分析完就跳出括号,还是按先右后左的顺序,如此循环,直</p>
<p>到整个声明分析完。举例:</p>
<p><code>int (*func)(int *p)</code></p>
<p>首先找到变量名func,外面有一对圆括号,而且左边是一个*号,这说明func是一个指针</p>
<p>;然后跳出这个圆括号,先看右边,又遇到圆括号,这说明(*func)是一个函数,所以</p>
<p>func是一个指向这类函数的指针,即函数指针,这类函数具有int*类型的形参,返回值</p>
<p>类型是int。</p>
<p><code>int (*func[5])(int *)</code></p>
<p>func右边是一个[]运算符,说明func是具有5个元素的数组;func的左边有一个*,说明</p>
<p>func的元素是指针(注意这里的*不是修饰func,而是修饰func[5]的,原因是[]运算符</p>
<p>优先级比*高,func先跟[]结合)。跳出这个括号,看右边,又遇到圆括号,说明func数</p>
<p>组的元素是函数类型的指针,它指向的函数具有int*类型的形参,返回值类型为int。</p>
<p>这种用法是比较复杂的,出现的频率也不少,往往在看到这样的用法却不能理解,相信以上的解释能有所帮助。</p>
autoconf-101
https://segmentfault.com/a/1190000000505631
2014-05-14T13:59:23+08:00
2014-05-14T13:59:23+08:00
丁靖
https://segmentfault.com/u/samt42
1
<h2>整体流程</h2>
<ol>
<li><p>源码根目录调用autoscan脚本,生成configure.scan文件,然后将此文件重命名为configure.ac(或configure.in,早期使用.in后缀)</p></li>
<li><p>修改【configure.ac】,利用autoconf提供的各种M4宏,配置项目需要的各种自动化探测项目</p></li>
<li><p>编写【自定义宏】,建议每个宏一个单独的*.m4文件;</p></li>
<li><p>调用aclocal收集configure.ac中用到的各种非Autoconf的宏,包括自定义宏;</p></li>
<li><p>调用autoheader,扫描configure.ac(configure.in)、acconfig.h(如果存在),生成config.h.in宏定义文件,里面主要是根据configure.ac中某些特定宏(如AC_DEFINE)生成的#define和#undefine宏,configure在将根据实际的探测结果决定这些宏是否定义(具体见后面例子)。</p></li>
<li><p>按照automake规定的规则和项目的目录结构,编写一个或多个【Makefile.am】(Makefile.am数目和存放位置和源码目录结构相关),Makefile.am主要写的就是编译的目标及其源码组成。</p></li>
<li><p>调用automake,将每个Makefile.am转化成Makefile.in,同时生成满足GNU编码规范的一系列文件(带-a选项自动添加缺少的文件,但有几个仍需要自己添加,在执行automake前需执行touch NEWS README AUTHORS ChangeLog)。如果configure.ac配置了使用libtool(定义了AC_PROG_LIBTOOL宏(老版本)或LT_INIT宏),需要在此步骤前先在项目根目录执行libtoolize --automake --copy --force,以生成ltmain.sh,供automake和config.status调用。</p></li>
<li><p>调用autoconf,利用M4解析configure.ac,生成shell脚本configure。以上几步完成后,开发者的工作就算完成了,后面的定制就由开源软件的用户根据需要给configure输入不同的参数来完成。</p></li>
<li><p>用户调用configure,生成Makefile,然后make && make install。</p></li>
</ol>
<h2>具体操作</h2>
<ol>
<li><p>进入project目录</p></li>
<li><p>运行autoscan命令</p></li>
<li><p>将configure.scan 文件重命名为configure.in,并修改configure.in文件</p></li>
<li><p>在project目录下新建Makefile.am文件,并在各个子目录下也新建makefile.am文件</p></li>
<li><p>在project目录下新建NEWS、 README、 ChangeLog 、AUTHORS文件</p></li>
<li><p>将/usr/share/automake-1.X/目录下的depcomp和complie文件拷贝到本目录下</p></li>
<li><p>运行aclocal命令</p></li>
<li><p>运行autoconf命令</p></li>
<li><p>运行automake -a命令</p></li>
<li><p>运行./confiugre脚本</p></li>
</ol>
<h2>图解</h2>
<p><img src="http://segmentfault.com/img/bVck7P" alt="产生Makefile的流程" title="产生Makefile的流程"></p>
thrift-101
https://segmentfault.com/a/1190000000475007
2014-05-01T22:40:08+08:00
2014-05-01T22:40:08+08:00
丁靖
https://segmentfault.com/u/samt42
2
<p>此教程基于 Linux CentOS 6.0, php 5.3X 环境</p>
<h2>下载</h2>
<pre><code>git clone https://git-wip-us.apache.org/repos/asf/thrift.git thrift
</code></pre>
<blockquote>
<p>PS:官网下那个安装包有诸多问题</p>
</blockquote>
<h2>安装</h2>
<p>环境变量:</p>
<pre><code>export PATH=$PATH:{php_src}/bin
</code></pre>
<p>这一步很重要,用于系统寻找 phpize 和 php-config</p>
<p>安装依赖库:</p>
<pre><code>yum install automake libtool flex bison pkgconfig gcc-c++ boost-devel libevent-devel zlib-devel openssl-devel
</code></pre>
<p>安装 autoconf (需要2.65版本以上):</p>
<pre><code>wget http://ftp.gnu.org/gnu/autoconf/autoconf-latest.tar.gz
tar
tar xzvf autoconf-latest.tar.gz
cd autoconf-xxx
./configure --prefix=/usr
</code></pre>
<p><code>libtool, autoconf, automake</code> 依赖关系真是让人捉急啊...</p>
<p>安装 thrift:</p>
<pre><code>./bootstrap.sh
./configure --with-cpp --with-boost --without-python --without-csharp --without-java --without-erlang --without-perl --with-php --with-php_extension --without-ruby --without-haskell --without-go --without-d --without-nodjs --without-lua --without-openssl=/usr
make && make install
</code></pre>
<p>检查工作:</p>
<ul>
<li>查看PHP扩展安装目录中有 <code>thrift_protocol.so</code> 则 PHP 扩展安装成功</li>
<li>查看 <code>/usr/local/include/thrift/c_glib</code> 存在则C Library安装成功</li>
<li>查看 <code>/usr/local/include/thrift/(server|protocol|...)</code> 存在则C++ Library安装成功</li>
</ul>
<p>修改 php.ini:</p>
<p>添加</p>
<pre><code>extension="thrift_protocol.so"
</code></pre>
<h2>Demo</h2>
<p>创建 demo.thrift:</p>
<pre><code>namespace cpp demo
namespace php demo
/*
C like comments are supported
*/
// This is also a valid comment
typedef string my_string // We can use typedef to get pretty names for the types we are using
service Demo
{
my_string hello(1:my_string thing),
}
</code></pre>
<p>生成PHP客户端:</p>
<pre><code>thrift --gen php demo.thrift
</code></pre>
<p>生成C++服务端:</p>
<pre><code>thrift --gen cpp demo.thrift
</code></pre>
<p>会产生两个文件夹 <code>gen-php</code> 和 <code>gen-cpp</code></p>
<p>编译服务端:</p>
<p>找到 <code>libthrift-1.0.0-dev.so</code> 的位置 (我机器上的位置在<code>/usr/local/lib</code> )</p>
<p>1) 在<code>gen-cpp</code>下</p>
<ul>
<li>创建服务端文件, 这里我复制了<code>thrfit</code>生成的<code>skeleton</code>文件</li>
</ul>
<pre><code>cp Demo_server.skeleton.cpp Demo_server.cpp
</code></pre>
<ul>
<li>创建<code>Makefile</code>
</li>
</ul>
<pre><code>GEN_SRC := Demo.cpp demo_php_constants.cpp demo_php_types.cpp
GEN_OBJ := $(patsubst %.cpp,%.o, $(GEN_SRC))
THRIFT_DIR := /usr/local/include/thrift
BOOST_DIR := /usr/local/include
INC := -I$(THRIFT_DIR) -I$(BOOST_DIR)
.PHONY: all clean
all: demo_server
%.o: %.cpp
$(CXX) -Wall $(INC) -c $< -o $@
demo_server: Demo_server.o $(GEN_OBJ)
$(CXX) -L/usr/local/lib -lthrift $^ -o $@
clean:
$(RM) *.o demo_server
</code></pre>
<ul>
<li>添加 ld 路径: <code>/etc/ld.so.conf.d</code> 下创建 <code>libthrift-x86_64.conf</code> (名字可以自定义, 以<code>.conf</code>结尾就行) 文件, 添加路径<code>/usr/local/lib</code>.</li>
<li>
<code>make</code>编译后在文件夹下会生成<code>demo_server</code>.</li>
<li>
<code>./demo_server</code>启动服务端.</li>
</ul>
<p>2) 在<code>gen-php</code>下</p>
<ul>
<li>把<code>{thrift_src}/lib/php/lib/Thrift</code>文件夹复制到<code>gen-php</code>下</li>
<li>在<code>demo</code>文件下创建文<code>client.php</code>
</li>
</ul>
<pre><code><?php
require_once '../Thrift/ClassLoader/ThriftClassLoader.php';
require_once 'Demo.php';
require_once 'Types.php';
use Thrift\ClassLoader\ThriftClassLoader;
use Thrift\Transport\TSocket;
use Thrift\Transport\TBufferedTransport;
use Thrift\Protocol\TBinaryProtocolAccelerated;
use demo\DemoClient;
$loader = new ThriftClassLoader();
$loader->register();
$loader->registerNamespace('Thrift\Base', dirname(dirname(__FILE__)));
$loader->registerNamespace('Thrift\Type', dirname(dirname(__FILE__)));
$loader->registerNamespace('Thrift\Exception', dirname(dirname(__FILE__)));
$loader->registerNamespace('Thrift\Transport', dirname(dirname(__FILE__)));
$loader->registerNamespace('Thrift\Protocol', dirname(dirname(__FILE__)));
$loader->registerNamespace('Thrift\Factory', dirname(dirname(__FILE__)));
$loader->registerNamespace('Thrift\StringFunc', dirname(dirname(__FILE__)));
$loader->registerNamespace('demo', dirname(dirname(__FILE__)));
//TBase
$loader->loadClass('TBase');
//Type
$loader->loadClass('TType');
$loader->loadClass('TMessageType');
//Transport
$loader->loadClass('TSocket');
$loader->loadClass('TBufferedTransport');
//Protocol
$loader->loadClass('TProtocol');
$loader->loadClass('TBinaryProtocolAccelerated');
//Factorys
$loader->loadClass('TStringFuncFactory');
//StringFunc
$loader->loadClass('Core');
//Exception
$loader->loadClass('TException');
$loader->loadClass('TProtocolException');
$loader->loadClass('TApplicationException');
try {
$host = '127.0.0.1';
$port = 9090;
$socket = new TSocket($host ,$port);
$transport = new TBufferedTransport($socket, 1024, 1024);
$protocol = new TBinaryProtocolAccelerated($transport);
$client = new DemoClient($protocol);
$transport->open();
$ret = $client->hello("Hello world!!");
echo $ret;
$transport->close();
} catch (TException $e) {
print 'Something went wrong: ' . $e->getMessage() . "\n";
}
</code></pre>
<p>执行:</p>
<pre><code>php client.php
</code></pre>
<p>服务端显示:</p>
<pre><code>hello
</code></pre>
<p>(全文完)</p>