服务器端开发经验总结 Linux C语言
简介
在进行服务器端开发的时候需要考虑一些算法和性能问题,经过了几年的开发,对这方面有了一些经验,现在写下来跟大家分享和讨论。
我主要是在Linux下进行C语言的开发,所以后面的实现都是基于Linux操作系统并用C语言来讲解。其它平台和语言需要考虑的问题是类似的只不过可能是实现细节上有一些差异,我尽量减少这些差异吧。注意一下讲解的所有内容都是基于32位系统的开发!
服务器程序开发核心是稳定,在稳定的前提下需要考虑效率。其中主要的公共模块是内存池和线程池。因为服务器程序一般都会长时间的运行,而且频繁的进行创建和释放内存的操作,这时如果使用系统的malloc和free方法,则会使系统中产生很多内存碎片,从而影响效率和稳定性。内存池的主要思想是先调用系统的malloc开辟一个很大的内存,然后对这个大内存进行管理,程序中要使用内存时,内存池分配内存,程序要释放内存时,只是通知内存池,而不真正释放内存。线程池,原理类似,并发处理一些任务的时候,需要使用很多线程,我们可以先创建很多线程,然后每次有任务需要处理,则找到一个空闲的线程让它来处理任务,处理完成后线程挂起。这样省去了每次一个任务都创建线程的时间开销。
预备知识
下面介绍一下在进行库的封装时用到的一些技巧。因为我们是使用C语言进行开发,所以为了使结构和效率得到很好的平衡,需要一些技巧来进行封装,从而使程序在保证C语言效率的前提下足够的模块化。
container_of 宏
首先介绍一下这个宏,这个宏是我在linux内核中看到的,它用于通过一个结构中的某个成员的指针,推算出整个结构的指针,先举一个例子吧。例如有如下一个结构:
1: struct my_struct
2: {
3: int a;
4: int b;
5: char c;
6: short d;
7: };
看看下面的代码:
1: int
2: main ()
3: {
4: struct my_struct mys; // 创建结构对象
5: struct my_struct* pmys; // 声明一个指向我们的结构的指针
6: char* pc = &(mys.c); // pc指向结构中c成员
7: int* pb = &(mys.b); // pb指向结构中b成员
8: pmys = container_of (pc, struct my_struct, c); // pmys实际上指向mys结构
9: pmys = container_of (pb, struct my_struct, b); // pmys还是指向mys结构
10: // 上面两行代码与 pmys = &mys; 的功能一样
11: ...
12: }
上面的代码说明,通过调用container_of可以通过某个结构中某个成员的指针(如pc或者pb),获得整个结构的指针。
这个宏需要三个参数,第一个参数是结构中某个成员的指针,第二个参数是结构的类型,第三个参数是,第一个参数中的成员指针指向的成员在结构的声明中的成名名称。这个宏定义如下:
1: #define offsetof(TYPE, MEMBER)((size_t) &((TYPE *)0)->MEMBER)
2:
3: #ifdef WIN32
4: // WIN32下(不支持类型安全检查)
5: #define container_of(ptr, type, member) /
6: ((type*)(ptr)-offsetof(type, member))
7:
8: #else
9: // linux下
10: #define container_of(ptr, type, member)({/
11: const typeof( ((type *)0)->member) *_mptr = (ptr); /
12: (type *)((char*)_mptr-offsetof(type, member));})
13:
14: #endif /* */
在上面的实现中,首先定义了一个offsetof宏,这个宏用于计算结构中的某个成员的地址相对于这个结构的起始地址的偏移。WIN32下可以使用系统自带offset宏。
container_of宏实际上是用结构中某个成员的地址减去这个成员相对于结构起始的偏移,就得到了结构的地址也就是指向这个结构的指针。linux下实现的时候:const typeof( ((type *)0)->member)* _mptr = (ptr);这句实际上是在做类型安全检查。保证给宏的第一个参数成员指针的类型就是指向这个成员类型的指针。typeof是gcc扩展,用于获得某个表达式的类型。在WIN32下我没有找到typeof的代替品,所以没有做类型安全检查。
有了container_of宏,我们可以做些什么工作呢?举个例子,如果我们写一个链表,教科书上一般将链表中的数据区域当作一个int。实际上可能是一个复杂的结构,而我作为编写链表的人,不知道使用者会在链表中存储什么数据。通常的做法是,数据区域就是一个void*,这样使用者可以用这个指针存储任何对象了。如果使用者要存储自己的数据,可以创建自定义结构的对象,然后用这个指针指向这个结构。链表中的每一个元素除了数据区占用的内存外还占用了一个void*.在服务器端开发时,数据量是很大的,这样就浪费了很多的void*。我们可以通过如下的方法来解决这个问题。
定义链表中的元素结构:
1: struct link_item
2: {
3: struct link_item* next;
4: };
而提供的操作链表的方法都是基于struct link_item* 指针的方法。用户在使用的时候声明自己的链表元素结构:
1: struct user_link_item
2: {
3: struct link_item lk_item;
4: int my_data1;
5: short my_data2;
6: // ...
7: };
这个结构中拥有struct link_item类型的一个成员lk_item。我们在使用链表的时候,总是将lk_item的地址传递给链表的方法。获取数据的时候获得的也是指向struct link_item的指针,但是我们可以使用container_of宏用链表方法返回的struct link_item的指针推算出struct user_link_item的指针,这样作为用户就可以获得存储在链表中的数据了。这样的实现中具体元素的内存也是外部使用者来分配的。
这种方法有些类似c++的继承机制。遇到继承的问题可以考虑使用这种方法。
引用计数器
在开发服务器程序的时候,我们经常是多线程的并行处理。对于要处理的数据就有一个线程安全的问题。最简单的线程安全处理是原子操作。每一次处理都是使用一条指令完成。其次是使用线程锁,进行线程同步。对于一个存储在某个数据结构中的数据来说,可能会在某个线程进行读取操作,而在另外的一个线程这个数据被删除了。我们可以模仿WIN32的COM处理这个问题时使用的方法。增加一个引用计数器,每次要对数据进行访问的时候都要递增这个数据的引用计数器,当使用完成的时候,递减这个数据的引用计数器。当引用计数器的值被递减为0的时候,这个数据就会真正的被删除。当然,引用计数器的递增和递减操作需要是线程安全的,完全可以使用原子操作来实现。
所有需要保证线程安全的数据对象,我们都可以通过上面讲到的使用container_of宏的实现的继承方式来实现。
通讯协议中变长字段的结构化处理
服务器端通讯通常会设计一套协议。在协议中通常是如下形式:
|4字节表示后面的字节数|1字节表示某个标志|1字节表示某个标志|2字节表示某个标志|n个字节表示内容|
上面的这中协议,我们如何来将它转换为一个结构呢?看看如下定义的结构:
1: // 错误的结构
2: struct wrong_struct
3: {
4: long size;
5: char f1;
6: char f2;
7: short f3;
8: char* content;
9: };
上面的结构明显是错误的,主要是content成员。content是一个指向字符串的指针它覆盖了协议中“n个字节表示内容”中的最前面的4个字节。如何解决这个问题呢?很简单:
1: struct right_struct
2: {
3: long size;
4: char f1;
5: char f2;
6: short f3;
7: char content[1];
8: };
我们将content声明为只有一个元素的字符串数组。字符串数组又是字符串指针,我们可以使用content成员来引用“n个字节表示内容”了。
当然这设计通讯协议的时候一定要考虑到字节对齐,关于字节对齐,这里就不再详细介绍了,可以参考一些C/C++的资料。
相关文章:

Backbone.js学习笔记 Hello World!
使用Backbone.js 和 MVC 架构创建一个典型的Hello world项目。虽然是“杀鸡用牛刀了”,毕竟是我第一次使用Backbone.js 依赖 jQuery 1.9.1Undersore.js 1.5.0Backbone.js开始 <!doctype html> <html> <head> <meta charset"utf-8"&g…

一文速览机器学习的类别(Python代码)
作者:泳鱼来源:算法进阶机器学习按照学习数据经验的不同,即训练数据的标签信息的差异,可以分为:*监督学习(supervised learning)*非监督学习(unsupervised learning)*半监…

Linux下分割与合并文件的方法
Linux下分割与合并文件的方法 切割合并文件在linux下用split和cat就可以完成。下面举些实例进行说明。1.文件切割文件切割模式分为两种: 文本文件 二进制模式。 1.1文本模式 文本模式只适用于文本文件,用这种模式切割后的每个文件都是可读的。文本模式又…

将网站程序放在tmpfs下
将网站程序放在tmpfs下然后用nginx直接做对外服务呢varnish或者squid都是利用内存和它的连接数来做到加速服务.但是如果是squid->nginx->fastcgi->mysql这样当中很多连接是开销在内部的连接之中而且如果客户端请求php.squid还需要将请求再转发至nginx,然后nginx再转发…

docker 连接容器
1.通过端口映射 sudo docker run -d -P training/webapp python app.py 容器有一个内部网络和IP地址(在使用Docker部分我们使用docker inspect命令显示容器的IP地址) -P 标记创建一个容器,将容器的内部端口随机映射到主机的高端口49000到4990…

新进展!英伟达用 AI 给纪录片配音,情绪语调拿捏得稳稳地
编译 | 禾木木 出品 | AI科技大本营(ID:rgznai100) AI 已经将合成语音从单调的机器人电话和传统 GPS 导航系统转变为智能手机和智能扬声器中动听的虚拟助手。 虽然日常和Siri、小爱或小度等对话时声音还是很机械,但最新的技术进展显示&#x…

揭开Annotation的面纱
Annotation是Java5、6只后的新特征(中文称之为注解),并且越来越多的得到了应用,比如Spring、Hibernate3、Struts2、iBatis3、JPA、JUnit等等都得到了广泛应用,通过使用注解,代码的灵活性大大提高。这些都是…

使用Nginx的proxy_cache缓存功能取代Squid
[文章作者:张宴 本文版本:v1.2 最后修改:2009.01.12 转载请注明原文链接:http://blog.s135.com/nginx_cache/] Nginx从0.7.48版本开始,支持了类似Squid的缓存功能。这个缓存是把URL及相关组合当作Key,用…

oracle grant 权限
grant connect,resource,dba to user;CONNECT角色: --是授予最终用户的典型权利,最基本的 CREATE SESSION --建立会话 RESOURCE角色: --是授予开发人员的 CREATE CLUSTER --建立聚簇 CREATE …

技术沙龙 | TeaTalk 带你深度探索 SDN 网络技术再创新
越来越多的企业、行业和政府机关顺应企业数字化转型、云服务和国家政策等趋势将业务迁移上云。随着移动云的快速发展,对网络提供差异化的服务能力也提出了很多新的考验。大规模数据中心、虚拟化 SDN 网络技术及超融合软硬一体可编程设备在云网络的应用已成为行业发展…

利用windows 2003实现服务器群集的搭建与架设(一) NLB群集的创建与架设
实验场景:西安凌云系统高科技有限公司利用IIS搭建了一个WEB站点,域名为nlb.angeldevil.com。由于业务的逐渐增加,网站速度也越来越慢,而且经常出现故障,为公司的利益带来了很多的不便;公司决定使用两台WEB站…

nginx 反向代理,动静态请求分离,proxy_cache缓存及缓存清除
一,nginx反向代理配置 #tomcat 显然就是用户访问www.wolfdream.com(需要设置本地localhost,将www.wolfdream.com指向nginx所在IP)的时候(或将www.wolfdream.com直接写在nginx所在的IP地址),将请求转到到后台的tomcat服务器,即127.…

深度强化学习的前景:帮助机器掌控复杂性
作者:数据实战派 来源:数据实战派深度强化学习,即机器通过测试其行为后果来学习的方法,是人工智能最有前途和影响力的领域之一。它将深度神经网络与强化学习结合在一起,可以通过训练实现多个步骤的目标。它是自动驾驶汽…

成绩转换(15)
#include<stdio.h> int main() {int n;char ch;while(scanf("%d",&n)!EOF){if(n>100||n<0) continue;if(n>90) chA;else if(n>80) chB;else if(n>70) chC;else if(n>60) chD;else chE;printf("%c\n",ch);} }转载于:https://ww…

pangolin最新版 v2.5.2.975
Pangolin是一款帮助渗透测试人员进行Sql注入测试的安全工具。 所谓的SQL注入测试就是通过利用目标网站的某个页面缺少对用户传递参数控制或者控制的不够好的情况下出现的漏洞,从而达到获取、修改、删除数据,甚至控制数据库服务器、Web服务器的目的的测试…

nginx 的proxy_cache才是王道
nginx 的proxy_cache才是性价比最高的缓存,我目前的配置是LiteSpeednginx,可以参考apachenginx将动态内容交给LiteSpeed或apache来处理,然后利用proxy_cache反向代理全部缓存在硬盘,变成静态内容,大家都知道nginx跑静态内容是有多厉害了吧,所以这样就可以小内存跑大PV.但是这样…

Android 占位符 %1$s %1$d
1、整型,比如“我今年23岁了”,这个23是整型的。在string.xml中可以这样写,<string name"old">我今年%1$d岁了</string> 在程序中,使用 [java] view plaincopy String sAgeFormat getResources().getStrin…

谁说技术男不适合养猫!90后程序员2天做出猫咪情绪识别软件
整理 | 王晓曼出品 | CSDN(ID:CSDNnews)9月1日,一则关于#程序员2天做出猫咪情绪识别软件#的话题登上微博热搜,参与阅读的人数达到了8218.1万,讨论次数1.3万,引发网友们的热议。高手在民间&#…

符合RESTful的接口规范
把api放在专属域名下,要带版本号 api的url中应该只有名词,和数据库的表或文档资源相对应;对资源(collection)的具体操作类型则由http方法动词表示 (安全性:不改变资源状态,类似只读&…

Nginx proxy_cache 使用示例
原文出处:http://blog.chenlb.com/2010/04/nginx-proxy-cache.html 动态网站使用缓存是很有必要的。前段时间使用了 nginx proxy_stroe 来保存静态页面,以达到缓存的目的。当然 proxy stroe 用来做缓存是不够好的方案。 缓存这一块当然还有 squid 之类的…

Lync 小技巧-49-Lync 自动备份-批量管理-用户(免费视频)
自从2010年开始,自从Lync Server 2010开始,我都在研究Lync 自动备份和批量管理用户,当年都做成功,做标准过.不过都是图片,未写博客,为什么呢?有可能你有这样那样的假设,但是今天可以…

数学很差的人能当程序员吗?
【CSDN 编者按】作者在大学时代受《程序员》杂志的启发,从数学专业投身计算机编程,毕业后进入软件开发行业。过去9年,他去过大厂敲代码,也曾在创业公司带过团队,一直从事“下一代”软件技术的研发,对于网上…

Nginx 学习笔记(六)引入线程池 性能提升9倍
原文地址:https://www.cnblogs.com/shitoufengkuang/p/4910333.html 一、前言 1、Nignx版本:1.7.11 以上 2、NGINX采用了异步、事件驱动的方法来处理连接。这种处理方式无需(像使用传统架构的服务器一样)为每个请求创建额外的专用…

Nginx源代码分析 - 日志处理
我看Nginx源代码的时候,感觉整个系统都在传递log指针。log在nginx里是比较关键的。日志和内存分配是最基础的两个起点代码,最好是在自己写的程序框架中早点完善并实现。以免未来要用大量的精力调整。1. 日志的源代码位置日志的源代码在src/code/ngx_log.…

strom.yaml配置
2019独角兽企业重金招聘Python工程师标准>>> 配置storm.yaml storm发行版在conf/storm.yaml包含了一些配置信息。你可以在这里看到默认配置。storm.yaml里面的配置比default.xml的优先级要高, 下面是要运行storm集群所必须的配置: 1. storm.zookeeper.se…

用 Python 快速制作海报级地图
作者:费弗里 来源:Python大数据分析 1 简介 基于Python中诸如matplotlib等功能丰富、自由度极高的绘图库,我们可以完成各种极富艺术感的可视化作品,关于这一点我在系列文章在模仿中精进数据可视化中已经带大家学习过很多案例了。 …

关于VS2012如何安装Windows Phone Toolkit
最近也是碰到很多问题,在编程的时候。这个问题是我遇到的比较棘手的一个,问了一堆人都说得很是模糊,最后还是琢磨出来了,深感欣慰。写下来以防以后忘记了怎么操作的,也期望能帮助到遇到同样问题的你。 首先让我先说了几…

论Oracle 11g数据库备份与恢复策略
11G中有个新特性,当表无数据时,不分配segment,以节省空间解决方案:1、insert一行,再roolback就会产生segment了该方法是在空表中插入一行数据,再删除,就会产生segment。则在数据库导出时可以导出…

使Apache实现gzip压缩
众所周知,在HTTP1.1中支持gzip压缩,这样可以缩小页面的容量从而加快页面的显示速度。可以使用常用HTTP抓包工具来检测一下你的站点是否开始了gzip压缩。 Apache默认的http.conf配置文件中没有开启gzip压缩,apache1.3.x可以用mod_gzip进行优化…

脸书 AI 识别误将黑人标记为「灵长类动物」
整理 | 禾木木 出品 | AI科技大本营(ID:rgznai100) 最近,Facebook用户在观看一段以黑人为主角的视频时,会看到一个自动生成的提示,询问他们是否愿意“继续观看灵长类动物的视频”。 视频的内容其实是几个黑人和警察发…