当前位置:  互联网>综合
本页文章导读:
    ▪大并发服务器内存转换的灵活运用,memcpy的思考      网络开发中,经常会碰到一些内存转换,如下面的场景: #define PACKAGE_PARSE_ERROR -1 #define PACKAGE_PARSE_OK 0 int parse_package( int* a, int* b, int* c, int* d, char* buf, int buf_len ) { if( !buf || buf_len < 16 ){.........
    ▪fastcgi 的使用与封装      服务器开发中不何或缺的要使用fastcgi,其使用方法如下: while( FCGI_Accept() >= 0 ){ printf( "Content-type: text/plain \r\n" "Content-Length: %d\r\n" "Conne.........
    ▪用newlisp管理windows下的nginx      我写了一个nginx.lsp文件,可以通过传递参数来进行Nginx进程管理。使用方法1. 从nginx站点下载windows版本的程序,加压后,将newlisp.lsp文件复制到该目录。2. 确保windows的system32目录下有newlisp.exe程.........

[1]大并发服务器内存转换的灵活运用,memcpy的思考
    来源: 互联网  发布时间: 2013-10-24

网络开发中,经常会碰到一些内存转换,如下面的场景:

#define PACKAGE_PARSE_ERROR -1
#define PACKAGE_PARSE_OK 0

int parse_package( int* a, int* b, int* c, int* d, char* buf, int buf_len )
{
        if( !buf || buf_len < 16 ){
                return PACKAGE_PARSE_ERROR;
        }
        memcpy( a, buf, 4 );
        memcpy( b, buf + 4, 4 );
        memcpy( c, buf + 8, 4 );
        memcpy( d, buf + 12, 4 );

        return PACKAGE_PARSE_OK;
}


这是网络解包的过程,封包的过程则是逆过程。

像这样的应用其实完全可以用整型强制转换来代替,而且效率会至少提高一倍。

为了说明问题,我们举个简单的例子:

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>

int main()
{
        int s;
        char buffer[4];

        memcpy(&s, buffer, 4 );
        s = *(int*)(buffer);
        return 0;
}

第10行和第11行的效果是一样的,10行采用的是内存复制,11行采用的是强制转换,为了方便比较,我们看一下汇编代码:

        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        subq    $16, %rsp
        leaq    -16(%rbp), %rcx
        leaq    -4(%rbp), %rax
        movl    $4, %edx
        movq    %rcx, %rsi
        movq    %rax, %rdi
        call    memcpy
        leaq    -16(%rbp), %rax
        movl    (%rax), %eax
        movl    %eax, -4(%rbp)
        movl    $0, %eax
        leave

代码中可以看出,内存复制方法占用了7-12行,共6行,强制转换占用了13-15行,共3行,指令上少了一半。

深究一下其实还不止,因为第12行其实是一个函数调用,必然会有栈的迁移,所以强制转换的效率比内存复制起码高一倍。

再看看glibc 的memcpy函数实现:

void *memcpy (void *dstpp, const void *srcpp, size_t len )
{
  unsigned long int dstp = (long int) dstpp;
  unsigned long int srcp = (long int) srcpp;

  if (len >= OP_T_THRES)
    {
      len -= (-dstp) % OPSIZ;
      BYTE_COPY_FWD (dstp, srcp, (-dstp) % OPSIZ);
      PAGE_COPY_FWD_MAYBE (dstp, srcp, len, len);
      WORD_COPY_FWD (dstp, srcp, len, len);
    }

  BYTE_COPY_FWD (dstp, srcp, len);

  return dstpp;
}

9-11行分别是三种处理方法,取决于 len 与 OP_T_THRES的比较,一般 OP_T_THRES 是8或16,对于len 小于OP_T_THRES的内存复制,glibc采用的是字节方式转换,即遍历每个字节,第个字节都要经过 “内存--寄存器--内存” 这个过程,CPU指令上可以说多了平空多了一倍。

从上面的分析可以看出,强制转换是节省了很大的运算时间,效率上至少提高一倍。不要小看这样的提升,在每秒几万并发的情况下,尤其每个并发都存在解包和封包的过程,这样的处理可以给我们带来相当大的性能提升。

开头中提到的解包过程,我们可以巧秒地运用强制转换,下面列出两种方法:

int parse_package( int* a, int* b, int* c, int* d, char* buf, int buf_len )
{
        if( !buf || buf_len < 16 ){
                return PACKAGE_PARSE_ERROR;
        }
        memcpy( a, buf, 4 );
        memcpy( b, buf + 4, 4 );
        memcpy( c, buf + 8, 4 );
        memcpy( d, buf + 12, 4 );

        return PACKAGE_PARSE_OK;
}

int parse_package2( int* a, int* b, int* c, int* d, char* buf, int buf_len )
{
        int* ibuf;
        if( !buf || buf_len < 16 ){
                return PACKAGE_PARSE_ERROR;
        }

        ibuf = buf;
        *a = ibuf[0];
        *b = ibuf[1];
        *c = ibuf[2];
        *d = ibuf[3];

        return PACKAGE_PARSE_OK;
}

parse_package汇编代码:

parse_package:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        subq    $48, %rsp
        movq    %rdi, -8(%rbp)
        movq    %rsi, -16(%rbp)
        movq    %rdx, -24(%rbp)
        movq    %rcx, -32(%rbp)
        movq    %r8, -40(%rbp)
        movl    %r9d, -44(%rbp)
        cmpq    $0, -40(%rbp)
        je      .L2
        cmpl    $15, -44(%rbp)
        jg      .L3
.L2:
        movl    $-1, %eax
        jmp     .L4.
L3:
        movq    -40(%rbp), %rcx
        movq    -8(%rbp), %rax
        movl    $4, %edx
        movq    %rcx, %rsi
        movq    %rax, %rdi
        call    memcpy
        movq    -40(%rbp), %rax
        leaq    4(%rax), %rcx
        movq    -16(%rbp), %rax
        movl    $4, %edx
        movq    %rcx, %rsi
        movq    %rax, %rdi
        call    memcpy
        movq    -40(%rbp), %rax
        leaq    8(%rax), %rcx
        movq    -24(%rbp), %rax
        movl    $4, %edx
        movq    %rcx, %rsi
        movq    %rax, %rdi
        call    memcpy
        movq    -40(%rbp), %rax
        leaq    12(%rax), %rcx
        movq    -32(%rbp), %rax
        movl    $4, %edx
        movq    %rcx, %rsi
        movq    %rax, %rdi
        call    memcpy
        movl    $0, %eax

L3段是我们的主段落,对a的赋值:

24-28行都是在“压栈”,为了memcpy函数内取出来,加上29行一共是6条,memcpy 解栈指令数>=3, 去处指令数>=4,不加算返回指令,一共指令数>6+3+4=13。


parse_package2汇编代码:

parse_package2:
.LFB1:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        movq    %rdi, -24(%rbp)
        movq    %rsi, -32(%rbp)
        movq    %rdx, -40(%rbp)
        movq    %rcx, -48(%rbp)
        movq    %r8, -56(%rbp)
        movl    %r9d, -60(%rbp)
        cmpq    $0, -56(%rbp)
        je      .L7     
        cmpl    $15, -60(%rbp)
        jg      .L8     
.L7:
        movl    $-1, %eax
        jmp     .L9     

.L8:
        movq    -56(%rbp), %rax
        movq    %rax, -8(%rbp)
        movq    -8(%rbp), %rax
        movl    (%rax), %edx
        movq    -24(%rbp), %rax
        movl    %edx, (%rax)
        movq    -8(%rbp), %rax
        addq    $4, %rax
        movl    (%rax), %edx
        movq    -32(%rbp), %rax
        movl    %edx, (%rax)
        movq    -8(%rbp), %rax
        addq    $8, %rax
        movl    (%rax), %edx
        movq    -40(%rbp), %rax
        movl    %edx, (%rax)
        movq    -8(%rbp), %rax
        addq    $12, %rax
        movl    (%rax), %edx
        movq    -48(%rbp), %rax
        movl    %edx, (%rax)
        movl    $0, %eax

L8是主段落,对a的赋值:

26-29行,一共4行解决。

这个例子中强制转换(parse_package2) 比内存复制(parse_package)要少2倍的CPU指令,性能至少可以提高2倍。

因此,我们的开发中应该尽量减少对内存复制的使用,而应该采用强制转换,现在64位服务器上,我们甚至可以用8个字节的long,下面的例子:

long lv;
char buffer[ 8 ];

memcpy( &lv, buffer, 8 );
lv = *(int*)(buffer);

这样就能更好的利用CPU的多字节指令提高性能。


作者:xiaofei_hah0000 发表于2013-5-22 11:42:18 原文链接
阅读:136 评论:0 查看评论

    
[2]fastcgi 的使用与封装
    来源: 互联网  发布时间: 2013-10-24

服务器开发中不何或缺的要使用fastcgi,其使用方法如下:

while( FCGI_Accept() >= 0 ){
                printf( "Content-type: text/plain \r\n"
                "Content-Length: %d\r\n"
                "Connection: close\r\n\r\n%s\r\n", strlen( buffer ), buffer );
        }


facgcgi 头文件有如下宏:

#undef  fprintf
#define	fprintf  FCGI_fprintf
#undef  printf
#define	printf   FCGI_printf

可以看出,已经对printf函数进行了宏转向,在程序里的printf 不再是标准输出了。这样就有一个问题,如果想调试打印信息到stdout中,就不行了。

现实开发中,可能会出现各种问题,有时候的确需要打印出一些信息,这时候可以修改fcgi_stdio.h头文件,改成如下:

#undef  _fprintf
#define _fprintf  FCGI_fprintf
#undef  _printf
#define	_printf   FCGI_printf

在程序中的相应地方也要换成相应的宏。


fastcgi 的解析可以通过这个函数:

char *getenv(const char *name) 

有如下几类参数:

CONTENT_TYPE

CONTENT_TYPE 得到请求类型 CONTENT_LENGTH 正文段长度 QUERY_STRING 请求字串
以如下一段HTTP请求报文为例:

GET /s?ie=utf-8&bs=%E8%BF%99%E6%98%AF&f=8&rsv_bp=1&rsv_spt=3&wd=%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%BC%80%E5%8F%91&rsv_sug3=11&rsv_sug=0&rsv_sug4=609&rsv_sug1=2&inputT=32681 HTTP/1.1
Host: www.baidu.com
Connection: keep-alive
Content-Type: text/html;charset=utf-8
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31
Referer: http://www.baidu.com/s?wd=%E8%BF%99%E6%98%AF&rsv_bp=0&ch=&tn=baidu&bar=&rsv_spt=3&ie=utf-8&rsv_sug3=4&rsv_sug=0&rsv_sug4=240&rsv_sug1=3&inputT=2835
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh-CN,zh;q=0.8
Accept-Charset: GBK,utf-8;q=0.7,*;q=0.3
Cookie: BAIDUID=A83324E58AE26486E46FC49ED127891B:FG=1; BDSVRTM=163; H_PS_PSSID=1439_2448_2454_2256_1788_2249; WWW_ST=1369271667063

CONTENT_TYPE 对应第5行,QUERY_STRING 对应第一行。


fastcgi 不是面向对象的,所有的任务都是在主循环内完成,包括解析,处理,响应。

在实际的开发中,为了提高效率,开发成员不用关心底层,我将其封装成一个类:fcgi_net_duty

#ifndef __FCGI_NET_H__
#define __FCGI_NET_H__

#define FCGI_NET_OK           0
#define FCGI_NET_ERROR        1
#define FCGI_NET_PARAM_ERROR  2

enum fcgi_net_type
{
        FCGI_NET_TEXT = 0,
        FCGI_NET_APP_STREAM,
        FCGI_NET_IMAGE,
};

class fcgi_net_duty
{
public:
        fcgi_net_duty();
        ~fcgi_net_duty();

        void do_request();

protected:
        void  do_prev();
        void  do_cast();
        virtual void  do_handle();
        void  do_write();
        void  do_finish();

        int get_req_int( const char* param, int *value );        
        int get_req_ll( const char* param, long long *value );
        int get_req_str( const char* param, const char **value );

        int get_cookie_int( const char* param, int *value );
        int get_cookie_ll( const char* param, long long *value );
        int get_cookie_str( const char* param, const char **value );


private:
        std::map< const char*, const char* > m_req;
        std::map< const char*, const char* > m_cookie;

        int rtn;        
        int content_type;
        int content_length;
        char* content_buf;

};



主循环放在do_request 函数里

fcgi_net_duty.cpp

void fcgi_net_duty::do_request()
{
        while( FCGI_Accept() >= 0 ){
                do_prev();
                do_cast();
                do_handle();
                do_write();
                do_finish();
        }
}

子类只需要关心相应的业务,每个业务可以用一个子类完成,如现有三个业务:busi1, busi2, busi3,我们以其中一个为例:

fcig_busi1.h

#ifndef __FCGI_BUSI1_H__
#define __FCGI_BUSI1_H__

class fcgi_busi1 : public fcgi_net_duty
{
public: 
        fcgi_busi1();
        ~fcgi_busi1();

        void do_handle();

};


#endif //__FCGI_BUSI1_H__


fcgi_busi1.cpp
void fcgi_busi1::do_handle()
{
        const char* busi1;
        int flag;

        rtn = FCGI_NET_ERROR;

        flag = get_req_str( "busi1", &busi1 );
        if( flag ){
                rtn = FCGI_NET_PARAM_ERROR;
                return;
        }

        sprintf( content_buf, "%s","your busi is ok" );
        rtn = FCGI_NET_OK;

}

int main()
{
        fcgi_busi1 busi1;
        busi1.do_request();
}

相应的makefile:

INC=-I./ 
LIB=-L/usr/lib64 -lfcgi

CPPFLAGS=-g -w $(INC)
CC=g++

BUSI1=./build/busi1
BUSI2=./build/busi2
BUSI3=./build/busi3

all: $(BUSI1) $(BUSI2) $(BUSI3)

$(BUSI1): ./fcgi_net_duty.o ./fcgi_busi1.o
        $(CC) -O $@ $^ $(LIB)

$(BUSI2): ./fcgi_net_duty.o ./fcgi_busi2.o
        $(CC) -O $@ $^ $(LIB)

$(BUSI3): ./fcgi_net_duty.o ./fcgi_busi3.o
        $(CC) -O $@ $^ $(LIB)

OK,现在一个轻型的fastcgi开发框架就搭建起来了,现实开发中,可以让一个成员开发网络处理,其他人专门做相应的业务处理,可以达到事半功倍的处理,我的理念就是让专业的人做专业的事,这样大家在专业技能上有更好的提升。




作者:xiaofei_hah0000 发表于2013-5-23 10:17:51 原文链接
阅读:77 评论:0 查看评论

    
[3]用newlisp管理windows下的nginx
    来源: 互联网  发布时间: 2013-10-24

我写了一个nginx.lsp文件,可以通过传递参数来进行Nginx进程管理。

使用方法

1. 从nginx站点下载windows版本的程序,加压后,将newlisp.lsp文件复制到该目录。

2. 确保windows的system32目录下有newlisp.exe程序。

3. 运行程序, 目前支持5个参数

newlisp nginx.lsp start|stop|monitor|reload|view

比如下面用view来观察进程:

c:\newlisp nginx.lsp view

Image Name                     PID Session Name        Session#    Mem Usage
========================= ======== ================ =========== ============
nginx.exe                     8048 Console                    1      6,716 K
nginx.exe                     4512 Console                    1      6,988 K
nginx.exe                     1684 Console                    1      6,836 K
nginx.exe                     4652 Console                    1      7,132 K
nginx.exe                     6512 Console                    1      6,816 K
nginx.exe                     6868 Console                    1      7,152 K

可以用stop参数关闭所有nginx进程。

具体实现代码如下:

;; parse argument as one of the following
;; start|stop|reload|monitor|view

(define (start)
  (process "start.bat")
  )

(define (kill-process e)
  (if (regex "^nginx.exe" e)
      (begin
	(set 'pid ((regex "^nginx.exe[ ]+([0123456789]+)" e) 3))
	(exec (append "Taskkill /PID " pid " /F")))))

(define (handle-element e)
  (if (regex "^nginx.exe" e)
      (set 'h true)))

(define (check-nginx-process)
  (set 'cmd "tasklist /fi \"imagename eq nginx.exe\"")
  (set 'result (exec cmd))
  (set 'h nil)
  (dolist (str result)
	  (handle-element str))
  )

(define (kill-nginx-processes)
  (set 'cmd "tasklist /fi \"imagename eq nginx.exe\"")
  (set 'result (exec cmd))
  (set 'h nil)
  (dolist (str result)
	  (kill-process str))
  )

(define (view)
  (set 'cmd "tasklist /fi \"imagename eq nginx.exe\"")
  (set 'result (exec cmd))
  (dolist (str result)
	  (println str))
  )

(define (stop)
  (set 'cmd "tasklist /fi \"imagename eq nginx.exe\"")
  (set 'result (exec cmd))
  
  (check-nginx-process)
  (if h
      (begin
	(println "stoping nginx now ...")
	(exec "nginx -s stop")
	(check-nginx-process)
	(if h
	    (begin
	      (println "kill nginx processes ...")
	      (kill-nginx-processes)
	      )))
      (println "do nothing because nginx is stopped"))
  )

(define (reload)
  (exec "nginx -s reload")
  )

(define (monitor)
  (while true
	 (begin
	   (sleep 10000)
	   (println "check nginx process at 10 seconds")
	   (check-nginx-process)
	   (unless h
		   (begin
		     (println "nginx is stopped, start it now ...")
		     (start))
		   (println "nginx process is alive")
		   )
	   )
	 )
  )

(define (main-fun)
  (set 'action ((main-args) 2))
  (unless action
	  (begin
	    (println "please pass one of the arguments: start|stop|reload|monitor|view")
	    (exit)))
  (if (= "start" action)
     (start))

  (if (= "stop" action)
     (stop))

  (if (= "reload" action)
     (reload))

  (if (= "monitor" action)
     (monitor))

  (if (= "view" action)
      (view))
  )

(main-fun)
(exit)



作者:sheismylife 发表于2013-5-23 17:12:38 原文链接
阅读:5 评论:0 查看评论

    
最新技术文章:
▪用户及权限基础 2---- Linux权限    ▪用户及权限基础 3---- Linux扩展权限    ▪git 简明教程(1) --创建及提交
▪背包 代码    ▪json对象的封装与解析    ▪01背包,完全背包,多重背包 ,模板代码
▪apache安装详解    ▪HDU 4668 Finding string (解析字符串 + KMP)    ▪《TCP-IP详解 卷1:协议》学习笔记(二)
▪《TCP-IP详解 卷1:协议》学习笔记(持续更新...    ▪windows下使用swig    ▪gensim试用
▪Linux Shell脚本编程--nc命令使用详解    ▪solr对跨服务器表联合查询的配置    ▪递归和非递归实现链表反转
▪Linux磁盘及文件系统管理 1---- 磁盘基本概念    ▪Cholesky Decomposition    ▪HTTP协议学习
▪用C语言写CGI入门教程    ▪用hdfs存储海量的视频数据的设计思路    ▪java多线程下载的实现示例
▪【原创】eAccelerator 一个锁bug问题跟踪    ▪hadoop学习之ZooKeeper    ▪使用cuzysdk web API 实现购物导航类网站
▪二维数组中的最长递减子序列    ▪内嵌W5100的网络模块WIZ812MJ--数据手册    ▪xss 跨站脚本攻击
▪RobotFramework+Selenium2环境搭建与入门实例    ▪什么是API    ▪用PersonalRank实现基于图的推荐算法
▪Logtype    ▪关于端口号你知道多少!    ▪Linux基本操作 1-----命令行BASH的基本操作
▪CI8.7--硬币组合问题    ▪Ruby on Rails 学习(五)    ▪如何使用W5300实现ADSL连接(二)
▪不允许启动新事务,因为有其他线程正在该会...    ▪getting start with storm 翻译 第六章 part-3    ▪递归求排列和组合(无重复和有重复)
▪工具类之二:RegexpUtils    ▪Coding Interview 8.2    ▪Coding Interview 8.5
▪素因子分解 Prime factorization    ▪C# DllImport的用法    ▪图的相关算法
▪Softmax算法:逻辑回归的扩展    ▪最小生成树---Kruskal算法---挑战程序设计竞赛...    ▪J2EE struts2 登录验证
▪任意两点间的最短路径---floyd_warshall算法    ▪Sqoop实现关系型数据库到hive的数据传输    ▪FFMPEG采集摄像头数据并切片为iPhone的HTTP Stream...
▪Ubuntu 13.04 – Install Jetty 9    ▪TCP/IP笔记之多播与广播    ▪keytool+tomcat配置HTTPS双向证书认证
▪安装phantomjs    ▪Page Redirect Speed Test    ▪windows media player 中播放pls的方法
▪sre_constants.error: unbalanced parenthesis    ▪http headers    ▪Google MapReduce中文版
▪The TCP three-way handshake (connect)/four wave (closed)    ▪网站反爬虫    ▪Log4j实现对Java日志的配置全攻略
▪Bit Map解析    ▪Notepad 快捷键 大全    ▪Eclipse 快捷键技巧 + 重构
▪win7 打开防火墙端口    ▪Linux Shell脚本入门--awk命令详解    ▪Linux Shell脚本入门--Uniq命令
▪Linux(Android NDK)如何避免僵死进程    ▪http Content-Type一览表    ▪Redis实战之征服 Redis + Jedis + Spring (二)
▪Tomcat7.0.40 基于DataSourceRealm的和JDBCRealm的资源...    ▪利用SQOOP将ORACLE到HDFS    ▪django输出 hello world
▪python re    ▪unity3D与网页的交互    ▪内存共享基本演示
▪python join    ▪不再为无限级树结构烦恼,且看此篇    ▪python实现变参
▪打开文件数限制功能不断地制造问题    ▪Arduino Due, Maple and Teensy3.0 的 W5200性能测试    ▪Selenium实例----12306网站测试
▪基于协同过滤的推荐引擎    ▪C4.5决策树    ▪C#HTTP代理的实现之注册表实现
▪nosql和关系型数据库比较?    ▪如何快速比较这两个字符串是否相等?    ▪hdoj 1863 畅通工程 最小生成树---prime算法
 


站内导航:


特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

©2012-2021,,E-mail:www_#163.com(请将#改为@)

浙ICP备11055608号-3