andylei

常用链接

统计

最新评论

2010年3月30日 #

Unix下IO模型

Unix下我们可用的五种IO模型是:
  • 阻塞I/O
  • 非阻塞I/O
  • I/O复用
  • 信号驱动I/O
  • 异步I/O(POSIX.1的aio_序列函数
  1. 阻塞I/O
    是最流行的I/O模型,以recvfrom为例,此系统调用直到数据到达且拷贝到应用缓冲区或出错才返回.进程阻塞于该系统调用,然后1).等待数据到达内核缓冲区,2)将数据从内核缓冲区中拷贝到用户空间中应用程序缓冲区. 拷贝完成后,返回成功指示.

  2. 非阻塞I/O
    进程不阻塞于I/O函数(recvfrom),不会等待数据, 如果数据没有到达,就立即从recvfrom返回到应用进程,然后等待一段时间,再次调用recvfrom,这个过程叫轮询(进程对一个非阻塞描述字循环调用I/O函数). 通过轮询不断查询内核,看看操作是否准备好,这对CPU时间是极大浪费.
  3. I/O复用
    通过select或poll调用,在这两个调用中的某一个阻塞,而不是阻塞于真正的系统调用. 这样的好处在于select可这等待多个描述字,阻塞IO模型在真正的系统调用recvfrom上阻塞,而recvfrom一次只能读一个描述字,对它的阻塞使得进程只能为这个描述字傻傻等待. 一般应用系统中都会有多个描述字.使用select可这很方便地对所有提供的描述字进行等待.
  4. 信号驱动I/O
    在描述字准备好后,让内核给应用进程发一个信号SIGIO,应用进程捕捉到该信号后再对描述字调用recvfrom. 首先允许套接口进行信号驱动,并通过系统调用sigaction安装一个信号处理程序,此系统调用立即返回,进程继续工作.
  5. 异步I/O(POSIX.1的aio_序列函数
    异步I/O是指Posix.1的1993版本中的新内容。异步的意思是,让内核启动操作,并在整个操作完成后(包括将数据从内核拷贝到应用进程的缓冲区中)通知我们。这种模型与其他四种模型的主要区别在于,信号驱动IO是由内核通知我们什么时候可以启动一个io操作, 而异步io模型是由内核通知我们io操作何时完成。
各种模型比较:
1.前四种模型在第一阶段(等待数据阶段)的行为不同, 在第二阶段基本相同:在将数据从内核拷贝到调用者的缓冲区时,进程都阻塞于recvfrom调用。
2.异步io模型处理的两个阶段都不同于前四种。

同步I/O 与异步I/O
同步I/O操作引起请求进程阻塞,直到IO操作完成
异步I/O操作不引起请求进程阻塞
上述前四种IO模型都属于同步IO, 因为真正的IO操作(recvfrom)阻塞进程。



posted @ 2010-03-30 16:28 逆水行舟 阅读(569) | 评论 (0)编辑 收藏

2010年3月25日 #

使用 lsof 工具查看CLOSE_WAIT进程信息

编写unix网络通信程序时,经常会遗留一些状态为CLOSE_WAIT的进程,使用netstat 命令查看,结果中没有进程相关的信息:
netstat -a|grep 9877
tcp        1      0 ylei-laptop.local:53773 ylei-linux.local:9877   CLOSE_WAIT
tcp        1      0 ylei-laptop.local:54080 ylei-laptop.local:9877  CLOSE_WAIT 

这时可这使用lsof工具,它可这显示出状态为CLOSE_WAIT的进程的程序名(command),进程id(pid),  等等。

lsof -i@ylei-laptop.local
COMMAND    PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
gvfsd-smb 2217 ylei   18u  IPv4  41440      0t0  TCP ylei-laptop.local:60953->ylei-linux.local:netbios-ssn (ESTABLISHED)
tcpcli    8055 ylei    3u  IPv4 312580      0t0  TCP ylei-laptop.local:54080->ylei-laptop.local:9877 (CLOSE_WAIT)
tcpcli    8057 ylei    3u  IPv4 312872      0t0  TCP ylei-laptop.local:53773->ylei-linux.local:9877 (CLOSE_WAIT)

这时就可这用KILL命令来杀死这些进程。

这种CLOSE_WAIT 进程遗留多了会对系统有一定的影响, 如何避免出现CLOSE_WAIT状态的通信进程?

http://blog.chinaunix.net/u/19782/showart_218982.html
这是一篇lsof使用的文章,可作参考。


posted @ 2010-03-25 20:45 逆水行舟 阅读(2001) | 评论 (0)编辑 收藏

Ubuntu9.10上pdf文件中的中文无法显示解决方案

在我的64位Ubuntu9.10上,有的pdf文档中文无法显示,是空白。解决方案:
 
1. sudo apt-get install xpdf-chinese-simplified    
2. sudo apt-get install xpdf-chinese-traditional
3. sudo apt-get install poppler-data

重新打开pdf文档,中文正常显示

posted @ 2010-03-25 17:58 逆水行舟 阅读(215) | 评论 (0)编辑 收藏

在QT中编写linux 程序 初次问题

今天在QT中编译纯linux程序时出现一个编译错误:

multiple definition of main

搜不半天,也没有合适的办法。

问题的出现原因在于项目的创建过程:
使用QT创建了一个含有main.cpp的项目,然后添加了一些*.c文件,因为同一个项目中不想同时包含两种不同类型的源文件,所以就把main.cpp改成了main.c. 但是,QT的项目文件(tcpserver.pro)中的内容多出了一个main.c来,这样,编译时,qmake会根据这个.pro文件生成Makefile, 这个Makefile中就会包含两个相同的目标main.o,连接的时候就会出现上面的错误。


QT -= gui
TARGET 
= tcpserver
CONFIG 
+= console
CONFIG 
-= app_bundle
TEMPLATE 
= app
SOURCES 
+= main.c \
    ..
/../../lib/wrapsock.c \
    main.c \
    ..
/../../lib/str_echo.c \
    ..
/../../lib/error.c \
    ..
/../../lib/wrapunix.c \
    ..
/../../lib/writen.c \
    ..
/../../lib/readline.c
HEADERS 
+= ../../../lib/unp.h
生成的Makefile如下:

####### Compile

main.o: main.c ..
/../../lib/unp.h \
        ../config.h \
        ../../../lib/addrinfo.h
    $(CC) -c $(CFLAGS) $(INCPATH) -
o main.o main.c

wrapsock.o: ..
/../../lib/wrapsock.c ../../../lib/unp.h \
        ..
/config.h \
        ..
/../../lib/addrinfo.h
    $(CC) 
-c $(CFLAGS) $(INCPATH) -o wrapsock.o ../../../lib/wrapsock.c

main.o: main.c ..
/../../lib/unp.h \
        ../config.h \
        ../../../lib/addrinfo.h
    $(CC) -c $(CFLAGS) $(INCPATH) -
o main.o 

str_echo.o: ..
/../../lib/str_echo.c ../../../lib/unp.h \
        ..
/config.h \
        ..
/../../lib/addrinfo.h
    $(CC) 
-c $(CFLAGS) $(INCPATH) -o str_echo.o ../../../lib/str_echo.c

error.o: ..
/../../lib/error.c ../../../lib/unp.h \
        ..
/config.h \
        ..
/../../lib/addrinfo.h
    $(CC) 
-c $(CFLAGS) $(INCPATH) -o error.o ../../../lib/error.c


解决办法是:
修改项目文件(tcpserver.pro) ,去掉重复的main.c.然后依次调用
clean all
qmake
make

posted @ 2010-03-25 17:07 逆水行舟 阅读(296) | 评论 (0)编辑 收藏

2010年3月23日 #

TCP状态转换

TCP状态转换看起来很复杂,实质很只有两个主要状态,
1. 从CLOSED 到 ESTABLISHED
2. 从ESTABLISHED到CLOSED

TCP应用一般涉及客户和服务器应用,根据客户和服务器端的不同,对TCP的状态进行细分:


对照Unix 网络编程卷1 第33页的图,下面分别是客户端和服务器端的TCP状态转换过程。

客户端
  客户端的TCP状态转换
  1. 客户端调用connect(),TCP发送SYN给服务器,执行主动打开,状态从CLOSED到SYN_SENT
  2. 客户处于SYN_SENT状态时,可能出现三种情况:
    • 如果发送SYN后没有收到服务器响应,出现超时,状态回到CLOSED
    • 如果接收到服务器的ACK和SYN,客户就会发送ACK作为对服务器的SYN确认,这时状态转换到ESTABLISHED,表示连接已建立
    • 如果客户发送SYN的同时接收到服务器发来的SYN,状态会变为SYN_RCVD(同时打开),这属于不正常的客户端TCP状态,在unp第33面的TCP状态转换图中,使用细实线来连接这种状态转换,表示不正常的状态转换。在图例中说明,正常的客户端TCP状态转换使用粗实线来连接,正常的服务器TCP状态转换使用粗虚线连接。
  3. 客户端经过上述第二步进入ESTABLISHED后,它发送完数据,然后客户端应用程序调用close(),它的下一个状态是关闭,即CLOSED,但是,有几种情况可以进入CLOSED状态
    • 客户端应用程序调用close后,TCP发送FIN给服务器,TCP处于FIN_WAIT_1状态,这个状态wait_1表示等待服务器对FIN的确认ACK,如果接收到服务器对FIN的确认,客户端TCP状态转换为FIN_WAIT_2,wait_2表示客户还在等待服务器的FIN.如果接收到服务器的FIN,客户就发送ACK对服务器的FIN进行确认,然后状态进入TIME_WAIT
    • 同时接收到FIN和ACK,然后发送ACK给服务器,进入TIME_WAIT状态
    • 客户端应用程序调用close后,TCP发送FIN给服务器,如果发送的同时接收到服务器的FIN,状态转换为CLOSING,表示同时关闭
服务器
    服务器应用程序启动后,一直等待客户端连接, 当等到以后, 执行被动打开。
  1. 服务器程序启动后,如果程序编写正确,自动从初始状态CLOSED转换为LISTEN
  2. 服务器收到客户端的SYN, 然后发送SYN和ACK给客户端,这时服务器处于SYN_RCVD
  3. 服务器收到客户端的ACK, 状态变为ESTABLISHED
  4. 服务器收到客户端的FIN, 然后发送ACK给客户端,状态变为CLOSE_WAIT
  5. 服务器应用程序调用close时,TCP发送FIN给客户,状态为LAST_ACK,表示等待最后一个ACK.
  6. 服务器收到客户的ACK, 状态变为初始状态 CLOSED.



posted @ 2010-03-23 11:00 逆水行舟 阅读(350) | 评论 (0)编辑 收藏

2010年3月19日 #

在ubuntu中安装 manpages-dev

By default installation, Ubuntu does not include the manual pages for developer, use the following command to install them manually:

1. apt-cache search manpages-dev
    This command search the man pages and output the following:
manpages-dev - Manual pages about using GNU/Linux for development
manpages-de-dev - German development manpages
manpages-fr-extra - French version of the manual pages


2. sudo apt-get install manpages-dev
this command install the man pages in the computer.

posted @ 2010-03-19 22:58 逆水行舟 阅读(465) | 评论 (0)编辑 收藏

2010年2月4日 #

函数指针的应用

  1. 函数指针的定义:
    void (*funcPtr)(); 
    这个表达式定义一个指向没有参数,没有返回值的函数。函数指针变量名是funcPtr. 分析一个较复杂的函数指针定义表达式时,可按下列步骤进行:
    • 先找到变量名
    • 找变量名右边的项,然后找左边的项,然后右边,...这种右-左-右的方法适用于大多数的表达式。
  2. void (*funcPtr)();的分析
    • 变量名是funcPtr,
    • 找右边,右边没有项了,只是一个右括号:")"
    • 找左边,变量左边是*表示funcPtr是一个指针
    • 找右边, 是(),表示一个空参数列表
    • 找左边,*的左边是void, 表示函数的返回类型。
    • 结果:
      funcPtr是一个指向函数的指针,该函数无参数,返回类型是void.
  3. void * (*(*fp1)(int))[10];的分析:
    • fp1
    • 右: )
    • 左: *, fp1是一个指针
    • 右:(int), fp1指向的函数的参数是int
    • 左:*,fp1指向的函数的返回值是一个指针
    • 右[10],fp1指向的函数的返回值是一个指针数组
    • 左void *,指针数组指向的是void类型。
  4. float (*(*fp2)(int,int,float))(int);
    •  fp2指向一个带有三个参数的函数,这个函数f返回一个指针,该指针又指向一个函数,这个函数有一个int参数,返回类型是float.
  5. 使用函数指针
    • 定义函数指针
    • 定义函数
    • 将函数地址赋给函数指针
    • 通过函数指针调用函数
#include <iostream>
using namespace std;

void func(){
     cout
<<"func() called" << endl; 
}

int main(){
     
void (*fp)();  // define a function pointer
     fp = func;     // Initialize it
     (*fp)();       // Dereferencing calls the function
     void (*fp2)() = func; // define and initialize
     (*fp2)();
}


posted @ 2010-02-04 17:02 逆水行舟 阅读(258) | 评论 (0)编辑 收藏

C++中使用#号输出变量名 - 在调试时有用

通过#define来使用#来输出变量的名字

 1 #define P(A) cout << #A << ": " << (A) << endl;
 2 
 3 int _tmain(int argc, _TCHAR* argv[])
 4 {
 5     int a = 1, b = 2, c = 3;
 6     P(a);
 7     P(b);
 8     P(c);
 9     P(a + b);
10     return 0;
11 }

输出:
a: 1
b: 2
c: 3
a + b: 3

在Think In C++中,有这样一段描述:
When you put a # before an argument in a preprocessor macro, the preprocessor turns that argument into a character array.

在定义宏的时候,1. 在变量名前加#, 2. 输出变量值的时候使用()括号括起来。


posted @ 2010-02-04 15:21 逆水行舟 阅读(1474) | 评论 (0)编辑 收藏

仅列出标题