posts - 200, comments - 8, trackbacks - 0, articles - 0

The libevent API provides a mechanism to execute a callback function when a
specific event occurs on a file descriptor or after a timeout has been
reached. Furthermore, libevent also support callbacks due to signals or
regular timeouts.

libevent is meant to replace the event loop found in event driven network
servers. An application just needs to call event_dispatch() and then add or
remove events dynamically without having to change the event loop.


官网
http://libevent.org/

Fast portable non-blocking network programming with Libevent
http://www.wangafu.net/~nickm/libevent-book/


epoll学习笔记
http://www.cppblog.com/converse/archive/2008/04/29/48482.aspx

libevent事件处理框架分析
http://www.cppblog.com/converse/archive/2009/01/03/71040.aspx

libevent入门教程:Echo Server based on libevent
http://www.felix021.com/blog/read.php?2068

libevent源码深度剖析
http://blog.csdn.net/sparkliang/article/category/660506

libevent源码分析
How to use epoll? A complete example in C

https://banu.com/blog/2/how-to-use-epoll-a-complete-example-in-c/

posted @ 2013-01-17 17:34 鑫龙 阅读(4928) | 评论 (0)编辑 收藏

     摘要: 一.多线程  1.了解多线程    解决多任务实现。    历史上Unix服务器不支持多线程    Unix/Linux上实现多线程有两种方式:     内核支持多线程   &nb...  阅读全文

posted @ 2013-01-17 14:30 鑫龙 阅读(635) | 评论 (0)编辑 收藏

Reactor这个词译成汉语还真没有什么合适的,很多地方叫反应器模式,但更多好像就直接叫reactor模式了,其实我觉着叫应答者模式更好理解一些。通过了解,这个模式更像一个侍卫,一直在等待你的召唤,或者叫召唤兽。

 

并发系统常使用reactor模式,代替常用的多线程的处理方式,节省系统的资源,提高系统的吞吐量。

 

先用比较直观的方式来介绍一下这种方式的优点,通过和常用的多线程方式比较一下,可能更好理解。

以一个餐饮为例,每一个人来就餐就是一个事件,他会先看一下菜单,然后点餐。就像一个网站会有很多的请求,要求服务器做一些事情。处理这些就餐事件的就需要我们的服务人员了。

 

在多线程处理的方式会是这样的:

一个人来就餐,一个服务员去服务,然后客人会看菜单,点菜。 服务员将菜单给后厨。

二个人来就餐,二个服务员去服务……

五个人来就餐,五个服务员去服务……

 

这个就是多线程的处理方式,一个事件到来,就会有一个线程服务。很显然这种方式在人少的情况下会有很好的用户体验,每个客人都感觉自己是VIP,专人服务的。如果餐厅一直这样同一时间最多来5个客人,这家餐厅是可以很好的服务下去的。

 

来了一个好消息,因为这家店的服务好,吃饭的人多了起来。同一时间会来10个客人,老板很开心,但是只有5个服务员,这样就不能一对一服务了,有些客人就要没有人管了。老板就又请了5个服务员,现在好了,又能每个人都受VIP待遇了。

 

越来越多的人对这家餐厅满意,客源又多了,同时来吃饭的人到了20人,老板高兴不起来了,再请服务员吧,占地方不说,还要开工钱,再请人就攒不到钱了。怎么办呢?老板想了想,10个服务员对付20个客人也是能对付过来的,服务员勤快点就好了,伺候完一个客人马上伺候另外一个,还是来得及的。综合考虑了一下,老板决定就使用10个服务人员的线程池啦~~~

 

但是这样有一个比较严重的缺点就是,如果正在接受服务员服务的客人点菜很慢,其他的客人可能就要等好长时间了。有些火爆脾气的客人可能就等不了走人了。

 

Reactor如何处理这个问题呢:

老板后来发现,客人点菜比较慢,大部服务员都在等着客人点菜,其实干的活不是太多。老板能当老板当然有点不一样的地方,终于发现了一个新的方法,那就是:当客人点菜的时候,服务员就可以去招呼其他客人了,等客人点好了菜,直接招呼一声“服务员”,马上就有个服务员过去服务。嘿嘿,然后在老板有了这个新的方法之后,就进行了一次裁员,只留了一个服务员!这就是用单个线程来做多线程的事。

 

实际的餐馆都是用的Reactor模式在服务。一些设计的模型其实都是从生活中来的。

 

Reactor模式主要是提高系统的吞吐量,在有限的资源下处理更多的事情。

 

在单核的机上,多线程并不能提高系统的性能,除非在有一些阻塞的情况发生。否则线程切换的开销会使处理的速度变慢。就像你一个人做两件事情,1、削一个苹果。2、切一个西瓜。那你可以一件一件的做,我想你也会一件一件的做。如果这个时候你使用多线程,一会儿削苹果,一会切西瓜,可以相像究竟是哪个速度快。这也就是说为什么在单核机上多线程来处理可能会更慢。

 

但当有阻碍操作发生时,多线程的优势才会显示出来,现在你有另外两件事情去做,1、削一个苹果。2、烧一壶开水。我想没有人会去做完一件再做另一件,你肯定会一边烧水,一边就把苹果削了。

 


转自:http://daimojingdeyu.iteye.com/blog/828696

posted @ 2013-01-17 13:38 鑫龙 阅读(470) | 评论 (0)编辑 收藏

     摘要: 转自:http://my.oschina.net/tuzibuluo/blog?catalog=1278261.Writable接口         Hadoop 并没有使用 JAVA 的序列化,而是引入了自己实的序列化系统, package org.apache.hadoop.io 这个...  阅读全文

posted @ 2013-01-15 21:48 鑫龙 阅读(1092) | 评论 (0)编辑 收藏

     转载请注明 出自:http://www.cppblog.com/mysileng/archive/2013/01/15/197284.html

     今天在看UNP6.5节,学习到了select与stdio混用的后果。特此进程实验一番。再实验之前需明确一下几点:
1.stdio流的i/o函数 与 系统i/o函数不同。stdio流函数在用户空间和内核都有缓冲,系统i/o函数只在内核有缓冲,用户空间没有。

2.stdio流的i/o函数缓冲机制:在面对文件时候用的是全缓冲,面对设备的时候用的行缓冲。(等下试验用的是键盘和屏幕),所以实验用的stdio函数采用行行缓冲。

3.select函数对于某一个描述符是否准备好可读可写,是对内核缓冲区中的数据是否达到某一个最低标准,而不是用户缓冲区。也就是说select函数不知道用户缓冲区的存在。

首先写了一个系统i/o函数 简单的select函数程序:

     程序给select函数只设置的键盘的描述符。也就是说如果键盘的描述符准备好了就不再阻塞。但是这里有一个问题,解除阻塞后,我们最多只从内核缓冲区读3个字节,这个时候就会有两个情况:
(1)内核空间本来存储的数据就小于等于3个字节,全被读走。那下次再次调用select函数,应该肯定会阻塞的,因为键盘输入的内核缓冲区已经没有数据了。
情况如下(内核空间只有3个字节:1 2 \n):


(2)如果内核空间的数据多余3个字节,但是因为最多只能读3个字节,就必将导致内核中有数据读不完。那么下次再遇到select函数的时候是否会阻塞呢?
情况见下:

     当我们输入5个字符时候(1 2 3 4 \n),第一次read掉3个字符,内核空间还剩下2个字符,然后再次碰到select函数,默认情况下如果键盘内核空间字符数大于1,select是不会阻塞键盘描述符的。结果也印证了,又read了2个字节,并没有堵塞。
      综上所述select是可以看见内核空间的缓冲区的。那到底能不能看见用户空间缓冲区呢?我们换成stdio流的i/o函数继续实验。

--------------------------------------------------------------------
stdio流的i/o函数使用select函数的程序如下:


程序用stdio流的getc函数从键盘读数据,运行结果如下:

   我们输入5个字符(1 2 3 4 \n),结果只输出了1个字符,然后就阻塞了。我们分析一下,首先输入5个字符,这5个字符被放入用户缓冲区,因为最后一个是换行符并且stdio面对设备使用行缓冲机制,所以这5个字符马上接着被从用户缓冲区刷入内核缓冲区。然后调用select函数,select函数发现内核空间中有数据,于是不阻塞返回。接着getc函数从用户空间输出缓冲区取一个字符,因为用户空间输出缓冲区没有数据,于是把内核空间的数据调入一行给用户空间输出缓冲区,然后getc返回。接着又碰上select函数,因为内核缓冲空间的数据已经被放入用户空间输出缓冲区了,所以内核缓冲没有数据,那么select认为键盘没有准备好,所以阻塞。虽然阻塞了,但需要注意的时候这个时候,用户空间是有4个字符数据的,被select函数无视了。

     接下来假设我们再输入2个字符(1 \n),将会发生什么呢?

    输出一对东西,这是怎么回事,我们继续分析。当输入2个字符(1 \n)的时候,内核空间缓冲没有数据,用户空间输出缓冲有4个字符。2个字符根据上一段同样原理,被刷入内核空间缓冲区。select函数被调用,发现有2个字符,于是不阻塞返回。getc函数从用户输出缓冲取出一个字符,打印stardard... --2然后返回。再次循环,调用select函数。关键来了,这里跟上次不一样了。这个时候内核空间的缓冲中还有上次遗留的2个字符,所以依然不阻塞返回,调用getc函数继续打印。。。这里的关键是,getc函数。getc函数在用户输出缓冲中有数据的时候,不会把内核空间缓冲中的数据移入用户空间的输出缓冲,使得内核空间缓冲一直留有数据。这将会持续到用户空间输出缓冲的数据被取完为止。所以上述奇怪的打印结果就可以解释的了。
综上所述,select函数确实是看不见用户空间缓冲的寻在的。


所以如果在使用select函数的时候,要谨慎使用stdio流函数。

posted @ 2013-01-15 13:09 鑫龙 阅读(935) | 评论 (1)编辑 收藏

功能描述:根据文件描述词来操作文件的特性。

#include <unistd.h>
#include <fcntl.h> 
int fcntl(int fd, int cmd); 
int fcntl(int fd, int cmd, long arg); 
int fcntl(int fd, int cmd, struct flock *lock);

[描述]
fcntl()针对(文件)描述符提供控制。参数fd是被参数cmd操作(如下面的描述)的描述符。针对cmd的值,fcntl能够接受第三个参数int arg。

[返回值]
fcntl()的返回值与命令有关。如果出错,所有命令都返回-1,如果成功则返回某个其他值。下列三个命令有特定返回值:F_DUPFD , F_GETFD , F_GETFL以及F_GETOWN。
    F_DUPFD   返回新的文件描述符
    F_GETFD   返回相应标志
    F_GETFL , F_GETOWN   返回一个正的进程ID或负的进程组ID

 

fcntl函数有5种功能: 
1. 复制一个现有的描述符(cmd=F_DUPFD). 
2. 获得/设置文件描述符标记(cmd=F_GETFD或F_SETFD). 
3. 获得/设置文件状态标记(cmd=F_GETFL或F_SETFL). 
4. 获得/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN). 
5. 获得/设置记录锁(cmd=F_GETLK , F_SETLK或F_SETLKW).

1. cmd值的F_DUPFD : 
F_DUPFD    返回一个如下描述的(文件)描述符:
        ·最小的大于或等于arg的一个可用的描述符
        ·与原始操作符一样的某对象的引用
        ·如果对象是文件(file)的话,则返回一个新的描述符,这个描述符与arg共享相同的偏移量(offset)
        ·相同的访问模式(读,写或读/写)
        ·相同的文件状态标志(如:两个文件描述符共享相同的状态标志)
        ·与新的文件描述符结合在一起的close-on-exec标志被设置成交叉式访问execve(2)的系统调用

实际上调用dup(oldfd);
等效于
        fcntl(oldfd, F_DUPFD, 0);

而调用dup2(oldfd, newfd);
等效于
        close(oldfd);
        fcntl(oldfd, F_DUPFD, newfd);

2. cmd值的F_GETFD和F_SETFD:      
F_GETFD    取得与文件描述符fd联合的close-on-exec标志,类似FD_CLOEXEC。如果返回值和FD_CLOEXEC进行与运算结果是0的话,文件保持交叉式访问exec(),否则如果通过exec运行的话,文件将被关闭(arg 被忽略)        
F_SETFD    设置close-on-exec标志,该标志以参数arg的FD_CLOEXEC位决定,应当了解很多现存的涉及文件描述符标志的程序并不使用常数 FD_CLOEXEC,而是将此标志设置为0(系统默认,在exec时不关闭)或1(在exec时关闭)    

在修改文件描述符标志或文件状态标志时必须谨慎,先要取得现在的标志值,然后按照希望修改它,最后设置新标志值。不能只是执行F_SETFD或F_SETFL命令,这样会关闭以前设置的标志位。 

3. cmd值的F_GETFL和F_SETFL:   
F_GETFL    取得fd的文件状态标志,如同下面的描述一样(arg被忽略),在说明open函数时,已说明
了文件状态标志。不幸的是,三个存取方式标志 (O_RDONLY , O_WRONLY , 以及O_RDWR)并不各占1位。(这三种标志的值各是0 , 1和2,由于历史原因,这三种值互斥 — 一个文件只能有这三种值之一。) 因此首先必须用屏蔽字O_ACCMODE相与取得存取方式位,然后将结果与这三种值相比较。       
F_SETFL    设置给arg描述符状态标志,可以更改的几个标志是:O_APPEND,O_NONBLOCK,O_SYNC 和 O_ASYNC。而fcntl的文件状态标志总共有7个:O_RDONLY , O_WRONLY , O_RDWR , O_APPEND , O_NONBLOCK , O_SYNC和O_ASYNC

可更改的几个标志如下面的描述:
    O_NONBLOCK   非阻塞I/O,如果read(2)调用没有可读取的数据,或者如果write(2)操作将阻塞,则read或write调用将返回-1和EAGAIN错误
    O_APPEND     强制每次写(write)操作都添加在文件大的末尾,相当于open(2)的O_APPEND标志
    O_DIRECT     最小化或去掉reading和writing的缓存影响。系统将企图避免缓存你的读或写的数据。如果不能够避免缓存,那么它将最小化已经被缓存了的数据造成的影响。如果这个标志用的不够好,将大大的降低性能
    O_ASYNC      当I/O可用的时候,允许SIGIO信号发送到进程组,例如:当有数据可以读的时候

转载自:http://blog.163.com/xychenbaihu@yeah/blog/static/132229655201010265577965/
1、获取文件的flags,即open函数的第二个参数:
       flags = fcntl(fd,F_GETFL,0);

2、设置文件的flags:

      fcntl(fd,F_SETFL,flags);

3、增加文件的某个flags,比如文件是阻塞的,想设置成 非阻塞:

       flags = fcntl(fd,F_GETFL,0);

       flags |= O_NONBLOCK;

      fcntl(fd,F_SETFL,flags);

4、取消文件的某个flags,比如文件是非阻塞的,想设置 成为阻塞:

      flags = fcntl(fd,F_GETFL,0);

      flags &= ~O_NONBLOCK;

      fcntl(fd,F_SETFL,flags);

 

获取和设置文件flags举例::

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <error.h>

char buf[500000];

int main(int argc,char *argv[])
{
        int ntowrite,nwrite;
        const char *ptr ;
        int flags;
    
        ntowrite = read(STDIN_FILENO,buf,sizeof(buf));
        if(ntowrite <0) 
        {   
                perror("read STDIN_FILENO fail:");
                exit(1);
        }   
        fprintf(stderr,"read %d bytes\n",ntowrite);
    
        if((flags = fcntl(STDOUT_FILENO,F_GETFL,0))==-1)
        {   
                perror("fcntl F_GETFL fail:");
                exit(1);
        }   
        flags |= O_NONBLOCK;
        if(fcntl(STDOUT_FILENO,F_SETFL,flags)==-1)
        {   
                perror("fcntl F_SETFL fail:");
                exit(1);
        }   
    
        ptr = buf;
        while(ntowrite > 0)
        {   
                nwrite = write(STDOUT_FILENO,ptr,ntowrite);
                if(nwrite == -1) 
                {  

                        perror("write file fail:");
                }   
                if(nwrite > 0)
                {   
                        ptr += nwrite;
                        ntowrite -= nwrite;
                }   
        }   
    
        flags &= ~O_NONBLOCK;
        if(fcntl(STDOUT_FILENO,F_SETFL,flags)==-1)
        {   
                perror("fcntl F_SETFL fail2:");
        }   
        return 0;
}

 

4. cmd值的F_GETOWN和F_SETOWN:   
F_GETOWN   取得当前正在接收SIGIO或者SIGURG信号的进程id或进程组id,进程组id返回的是负值(arg被忽略)     
F_SETOWN   设置将接收SIGIO和SIGURG信号的进程id或进程组id,进程组id通过提供负值的arg来说明(arg绝对值的一个进程组ID),否则arg将被认为是进程id

 5. cmd值的F_GETLK, F_SETLK或F_SETLKW: 获得/设置记录锁的功能,成功则返回0,若有错误则返回-1,错误原因存于errno。
F_GETLK    通过第三个参数arg(一个指向flock的结构体)取得第一个阻塞lock description指向的锁。取得的信息将覆盖传到fcntl()的flock结构的信息。如果没有发现能够阻止本次锁(flock)生成的锁,这个结构将不被改变,除非锁的类型被设置成F_UNLCK    
F_SETLK    按照指向结构体flock的指针的第三个参数arg所描述的锁的信息设置或者清除一个文件的segment锁。F_SETLK被用来实现共享(或读)锁(F_RDLCK)或独占(写)锁(F_WRLCK),同样可以去掉这两种锁(F_UNLCK)。如果共享锁或独占锁不能被设置,fcntl()将立即返回EAGAIN     
F_SETLKW   除了共享锁或独占锁被其他的锁阻塞这种情况外,这个命令和F_SETLK是一样的。如果共享锁或独占锁被其他的锁阻塞,进程将等待直到这个请求能够完成。当fcntl()正在等待文件的某个区域的时候捕捉到一个信号,如果这个信号没有被指定SA_RESTART, fcntl将被中断

当一个共享锁被set到一个文件的某段的时候,其他的进程可以set共享锁到这个段或这个段的一部分。共享锁阻止任何其他进程set独占锁到这段保护区域的任何部分。如果文件描述符没有以读的访问方式打开的话,共享锁的设置请求会失败。

独占锁阻止任何其他的进程在这段保护区域任何位置设置共享锁或独占锁。如果文件描述符不是以写的访问方式打开的话,独占锁的请求会失败。

结构体flock的指针:
struct flcok 

short int l_type; /* 锁定的状态*/

//以下的三个参数用于分段对文件加锁,若对整个文件加锁,则:l_whence=SEEK_SET, l_start=0, l_len=0
short int l_whence; /*决定l_start位置*/ 
off_t l_start; /*锁定区域的开头位置*/ 
off_t l_len; /*锁定区域的大小*/

pid_t l_pid; /*锁定动作的进程*/ 
};

l_type 有三种状态: 
F_RDLCK   建立一个供读取用的锁定 
F_WRLCK   建立一个供写入用的锁定 
F_UNLCK   删除之前建立的锁定

l_whence 也有三种方式: 
SEEK_SET   以文件开头为锁定的起始位置 
SEEK_CUR   以目前文件读写位置为锁定的起始位置 
SEEK_END   以文件结尾为锁定的起始位置


fcntl文件锁有两种类型:建议性锁和强制性锁
建议性锁是这样规定的:每个使用上锁文件的进程都要检查是否有锁存在,当然还得尊重已有的锁。内核和系统总体上都坚持不使用建议性锁,它们依靠程序员遵守这个规定。
强制性锁是由内核执行的:当文件被上锁来进行写入操作时,在锁定该文件的进程释放该锁之前,内核会阻止任何对该文件的读或写访问,每次读或写访问都得检查锁是否存在。

系统默认fcntl都是建议性锁,强制性锁是非POSIX标准的。如果要使用强制性锁,要使整个系统可以使用强制性锁,那么得需要重新挂载文件系统,mount使用参数 -0 mand 打开强制性锁,或者关闭已加锁文件的组执行权限并且打开该文件的set-GID权限位。
建议性锁只在cooperating processes之间才有用。对cooperating process的理解是最重要的,它指的是会影响其它进程的进程或被别的进程所影响的进程,举两个例子:
(1) 我们可以同时在两个窗口中运行同一个命令,对同一个文件进行操作,那么这两个进程就是cooperating  processes
(2) cat file | sort,那么cat和sort产生的进程就是使用了pipe的cooperating processes

使用fcntl文件锁进行I/O操作必须小心:进程在开始任何I/O操作前如何去处理锁,在对文件解锁前如何完成所有的操作,是必须考虑的。如果在设置锁之前打开文件,或者读取该锁之后关闭文件,另一个进程就可能在上锁/解锁操作和打开/关闭操作之间的几分之一秒内访问该文件。当一个进程对文件加锁后,无论它是否释放所加的锁,只要文件关闭,内核都会自动释放加在文件上的建议性锁(这也是建议性锁和强制性锁的最大区别),所以不要想设置建议性锁来达到永久不让别的进程访问文件的目的(强制性锁才可以);强制性锁则对所有进程起作用。

fcntl使用三个参数 F_SETLK/F_SETLKW, F_UNLCK和F_GETLK 来分别要求、释放、测试record locks。record locks是对文件一部分而不是整个文件的锁,这种细致的控制使得进程更好地协作以共享文件资源。fcntl能够用于读取锁和写入锁,read lock也叫shared lock(共享锁), 因为多个cooperating process能够在文件的同一部分建立读取锁;write lock被称为exclusive lock(排斥锁),因为任何时刻只能有一个cooperating process在文件的某部分上建立写入锁。如果cooperating processes对文件进行操作,那么它们可以同时对文件加read lock,在一个cooperating process加write lock之前,必须释放别的cooperating process加在该文件的read lock和wrtie lock,也就是说,对于文件只能有一个write lock存在,read lock和wrtie lock不能共存。

posted @ 2013-01-15 10:57 鑫龙 阅读(3958) | 评论 (0)编辑 收藏

     摘要: 回顾:  多进程的问题:数据共享。  多进程的问题: 进程的上下文环境(context)          文件描述符号是整数以及对应上下文环境  多进程的问题:上下文环境共享一.SELECT TCP服务器编程模式 1....  阅读全文

posted @ 2013-01-13 11:04 鑫龙 阅读(370) | 评论 (0)编辑 收藏

         转载请注明 出自:http://www.cppblog.com/mysileng/archive/2013/01/11/197202.html

     讨论两个由sigchld信号引起的血案问题,讨论的环境是服务端的并发程序。我们先把最原始的服务器端并发程序模型贴出来:

     以上是服务器端程序,我们先不看被注视掉的部分,程序对于每一个accept的TCP连接会产生一个子进程,交给子进程去处理。而子进程其实并不做什么,直接睡眠3秒就结束。可以想象这样当子进程exit以后,会给父进程发sigchld信号,通知父进程自己挂了。但是在我们的父进程中,我们对于sigchld信号采用默认处置(忽略)。结果可想而知就是来一个连接,就产生一个僵死进程。我们运行程序3次,看看是否会得到3个僵死进程。
服务器端运行程序,被某客户端连接3次:

客户端运行程序执行3次,并查看进程情况:

    首先声明,我们用客户端可以查看服务器端进程的原因是,我们把客户端和服务端放在了一台电脑上进程本次试验。我们并不关心cli客户端的具体实现,因为服务器并不从客户端获取任何信息。
    从结果可知,果然服务端果断产生了3个僵死进程。接下来我们加上对sigchld的处理程序。但加上以后也将产生我们的第一个血案:

白色为客户端,黑为服务端:

    可见,僵死进程的问题已经解决,但还有个潜在的隐藏危机。
    PHOSIX对于向accept这种慢速的系统系统调用有一个基本规则(apue,unp都有涉及):当进程阻塞于某个慢系统调用的时候(我们的程序是accept),当进程捕捉到某个信号(我们的程序是sigchld),并从信号的处理函数返回时(我的程序是deal函数),进程不再阻塞与之前的慢速系统调用,而是返回一个EINTER错误。
     对于上面的这个规则,各个操作系统的对待方式是不同的。有的操作系统返回EINTER以后,就会自动重启之前的慢速系统调用而继续,有些则不会自动重启。对我们实验程序的这个操作系统环境(centos5.5),从结果来看,因为并没打印"accept error"并退出程序,我猜想,centos5.5应该是会自动重启慢速系统调用的。也就是说在这里我因为操作系统的优秀,躲过一劫(躲过第一次血案)。但为了可移植性我们应该改进程序为以下实现:

    我的改动主要集中在对accpt的错误处理里面。接下来阐述另外一个血案
----------------------------------------------------------------------------
    我们继续沿用上述的最后一次服务端程序来进行接下来的实验,现在我们编写一个客户端程序,客户端一次性跟服务端申请5个连接。客户端的程序如下:

   整个程序的构架大概如下:

    这里客户端最需要注意的是程序的最后一句并不是一个个close所有的套接字描述符,而是调用exit程序结束进程。根据APUE描述,exit系统调用会执行关闭该进程所有描述符的操作,也就是说客户端的所有描述符,包括套接字描述符也被几乎同时关闭了。也就是说服务端的由监听进程产生的所有处理子进程也会在几乎同时死掉。那么就会在几乎同时给父进程发送sigchld信号。情况如下:

    血案即将发生。请注意,根据APUE对于信号在1-31之内的的信号,因为历史原因,是不可靠信号,也就说,SIGCHLD信号在被递送到正在阻塞SIGCHLD信号的进程时,是不会排队的,而是会被系统压缩。上述问题就是当5个sigchld信号几乎同时到达父进程时,只有第一个能顺利被父进程的信号处理函数处理。又因为被signal/sigaction设置的信号处理函数会自动阻塞正在处理的信号这一原则,接下来没被处理的4个sigchld信号,被排在了父进程门口。不巧的是,sigchld又是不可靠信号,结果是4个sigchld被压缩成一个sigchld信号。这就导致信号的丢失。也因为丢失了3个sigchld信号,就会产生3个僵尸进程。你说这是不是一个名符其实的血案。接下来我们实验一下:

    可以清晰的看到结果如预期,所有出现信号丢失导致3个僵死进程。那么怎么解决这个问题呢?~。。。。

posted @ 2013-01-11 18:54 鑫龙 阅读(1408) | 评论 (1)编辑 收藏

     摘要: 一.TCP的编程模型 回顾:  UDP模型的UML图  TCP模型的UML图 案例1:  TCP的服务器(在案例中使用浏览器作为客户程序)   socket建立服务器的文件描述符号缓冲 bind把IP地址与端口设置到文件描述符号中 listen负责根据客户连接的不同IP...  阅读全文

posted @ 2013-01-10 12:49 鑫龙 阅读(398) | 评论 (0)编辑 收藏

网络IP地址本是用32位二进制来表示的,为了记忆的方便可以用点分十进制来表示IP地址,同时,网络IP地址在网络传输和计算机内部的存储方式也不同,需要用函数来进行转换。 

1.将点分十进制字符串转换成十进制长整型数:in_addr_t inet_addr(const char *cp);       in_addr_t 即long型,参数cp表示一个点分十进制字符串,返回值是十进制长整型数。 

2.将长整型IP地址转换成点分十进制:char *inet_ntoa(struct in_addr in);   参数in是一个in_addr类型的结构体,这个结构体在man 7 ip中查得到:
  struct in_addr{
     uint32_t s_addr
  };
inet_ntoa返回的是点分十进制的IP地址字符串。
 

3.主机字符顺序和网络字符顺序的转换:计算机中的字符和网络中的字符的存储顺序是不同的,计算机中的整型数和网络中的整型数进行交换时,需要相关的函数进行转换。如果将计算机中的长整型IP地址转换成网络字符顺序的整型IP地址,使用htonl函数。这些函数如下:

uint32_t htonl(uint32_t hostlong);将计算机中的32位长整型数转换成网络字符顺序的32位长整型数。(用于IP的转换)

uint16_t htons(uint16_t hostshort);将计算机中的16位整型数转换成网络字符顺序的16位整型数。。(用于port的转换)

uint32_t ntohl(uint32_t netlong);将网络中的32位常整型数转换成计算机中的32位长整型数。。(用于IP的转换)

uint16_t ntons(uint16_t netshort);将网络中的16位整型数转换成计算机中的16位整型数。。(用于port的转换)

转自:
http://www.linuxidc.com/Linux/2012-01/51068.htm

posted @ 2013-01-09 18:02 鑫龙 阅读(575) | 评论 (0)编辑 收藏

仅列出标题
共20页: First 4 5 6 7 8 9 10 11 12 Last