2012年7月23日


共享内存优点:1.在进程之间不通过内核传递数据,即不通过系统调用拷贝数据,达到快速,高效的数据传输。
                    2.随内核持续
*nix的共享内存有两套API:Posix和System V

两者的主要差别是共享内存的大小
1.Posix共享内存大小可通过函数ftruncate随时修改
2.System V共享内存大小在创建时就已经确定,而且最大值根据系统有所不同

 Posix共享内存 
    #include <sys/mman.h>  (mmap,munmap,msync,shm_open,shm_unlink)

最主要的函数  -- mmap
     void* mmap(void* addr,size_t len,int prot,int flags,int fd,off_t offset)
     函数将一个句柄映射到内存中,这个句柄可以是open的文件句柄,也可以是shm_open的共享内存区对象。当fd=-1时为匿名共享内存。
     *nix一切皆文件的观点,shm_open也是在/dev/shm目录下创建一个文件对象,返回对象的描述符。
     mmap将句柄作为共享内存的底层支撑对象,映射到内存中,这样可以不通过read、write在进程之间共享内存。由此推测一下,在*nix的进程间传递数据更加原始的方法是进程间读写一个文件。但是频繁的open、read、write、lseek系统调用会消耗过多的计算资源。所以想到了将这个文件句柄映射到内存中,这样就提高了进程间传递数据的效率。

需要注意的函数 -- msync
      当修改了内存映射区的内存后,内核会在某个时刻将文件的内容更新。为了确信文件被更新,调用函数msync。文件的更新可以是同步(MS_SYNC)也可以是异步(MS_ASYNC)。(估计这里也是调用了函数write更新文件)

System V共享内存
    #include <sys/shm.h>  (shmget,shmat,shmdt,shmctl)
由于System V的共享内存有大小的限制,所以可考虑,使用共享内存数组来解决这个问。虽然数组的大小即一个进程可以获取共享内存的数量也是有限制,但是可以缓解System V单个共享内存过小的问题。
posted @ 2012-07-23 00:08 Range 阅读(1413) | 评论 (0)编辑 收藏

2012年7月1日

receive works as follows:
1. When we enter a receive statement, we start a timer (but only if an after section is present in the expression).
2. Take the first message in the mailbox and try to match it against Pattern1, Pattern2, and so on. If the match succeeds, the message is removed from the mailbox, and the expressions following the pattern are evaluated.
3. If none of the patterns in the receive statement matches the first message in the mailbox, then the first message is removed from the mailbox and put into a “save queue.” The second message in the mailbox is then tried. This procedure is repeated until a matching message is found or until all the messages in the mailbox have been examined.
4. If none of the messages in the mailbox matches, then the process is suspended and will be rescheduled for execution the next time a new message is put in the mailbox. Note that when a new message arrives, the messages in the save queue are not rematched; only the new message is matched.
5. As soon as a message has been matched, then all messages that have been put into the save queue are reentered into the mailbox in the order in which they arrived at the process. If a timer was set, it is cleared.
6. If the timer elapses when we are waiting for a message, then evaluate the expressions ExpressionsTimeout and put any saved messages back into the mailbox in the order in which they arrived at the process.
posted @ 2012-07-01 17:17 Range 阅读(657) | 评论 (0)编辑 收藏

2011年10月9日


       #define _GNU_SOURCE

       #include <string.h>

       char *strcasestr(const char *haystack, const char *needle);

        用于在c串haystack中查找c串needle,忽略大小写。如果找到则返回needle串在haystack串中第一次出现的位置的char指针

        在实际的应用中如果只加上头文件,当编译时会出现  warning: assignment makes pointer from integer without a cast

       这是因为函数的声明在调用之后。未经声明的函数默认返回int型。
 
       因此要在#include所有头文件之前加  
#define _GNU_SOURCE  ,以此解决此问题。
posted @ 2011-10-09 19:12 Range 阅读(20633) | 评论 (0)编辑 收藏

2011年8月26日

Linux IO系统的架构图


 

一.设备-------- 影响磁盘性能的因素

硬盘的转速影响硬盘的整体性能。一般情况下转速越大,性能会越好。

硬盘的性能因素主要包括两个:1.平均访问时间2传输速率。

 

平均访问时间包括两方面因素:

平均寻道时间(Average Seek Time)是指硬盘的磁头移动到盘面指定磁道所需的时间。一般在3ms至15ms之间。

平均旋转等待时间(Latency)是指磁头已处于要访问的磁道,等待所要访问的扇区旋转至磁头下方的时间。一般在2ms至6ms之间。

 

传输速率(Data Transfer Rate) 硬盘的数据传输率是指硬盘读写数据的速度,单位为兆字节每秒(MB/s)。磁盘每秒能传输80M~320M字节。

 

传输速率包括内部传输速率和外部传输速率。

内部传输率(Internal Transfer Rate) 也称为持续传输率(Sustained Transfer Rate),它反映了硬盘缓冲区未用时的性能。内部传输率主要依赖于硬盘的旋转速度。

外部传输率(External Transfer Rate)也称为突发数据传输率Burst Data Transfer Rate)或接口传输率,它标称的是系统总线与硬盘缓冲区之间的数据传输率,外部数据传输率与硬盘接口类型硬盘缓存的大小有关。STAT2 的传输速率在300MB/s级别。

 

因此在硬件级上,提高磁盘性能的关键主要是降低平均访问时间。

二.设备驱动

内存到硬盘的传输方式:poll,中断,DMA

 

DMA:当 CPU 初始化这个传输动作,传输动作本身是由 DMA 控制器 来实行和完成。

DMA控制器获得总线控制权后,CPU即刻挂起或只执行内部操作,由DMA控制器输出读写命令,直接控制RAM与I/O接口进行DMA传输。DMA每次传送的是磁盘上相邻的扇区。Scatter-gather DMA允许传送不相邻的扇区。

 

CPU性能与硬盘与内存的数据传输速率关系不大。

 

设备驱动内有一个结构管理着IO的请求队列

structrequest_queue(include/linux/Blkdev.h

这里不仅仅有读写请求的数据块,还有用于IO调度的回调函数结构。每次需要传输的时候,就从队列中选出一个数据块交给DMA进行传输。

 

所以IO调度的回调函数这是降低平均访问的时间的关键。

 

三.OS

IO调度器

Linux kernel提供了四个调度器供用户选择。他们是noop,cfq,deadline,as。可以在系统启动时设置内核参数elevator=<name>来指定默认的调度器。也可以在运行时为某个块设备设置IO调度程序。

 

下面来简要介绍这四个调度器的电梯调度算法。

Noop:最简单的调度算法。新的请求总是被添加到队头或者队尾,然后总是从队头中选出将要被处理的请求。

 

CFQ:(Complete FarinessQueueing)它的目标是在所有请求的进程中平均分配IO的带宽。因此,它会根据进程创建自己的请求队列,然后将IO请求放入相应的队列中。在使用轮转法从每个非空的队列中取出IO请求。

 

Deadline:使用了四个队列,两个以磁盘块序号排序的读写队列,两个以最后期限时间排序的读写队列。算法首先确定下一个读写的方向,读的优先级高于写。然后检查被选方向的最后期限队列:如果最后期限时间的队列中有超时的请求,则将刚才的请求移动至队尾,然后在磁盘号排序队列中从超时请求开始处理。当处理完一个方向的请求后,在处理另一个方向的请求。(读请求的超时时间是500ms,写请求的超时时间是5s

 

Anticipatory:它是最复杂的IO调度算法。和deadline算法一样有四个队列。还附带了一些启发式策略。它会从当前的磁头位置后的磁盘号中选择请求。在调度了一个由P进程的IO请求后,会检查下一个请求,如果还是P进程的请求,则立即调度,如果不是,同时预测P进程很快会发出请求,则还延长大约7ms的时间等待P进程的IO请求。

 

 

 

Write/Read函数

以ext3的write为例:

系统调用write()的作用就是修改页高速缓存内的一些页的内容,如果页高速缓存内没有所要的页则分配并追加这些页。

当脏页达到一定数量或者超时后,将脏页刷回硬盘。也可以执行相关系统调用。

 

为什么要达到一定数量,是因为延迟写能在一定层度上提高系统的性能,这也使得块设备的平均读请求会多于写请求。

 

在程序中调用write函数,将进入系统调用f_op->write。这个函数将调用ext3的do_sync_write。这个函数将参数封装后调用generic_file_aio_write。由参数名可以看出同步写变成了异步写。如果没有标记O_DIRECT,将调用函数generic_file_buffered_write将写的内容写进kernel的高速页缓存中。Buffer是以page为单位即4k。之后当调用cond_resched()进行进程的调度,DMA会将buffer中的内容写进硬盘。

所以当每次以4k为单位写入硬盘时效率会达到最高。下面是UNIX环境高级编程的实验结果:


下图是linux 的块设备的数据操作层次:

Sector扇区:是设备驱动和IO调度程序处理数据粒度。

Block块:是VFS和文件系统处理数据的粒度。其大小不唯一,可以是512,1024,2048,4096字节。内核操作的块大小是4096字节。

Segment段:是DMA传送的单位。每一个段包含了相邻的扇区,它能使DMA传送不相邻的扇区。


 

四.用户程序

根据以上的分析,我们的write buffer一般设置为4K的倍数。

 

在程序中有意识的延迟写。这个是os的策略,当然也可以应用到程序的设计中。当然也会有缺点:1.如果硬件错误或掉电,则会丢失内容(做额外的备份)2.需要额外的内存空间。(牺牲内存来提高IO的效率)

 

我们还需根据系统的IO调度器的调度策略,设计出不同的IO策略。尽量降低磁盘的平均访问时间,降低请求队列,提高数据传输的速率。

 

 

 

五.监控硬盘的工具和指标

Iostat–x –k 1

 -x显示更多的消息 -k数据以KB为单位 1每秒显示一次

输出显示的信息

Iowait:cpu等待未完成的IO请求而空闲的时间的比例。

Idle:cpu空闲且无IO请求的比例。

rrqm/s:每秒这个设备相关的读取请求有多少被Merge了。

wrqm/s:每秒这个设备相关的写入请求有多少被Merge了。

rsec/s:每秒读取的扇区数;

wsec/:每秒写入的扇区数。

r/s:每秒完成的读 I/O 设备次数。即 delta(rio)/s

w/s:每秒完成的写 I/O 设备次数。即 delta(wio)/s

await:每一个IO请求的处理的平均时间(单位是毫秒)。包括加入请求队列和服务的时间。

svctm:   平均每次设备I/O操作的服务时间。

avgrq-sz: 平均每次设备I/O操作的数据大小 (扇区)。即 delta(rsect+wsect)/delta(rio+wio)
avgqu-sz:
平均I/O队列长度。即 delta(aveq)/s/1000 (因为aveq的单位为毫秒)

%util:在统计时间内所有处理IO时间,除以总共统计时间。例如,如果统计间隔1秒,该设备有0.8秒在处理IO,而0.2秒闲置,那么该设备的%util = 0.8/1 = 80%,所以该参数暗示了设备的繁忙程度。一般地,如果该参数是100%表示设备已经接近满负荷运行了(当然如果是多磁盘,即使%util是100%,因为磁盘的并发能力,所以磁盘使用未必就到了瓶颈)。

 

下面我们做一个实验来分析一下

我们使用命令

time dd if=/dev/zero of=/home/zhouyuan/mytest bs=1M count=3000

向mytest写入数据,写入3G

截取部分的状态监控:




 

如图2,当两条数据 iowait 达到了 99% 以上,写入的数据是0,这是因为DMA将内存的中的数据传输给设备。结合图1的前两条数据,利用率达到了99%+却没有写入的磁盘块。

如图3,iowait下降,说明cpu开始执行相关程序,而此时块设备开始写入的数据。这两个操作是异步进行的。

 

Vmstat–k –n 1

Swap

  si: 从磁盘交换到内存的交换页数量,单位:KB/

  so: 从内存交换到磁盘的交换页数量,单位:KB/

IO

  bi: 从块设备接受的块数,单位:块/

  bo: 发送到块设备的块数,单位:块/



从图中我们可以看出系统的延迟写。

posted @ 2011-08-26 20:04 Range 阅读(17072) | 评论 (2)编辑 收藏

2011年7月15日

      最近调试网络的服务端程序,自己写了一个小客户端程序来测试,发现服务程序解包错误。经调试发现客户端的协议头大小和服务器端的协议头大小不一致。原因是服务器端加了#pragma pack(1),而客户端没加。

    之前没接触过这个编译宏,现在来认真学习之。

    首先google~~

    原来#pragma pack有几种形式,我所接触到的是#pragma pack(n),即变量以n字节对齐。

    变量对齐在每个系统中是不一样的,默认的对齐方式能有效的提高cpu取指取数的速度,但是可能会浪费一定的空间。在网络程序中采用#pragma pack(1),即变量紧缩,不但可以减少网络流量,还可以兼容各种系统,不会因为系统对齐方式不同而导致解包错误。

    了解了概念和优点,现在我们就来测试之~

 

    平台:CPU—Pentium E5700 内存—2G

     1.操作系统:ubuntu 11.04 32bit   编译器:G++ 4.5.2

     2.操作系统:windows xp              编译器:VS2010

 

    先看第一个测试。

    结构体在正常情况和紧缩情况在以上不同环境下占用的内存大小。

1 struct pack {
2   int i;
3   short s;
4   double d;
5   char c;
6   short f;
7 }

  
   测试结果为:

    1
   

 

    2
    
  
    测试结果分析:

 

 

    可以看出紧缩后结构体的大小为15,是结构体内置类型大小的和。但是在默认情况下,结构体的大小都是对齐字节数的倍数。ubuntupack只需要20个字节,而windows24个字节。这是因为ubuntu是以4字节对齐,而windows则是以最大的内置类型的字节数对齐,在结构体内最大的内置类型为double,其大小为8个字节。他们在内存中的对齐方式如下图:

    1

    

    2

    

   还需注意的是,在对齐类型的内部都是以2字节对齐的。

   结论:在默认情况下,linux操作系统是以4字节对齐,windows操作系统则是以最大的内置类型对齐。

 

   第二个测试

   一个结构体内包含另外一个结构体,其大小的情况。

   内部的结构体为

1 struct pack {
2   short s;
3   double d;
4 }

   外部的结构体为

   1 struct complex _pack{

2   char c;
3   struct pack s;
4   double d;
5 };

    我们有四种情况:

     1.      pack紧缩,complex _pack紧缩

     2.      pack紧缩,complex _pack默认

     3.      pack默认,complex _pack紧缩

     4.      pack默认,complex _pack默认

    以下的排列均按此顺序。

     测试的结果

      1

      

     2

      

    测试结果分析:

    在两个操作系统下,除了第一种情况----内结构体和外结构体都紧缩----相同之外,其他三种情况都不相同。我们可以根据偏移画出结构体在内存中的情况。第一种情况省略。

     1

     

     2

     

    结论:#pragma pack只影响当前结构体的变量的对齐情况,并不会影响结构体内部的结构体变量的排列情况。或者说#pragma pack的作用域只是一层。我们由第三种情况,内部结构体正常,外部结构体紧缩,可以得出结构体的对齐是按偏移计算的。

    这里还有一个问题没解决,为什么第二种情况内部结构体的偏移都是1?不是4或者8?

 

posted @ 2011-07-15 20:36 Range 阅读(6044) | 评论 (6)编辑 收藏

      The IP Network Address TranslatorIP网络地址转换是人们说的NAT,或者说NA(P)TNAT是为了解决IPv4地址不足而提出来得一种替代方案,可以对外界屏蔽内部的网络拓扑。随着网络的发展,NAT阻碍了构建在覆盖网络的P2P程序的发展。因为覆盖网络是构建在应用层,屏蔽了传输层以下的网络拓扑,网络中的每一个节点或某些节点有此网络的路由表,由这些路由表构建出这个覆盖网络,但是NAT阻碍的覆盖网络中节点的连接。
    
    
   
    
上图显示了NAT的原理。NAT将内网的IP替换为公网IP,将端口映射为公网的端口。公网IP替换内网IP是固定的,NAT的不足之处在于端口的替换。因为NAT还没有形成标准,替换策略有几种,这也是NAT行为的关键。

     在《Behavior and Classification of NAT Devices and Implications for NAT Traversal》一文中就把端口映射的行为分成四种,其中包括保留端口,不保留端口,端口重载,端口复用。这四种分类最终区分了NAT的四种类型即Full cone NATSymmetric NATPort-Restrictes cone NAT Address-Restriced cone NAT

     为了使覆盖网络中的节点相互通信,我们需要进行NAT穿越。在《A NAT Traversal Mechanism for Peer-To-Peer Networks》一文种介绍了根据两端不同的NAT类型对应的四种NAT穿越方案。如下图
     
     

    这些解决方案都需要STUNSimple Traversal of User Datagram Protocol through Network Address Translators (NATs)NATUDP简单穿越)协议帮助。STUN协议要求一台具有公网IP的主机帮助一台主机进行NAT类型的判断。
    

      上图是STUN协议的流程,其主要的思想是通过STUN的回射来判断主机的NAT类型。

     除了直接连接,反向连接、打洞和依赖都需要第三台主机的帮助。

     在《Characterization and Measurement of TCP Traversal through NATs and Firewalls》一文中介绍了TCP穿越的方法。在STUNT#2方法中,第三台主机和两台需要连接的主机都有长连接,当一方需要发起来连接时,向第三台主机发请求,第三台主机向被请求的主机发送邀请,此时需要连接的主机都向对方发送SYN包,此时双方的防火墙都有了洞,只要有一方的SYN包到达对方主机,连接就会被建立。Relay方法需要耗费的代价太大,在P2P应用中一般会消极的处理双方都是对称NAT的情况。






 

posted @ 2011-07-15 20:35 Range 阅读(1793) | 评论 (2)编辑 收藏
     Hello World!
posted @ 2011-07-15 20:31 Range 阅读(382) | 评论 (0)编辑 收藏
仅列出标题  

统计