add lots file of APUE

This commit is contained in:
geekard
2012-10-30 20:31:20 +08:00
parent 05e8ae5877
commit 6642e173f9
113 changed files with 4954 additions and 181 deletions

View File

@@ -0,0 +1,346 @@
Content-Type: text/x-zim-wiki
Wiki-Format: zim 0.4
Creation-Date: 2012-10-15T20:39:55+08:00
====== Linux Epoll介绍和程序实例 ======
Created Monday 15 October 2012
http://blog.csdn.net/sparkliang/article/details/4770655
===== Epoll 是何方神圣? =====
Epoll 可是当前在 Linux 下开发**大规模并发网络程序**的热门人选, Epoll 在 Linux2.6 内核中正式引入,和 select 相似,其实都 **I/O 多路复用**技术而已 ,并没有什么神秘的。其实在 Linux 下设计并发网络程序,向来不缺少方法,比如典型的 Apache 模型( Process Per Connection ,简称 __PPC__ __TPC__ Thread Per Connection )模型,以及 select 模型和 poll 模型,那为何还要再引入 Epoll 这个东东呢?那还是有得说说的 …
===== 常用模型的缺点 =====
如果不摆出来其他模型的缺点,怎么能对比出 Epoll 的优点呢。
==== 2.1 PPC/TPC 模型 ====
这两种模型思想类似,就是让每一个到来的连接一边自己做事去,别再来烦我 。只是 PPC 是为它开了一个进程,而 TPC 开了一个线程。可是别烦我是有代价的,它要**时间和空间**啊连接多了之后那么多的__进程 / 线程切换开销__就上来了因此这类模型能接受的最大连接数都不会高一般在几百个左右。
==== 2.2 select 模型 ====
1. 最大并发数限制,因为一个进程所打开的 FD (文件描述符)是有限制的,由 FD_SETSIZE 设置,默认值是 1024/2048 ,因此 Select 模型的最大并发数就被相应限制了。自己改改这个 FD_SETSIZE ?想法虽好,可是先看看下面吧 …
2. 效率问题, select 每次调用都会**线性扫描全部**的 FD 集合,这样效率就会呈现线性下降,把 FD_SETSIZE 改大的后果就是,大家都慢慢来,什么?都超时了??!!
3. 内核 / 用户空间 **内存拷贝问题**,如何让内核把 FD 消息通知给用户空间呢?在这个问题上 select 采取了**内存拷贝**方法。
==== 2.3 poll 模型 ====
基本上效率和 select 是相同的, select 缺点的 2 和 3 它都没有改掉。
===== 3. Epoll 的提升 =====
把其他模型逐个批判了一下,再来看看 Epoll 的改进之处吧,其实把 select 的缺点反过来那就是 Epoll 的优点了。
3.1. Epoll 没有最大并发连接的限制,上限是最大可以打开文件的数目,这个数字一般远大于 2048, 一般来说这个数目和系统内存关系很大 ,具体数目可以 cat /proc/sys/fs/file-max 察看。
3.2. 效率提升, Epoll 最大的优点就在于它只管你__“活跃”__的连接 ,而跟连接总数无关,因此在实际的网络环境中, Epoll 的效率就会远远高于 select 和 poll 。
3.3. 内存拷贝, Epoll 在这点上使用了“共享内存 ”,这个内存拷贝也省略了。
===== 4. Epoll 为什么高效 =====
Epoll 的高效和其数据结构的设计是密不可分的,这个下面就会提到。
首先回忆一下 select 模型,当有 I/O 事件到来时, select 通知应用程序有事件到了快去处理而应用程序必须__轮询所有的 FD 集合__测试每个 FD 是否有事件发生,并处理事件;代码像下面这样:
int res = select(maxfd+1, &readfds, NULL, NULL, 120);
if (res > 0)
{
for (int i = 0; i < MAX_CONNECTION; i++)
{
if (FD_ISSET(allConnection[i], &readfds))
{
handleEvent(allConnection[i]);
}
}
}
// if(res == 0) handle timeout, res < 0 handle error
Epoll 不仅会告诉应用程序有I/0 事件到来还会告诉应用程序相关的信息这些信息是应用程序填充的因此根据这些信息应用程序就能直接定位到事件而__不必遍历整个FD 集合__。
int res = epoll_wait(epfd, events, 20, 120);
for (int i = 0; i < res;i++)
{
handleEvent(events[n]);
}
===== 5. Epoll 关键数据结构 =====
前面提到 Epoll 速度快和其数据结构密不可分,其关键数据结构就是:
struct epoll_event {
__uint32_t events; // Epoll events
epoll_data_t data; // User data variable
};
typedef union epoll_data {
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;
可见 epoll_data 是一个 union 结构体 , 借助于它应用程序可以__保存很多类型的信息__ :fd 、指针等等。有了它,应用程序就可以直接定位目标了。
6. ===== 使用 Epoll =====
既然 Epoll 相比 select 这么好,那么用起来如何呢?会不会很繁琐啊 … 先看看下面的三个函数吧,就知道 Epoll 的易用了。
int epoll_create(int size);
生成一个 Epoll 专用的文件描述符,其实是**申请一个内核空间**,用来存放你想关注的 socket fd 上是否发生以及发生了什么事件。 size 就是你在这个 Epoll fd 上能关注的最大 socket fd 数,大小自定,只要内存足够。
int epoll_ctl(int epfd, int __op__, int fd, struct epoll_event *event );
控制某个 Epoll 文件描述符上的事件:注册、修改、删除。其中参数 epfd 是 epoll_create() 创建 Epoll 专用的文件描述符。相对于 select 模型中的 FD_SET 和 FD_CLR 宏。
int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout);
等待 I/O 事件的发生;参数说明:
epfd: 由 epoll_create() 生成的 Epoll 专用的文件描述符;
epoll_event: 用于__回传__代处理事件的数组
maxevents: 每次能处理的事件数;
timeout: 等待 I/O 事件发生的超时值;
返回发生事件数。相对于 select 模型中的 select 函数。
7. 例子程序
下面是一个简单 Echo Server 的例子程序,麻雀虽小,五脏俱全,还包含了一个简单的超时检查机制,简洁起见没有做错误处理。
//
// a simple echo server using epoll in linux
//
// 2009-11-05
// by sparkling
//
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <iostream>
using namespace std;
#define MAX_EVENTS 500
struct myevent_s
{
int fd;
int events;
void *arg;
void (*call_back)(int fd, int events, void *arg); //call_back函数的参数都从结构体成员中得到。
int status; // __1: in epoll wait list, 0 not in__
char buff[128]; // recv data buffer
int len;
long last_active; // last active time
};
// set event
void EventSet(myevent_s *ev, int fd, void (*call_back)(int, int, void*), void *arg)
{
ev->fd = fd;
ev->call_back = call_back;
**ev->events = 0;**
**ev->arg = arg; //arg其实就是struct myevent_s的指针。**
ev->status = 0;
ev->last_active = time(NULL);
}
// add/mod an event to epoll
void EventAdd(int epollFd, int events, myevent_s *ev)
{
__struct epoll_event epv = {0, {0}};__
int op;
__epv.data.ptr = ev; //myevent_s被封装到epoll_event的ptr成员中。__
__epv.events = ev->events = events;__
if(ev->status == 1){ //ev->fd已经处于epoll wait list中
op = EPOLL_CTL_MOD;
}
else{
op = EPOLL_CTL_ADD;
ev->status = 1;
}
if(__epoll_ctl__(epollFd, op, ev->fd, **&epv**) < 0)
printf("Event Add failed[fd=%d]/n", ev->fd);
else
printf("Event Add OK[fd=%d]/n", ev->fd);
}
// delete an event from epoll
void EventDel(int epollFd, myevent_s *ev)
{
struct epoll_event epv = {0, {0}};
if(ev->status != 1) return;
epv.data.ptr = ev;
ev->status = 0;
__epoll_ctl__(epollFd, EPOLL_CTL_DEL, ev->fd, &epv);
}
int g_epollFd;
myevent_s g_Events[MAX_EVENTS+1]; // g_Events[MAX_EVENTS] is used by __socket listen fd__
void RecvData(int fd, int events, void *arg);
void SendData(int fd, int events, void *arg);
// accept new connections from clients
void AcceptConn(int fd, int events, void *arg) //arg参数在该函数中并没有被使用。
{
__struct sockaddr_in__ sin;
socklen_t len = sizeof(struct sockaddr_in);
int nfd, i;
// accept
if((__nfd__ = accept(fd, (struct sockaddr*)&sin, &len)) == -1)
{
if(errno != EAGAIN && errno != EINTR)
{
printf("%s: bad accept", __func__);
}
return;
}
do
{
for(i = 0; i < MAX_EVENTS; i++)
{
if(g_Events[i].status == 0)
{
break;
}
}
if(i == MAX_EVENTS)
{
printf("%s:max connection limit[%d].", __func__, MAX_EVENTS);
break;
}
__// set nonblocking__
if(fcntl(nfd, F_SETFL, O_NONBLOCK) < 0) break;
// add a read event for receive data
EventSet(&g_Events[i], nfd, __RecvData__, &g_Events[i]); //注册一个**回调函数**
EventAdd(g_epollFd, **EPOLLIN|EPOLL**__ET__, &g_Events[i]);
printf("new conn[%s:%d][time:%d]/n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), g_Events[i].last_active);
}while(0);
}
// receive data
void RecvData(int fd, int events, void *arg) //当fd可读时envents和arg来源于__struct epoll_event 的 epv.data.ptr和epv.events 成员。__
{
struct myevent_s *ev = __(struct myevent_s*)arg__;
int len;
// receive data
len = recv(fd, ev->buff, sizeof(ev->buff)-1, 0);
EventDel(g_epollFd, ev);
if(len > 0)
{
ev->len = len;
ev->buff[len] = '/0';
printf("C[%d]:%s/n", fd, ev->buff);
// change to **send** event
EventSet(ev, fd, __SendData__, ev);
EventAdd(g_epollFd, __EPOLLOUT|EPOLLET,__ ev);
}
else if(len == 0)
{
close(ev->fd);
printf("[fd=%d] closed gracefully./n", fd);
}
else
{
close(ev->fd);
printf("recv[fd=%d] error[%d]:%s/n", fd, errno, strerror(errno));
}
}
// send data
void SendData(int fd, int events, void *arg)
{
struct myevent_s *ev = (struct myevent_s*)arg;
int len;
// send data
len = send(fd, ev->buff, ev->len, 0);
ev->len = 0;
EventDel(g_epollFd, ev);
if(len > 0)
{
// change to **receive** event
EventSet(ev, fd, __RecvData__, ev);
EventAdd(g_epollFd, EPOLLIN|EPOLLET, ev);
}
else
{
close(ev->fd);
printf("recv[fd=%d] error[%d]/n", fd, errno);
}
}
void InitListenSocket(int epollFd, short port)
{
int listenFd = socket(AF_INET, SOCK_STREAM, 0);
fcntl(listenFd, F_SETFL, O_NONBLOCK); // set non-blocking
printf("server listen fd=%d/n", listenFd);
EventSet(&g_Events[MAX_EVENTS], __listenFd, AcceptConn__, &g_Events[MAX_EVENTS]); //注册一个**回调函数**
// add listen socket
EventAdd(epollFd, __EPOLLIN|EPOLLET__, &g_Events[MAX_EVENTS]);
// bind & listen
sockaddr_in sin;
bzero(&sin, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = htons(port);
bind(listenFd, (const sockaddr*)&sin, sizeof(sin));
listen(listenFd, 5);
}
int main(int argc, char **argv)
{
short port = 12345; // default port
if(argc == 2){
port = atoi(argv[1]);
}
**// create epoll**
g_epollFd = epoll_create(MAX_EVENTS);
if(g_epollFd <= 0) printf("create epoll failed.%d/n", g_epollFd);
// create & bind listen socket, and add to epoll, set non-blocking
InitListenSocket(**g_epollFd**, port);
__// event loop__
struct epoll_event events[MAX_EVENTS];
printf("server running:port[%d]/n", port);
int checkPos = 0;
while(1){
// a simple timeout check here, every time 100, better to use a mini-heap, and add timer event
long now = time(NULL);
for(int i = 0; i < 100; i++, checkPos++) // __doesn't check listen fd__
{
if(checkPos == MAX_EVENTS) checkPos = 0; // recycle
if(g_Events[checkPos].status != 1) continue;
long duration = now - g_Events[checkPos].last_active;
if(duration >= 60) // 60s timeout
{
close(g_Events[checkPos].fd);
printf("[fd=%d] timeout[%d--%d]./n", g_Events[checkPos].fd, g_Events[checkPos].last_active, now);
EventDel(g_epollFd, &g_Events[checkPos]);
}
}
// wait for events to happen
int fds = __epoll_wait__(g_epollFd, __events__, MAX_EVENTS, 1000);
if(fds < 0){
printf("epoll_wait error, exit/n");
break;
}
for(int i = 0; i < fds; i++){
**myevent_s *ev = (struct myevent_s*)events[i].data.ptr ;**
if((events[i].events&**EPOLLIN**)&&(ev->events&EPOLLIN)) // read event
{
ev->call_back(ev->fd, events[i].events, ev->arg);
}
if((events[i].events&**EPOLLOUT**)&&(ev->events&EPOLLOUT)) // write event
{
ev->call_back(ev->fd, events[i].events, ev->arg);
}
}
}
// free resource
return 0;
}

View File

@@ -0,0 +1,138 @@
Content-Type: text/x-zim-wiki
Wiki-Format: zim 0.4
Creation-Date: 2012-10-15T21:09:07+08:00
====== epoll精髓 ======
Created Monday 15 October 2012
http://www.cnblogs.com/OnlyXP/archive/2007/08/10/851222.html
在linux的网络编程中很长的时间都在使用select来做**事件触发**。在linux新的内核中有了一种替换它的机制就是epoll。相比于selectepoll最大的好处在于它**不会随着监听fd数目的增长而降低效率**。因为在内核中的select实现中它是采用轮询来处理的轮询的fd数目越多自然耗时越多。并且在linux/posix_types.h头文件有这样的声明
#define __FD_SETSIZE 1024
表示select最多同时监听1024个fd当然可以通过修改头文件再重编译内核来扩大这个数目但这似乎并不治本。
epoll的接口非常简单一共就三个函数
1. int epoll_create(int size);
创建一个epoll的句柄size用来告诉内核这个监听的数目一共有多大。这个参数不同于select()中的第一个参数给出最大监听的fd+1的值。需要注意的是当创建好epoll句柄后它就是会__占用一个fd值__在linux下如果查看/proc/进程id/fd/是能够看到这个fd的所以在使用完epoll后必须调用close()关闭否则可能导致fd被耗尽。
2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epoll的**事件注册函数**它不同与select()是在监听事件时告诉内核要监听什么类型的事件而是在这里__先注册要监听的事件类型__。
第一个参数是epoll_create()的返回值
第二个参数表示动作,用三个宏来表示:
EPOLL_CTL_ADD注册新的fd到epfd中
EPOLL_CTL_MOD修改已经注册的fd的监听事件
EPOLL_CTL_DEL从epfd中删除一个fd
第三个参数是需要监听的fd
第四个参数是告诉内核需要**监听什么事**struct epoll_event结构如下
struct epoll_event {
__uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
events可以是以下几个**宏的位与**
EPOLLIN 表示对应的文件描述符可以读包括对端SOCKET正常关闭
EPOLLOUT表示对应的文件描述符可以写
EPOLLPRI表示对应的文件描述符有紧急的数据可读这里应该表示有带外数据到来
EPOLLERR表示对应的文件描述符发生错误
EPOLLHUP表示对应的文件描述符被挂断
EPOLLET 将EPOLL设为__边缘触发__(Edge Triggered)模式这是相对于__水平触发__(Level Triggered)来说的。
EPOLLONESHOT只监听一次事件当监听完这次事件之后如果还需要继续监听这个socket的话需要再次把这个socket加入到EPOLL队列里
typedef union epoll_data {
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;
3. int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
等待事件的产生类似于select()调用。参数events用来__从内核得到事件的集合__maxevents告之内核这个events有多大这个maxevents的值不能大于创建epoll_create()时的size参数timeout是超时时间毫秒0会立即返回-1将不确定也有说法说是永久阻塞。该函数返回需要处理的事件数目如返回0表示已超时。
--------------------------------------------------------------------------------------------
从man手册中得到ET和LT的具体描述如下
EPOLL事件有两种模型
Edge Triggered (ET)
Level Triggered (LT)
假如有这样一个例子:
1. 我们已经把一个用来从管道中读取数据的文件句柄(RFD)添加到epoll描述符
2. 这个时候从管道的另一端被写入了2KB的数据
3. 调用epoll_wait(2)并且它会返回RFD说明它已经准备好读取操作
4. 然后我们读取了1KB的数据
5. 调用epoll_wait(2)......
=== Edge Triggered 工作模式: ===
如果我们在第1步将RFD添加到epoll描述符的时候使用了__EPOLLET__标志那么在第5步调用epoll_wait(2)之后将有可能会挂起,因为剩余的数据还存在于文件的输入缓冲区内,而且**数据发出端**还在等待一个针对已经发出数据的反馈信息。__只有在监视的文件句柄上发生了某个事件的时候 ET 工作模式才会汇报事件__。因此在第5步的时候调用者可能会放弃等待仍存在于文件输入缓冲区内的剩余数据。在上面的例子中会有一个事件产生在RFD句柄上因为在第2步执行了一个写操作然后__事件将会在第3步被销毁__。因为第4步的读取操作没有读空文件输入缓冲区内的数据因此我们在第5步调用 epoll_wait(2)完成后是否挂起是不确定的。epoll工作在ET模式的时候必须使用非阻塞套接口以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。最好以下面的方式调用ET模式的epoll接口在后面会介绍避免可能的缺陷。
i 基于非阻塞文件句柄
ii 只有当read(2)或者write(2)返回EAGAIN时才需要挂起等待。但这并不是说每次read()时都需要循环读直到读到产生一个EAGAIN才认为此次事件处理完成当read()返回的__读到的数据长度小于请求的数据长度时就可以确定此时缓冲中已没有数据了__也就可以认为此事读事件已处理完成。
=== Level Triggered 工作模式 ===
相反的以LT方式调用epoll接口的时候它就相当于一个速度比较快的poll(2),并且无论后面的数据是否被使用,因此他们具有同样的职能(**如果epoll_wait返回的fd数据没有读完下次再调用epoll_wait时继续返回该fd可读事件。**)。因为即使使用ET模式的epoll在收到多个chunk的数据的时候仍然会产生多个事件。调用者可以设定EPOLLONESHOT标志在 epoll_wait(2)收到事件后epoll会与事件关联的文件句柄从epoll描述符中禁止掉。因此当EPOLLONESHOT设定后使用带有 EPOLL_CTL_MOD标志的epoll_ctl(2)处理文件句柄就成为调用者必须作的事情。
然后详细解释ET, LT:
LT(level triggered)是__缺省的工作方式__并且同时支持block和no-block socket.在这种做法中内核告诉你一个文件描述符是否就绪了然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作__内核还是会继续通知你的(下一次调用epoll_wait会继续立即返回)__所以这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表
ET(edge-triggered)是高速工作方式__只支持no-block socket__。在这种模式下当描述符从未就绪变为就绪时内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪并且不会再为那个文件描述符发送更多的就绪通知(__即使下一次调用epoll_wait时该fd仍然就绪),直到你做了某些操作导致那个文件描述符不再为就绪状态了(__比如**读该fd直到将缓冲区中的数据读完或写该fd直到将缓冲区填满。**。但是请注意如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once)。
在许多测试中我们会看到如果没有大量的idle -connection或者dead-connectionepoll的效率并不会比select/poll高很多但是当我们遇到大量的idle- connection(例如WAN环境中存在大量的慢速连接)就会发现epoll的效率大大高于select/poll。
另外当使用epoll的ET模型来工作时当产生了一个EPOLLIN事件后读数据的时候需要考虑的是当recv()返回的大小如果等于请求的大小那么很有可能是缓冲区还有数据未读完也意味着该次事件还没有处理完所以__还需要再次读取(直到对到的数据量小于请求的数据量或返回EAGAIN或EWOULDBLOCK错误)__
while(rs)
{
buflen = recv(activeevents[i].data.fd, buf, sizeof(buf), 0);
if(buflen < 0)
{
// 由于是非阻塞的模式,所以当errno为EAGAIN时,表示当前缓冲区已无数据可读
// 在这里就当作是该次事件已处理处.
if(errno == EAGAIN)
break;
else
return;
}
else if(buflen == 0)
{
// 这里表示对端的socket已正常关闭.
}
if(buflen == sizeof(buf)
rs = 1; // **需要再次读取**
else
rs = 0;
}
还有,假如发送端流量大于接收端的流量(意思是epoll所在的程序读比转发的socket要快), 由于是非阻塞的socket,那么send()函数虽然返回,但实际缓冲区的数据并未真正发给接收端,这样不断的读和发当缓冲区满后会产生EAGAIN错误(参考man send),同时,不理会这次请求发送的数据.所以,需要封装socket_send()的函数用来处理这种情况,该函数会尽量将数据写完再返回,返回-1表示出错。在socket_send()内部,当写缓冲已满(send()返回-1,且errno为EAGAIN),那么会等待后再重试.这种方式并不很完美,在理论上可能会长时间的阻塞在socket_send()内部,但暂没有更好的办法.
ssize_t socket_send(int sockfd, const char* buffer, size_t buflen)
{
ssize_t tmp;
size_t total = buflen;
const char *p = buffer;
while(1)
{
tmp = send(sockfd, p, total, 0);
if(tmp < 0)
{
// 当send收到信号时,可以继续写,但这里返回-1.
if(errno == EINTR)
return -1;
// 当socket是非阻塞时,如返回EAGAIN错误,表示写缓冲队列已满,
// 在这里做延时后再重试.
if(errno == EAGAIN)
{
usleep(1000);
continue;
}
return -1;
}
if((size_t)tmp == total)
return buflen;
total -= tmp;
p += tmp;
}
return tmp;
}

View File

@@ -5,7 +5,9 @@ Creation-Date: 2011-06-03T21:32:39+08:00
====== Linux 网络编程之TIME WAIT状态 ======
Created 星期五 03 六月 2011
http://blog.csdn.net/feiyinzilgd/archive/2010/09/19/5894446.aspx
{{./TIME_WAIT.jpg}}
刚刚开始看TCP socket的4次握手终止流程图的时候,对于最后的TIME_WAIT状态不是很理解.现在在回过头来研究,发现TIME_WAIT状态是一个很微妙状态.之所以设计TIME_WAIT状态的原因有2个原因:
* 使得TCP的全双工连接能够可靠的终止.
@@ -13,14 +15,14 @@ http://blog.csdn.net/feiyinzilgd/archive/2010/09/19/5894446.aspx
在具体详解这两个原因之前,我们需要理解MSL(maxinum segment lifetime)这个概念.
每一个TCP 都必须有一个MSL值.这个值一般是2分钟,但也不是固定的,不同的系统不一样.无论是否出错或者连接被断开,总之,一个数据包在网路上能停留的最大时间是MSL.也就是说MSL是数据包的生命周期时间.操作这个时间,该数据包将会被丢弃而不被发送.而TIME_WAIT状态持续的时间是MSL的两倍,也就是2MSL时间.
每一个TCP 都必须有一个MSL值.这个值一般是2分钟,但也不是固定的,不同的系统不一样.无论是否出错或者连接被断开,总之,一个数据包在网路上能停留的最大时间是MSL.也就是说MSL是**数据包的生命周期时间.**操作这个时间,该数据包将会被丢弃而不被发送.而TIME_WAIT状态持续的时间是MSL的两倍,也就是2MSL时间.
TCP的全双工连接能够被可靠终止
TCP的可靠终止需要经过4次握手终止.如上图所示:首先,client 主动close,导致FIN发送给server,server接收到FIN后,给client回复一个ACK,之后,server会关闭和client的连接,即向client发送一个FIN,client接收到FIN之后,会发送一个ACK给server.此时client就进入TIME_WAIT状态.如果server没有收到ACK,server会重新发送一个FIN信息给client,client会重发ACK,server然后继续等待client发送一个ACK.这样保证了双方的可靠终止.2端都知道对方已经终止了.那么,在这个TIME_WAIT时间中,可以重发ACK,如果client没有收到FIN信息,则TCP会向server发送一个RST信息,这个信息会被server解释成error.
TCP的可靠终止需要经过4次握手终止.如上图所示:首先,client 主动close,导致FIN发送给server,server接收到FIN后,给client回复一个ACK,之后,server会关闭和client的连接,即向client发送一个FIN,client接收到FIN之后,会发送一个ACK给server. 此时client就进入TIME_WAIT状态.**如果server没有收到ACK,server会重新发送一个FIN信息给client,client会重发ACK**,server然后继续等待client发送一个ACK.这样保证了双方的可靠终止.2端都知道对方已经终止了.那么,**在这个TIME_WAIT时间中**,可以重发ACK,如果client没有收到FIN信息,则TCP会向server发送一个RST信息,这个信息会被server解释成error.
连接终止后网络上任然残留的发送到该连接的数据被丢弃而不至于被新连接接收.
举个例子:
在10.12.24.48 port:21和206.8.16.32 port:23(不必关心哪一端是server哪一端是client)之间建立了一个TCP连接A.然后此链接A被close掉了.然后此时又在10.12.24.48 port:21和206.8.16.32 port:23(不必关心哪一端是server哪一端是client)之间建立了一个新的TCP连接B.很可能A和B连接是有不同的应用程序建立的.那么,当我们close掉A之后,网络上很有可能还有属于A连接两端的数据m正在网路上被传送.而此时A被close掉了,重新建立了B连接,由于A和B连接的地址和端口都是一样的.这样,m数据就会被最终发送到B连接的两端.这样就造成了混乱,B接收到了原本数据A的数据.处于TIME_WAIT状态的连接会禁止新的同样的连接(如A,B)连接被建立.除非等到TIME_WAIT状态结束,也就是2MSL时间之后.其中,一个MSL时间是为了网络上的正在被发送到该链接的数据被丢弃,另一个MSL使得应答信息被丢弃.这样,2MSL之后,保证重新建立的所得到的数据绝对不会是发往就连接的数据.
在10.12.24.48 port:21和206.8.16.32 port:23(不必关心哪一端是server哪一端是client)之间建立了一个TCP连接A.然后此链接A被close掉了.然后此时又在10.12.24.48 port:21和206.8.16.32 port:23(不必关心哪一端是server哪一端是client)之间建立了一个新的TCP连接B.很可能A和B连接是有不同的应用程序建立的.那么,当我们close掉A之后,网络上很有可能还有属于A连接两端的数据m正在网路上被传送.而此时A被close掉了,重新建立了B连接,由于A和B连接的地址和端口都是一样的.这样,m数据就会被最终发送到B连接的两端.这样就造成了混乱,**B接收到了原本数据A的数据**.__处于TIME_WAIT状态的连接会禁止新的同样的连接(如A,B)连接被建立.除非等到TIME_WAIT状态结束,也就是2MSL时间之后__.其中,一个MSL时间是为了网络上的正在被发送到该链接的数据被丢弃,另一个MSL使得应答信息被丢弃.这样,2MSL之后,保证重新建立的所得到的数据绝对不会是发往就连接的数据.

View File

@@ -17,9 +17,7 @@ http://www.cnblogs.com/xuyuan77/archive/2008/09/06/1285800.html
  我们将传统方案中的线程执行过程分为三个过程T1、T2、T3。
  T1线程创建时间
  T2线程执行时间包括线程的同步等时间
  T3线程销毁时间
  那么我们可以看出,线程本身的开销所占的比例为(T1+T3) / (T1+T2+T3)。如果线程执行的时间很短的话这比开销可能占到20%-50%左右。如果任务执行时间很频繁的话,这笔开销将是不可忽略的。
@@ -35,11 +33,8 @@ http://www.cnblogs.com/xuyuan77/archive/2008/09/06/1285800.html
  一般线程池都必须具备下面几个组成部分:
  1) 线程池管理器:用于创建并管理线程池
  2) 工作线程: 线程池中实际执行的线程
 2) 工作线程: 线程池中实际执行的线程
  3) 任务接口: 尽管线程池大多数情况下是用来支持网络服务器,但是我们将线程执行的任务抽象出来,形成任务接口,从而是的线程池与具体的任务无关。
  4) 任务队列:线程池的概念具体到实现则可能是队列,链表之类的数据结构,其中保存执行线程。
  我们实现的通用线程池框架由五个重要部分组成CThreadManageCThreadPoolCThreadCJobCWorkerThread除此之外框架中还包括线程同步使用的类CThreadMutex和CCondition。

View File

@@ -7,13 +7,10 @@ Created 星期五 03 六月 2011
Linux网络编程中,socket的选项很多.其中几个比较重要的选项有:SO_LINGER(仅仅适用于TCP,SCTP), SO_REUSEADDR.
SO_LINGER
__SO_LINGER__
在默认情况下,当调用close关闭socke的使用,close会立即返回,但是,如果send buffer中还有数据,系统会试着先把send buffer中的数据发送出去,然后close才返回.
SO_LINGER选项则是用来修改这种默认操作的.SO_LINGER相关联的一个结构体如下:
view plaincopy to clipboardprint?
SO_LINGER选项则是用来修改这种默认操作的. SO_LINGER相关联的一个结构体如下:
#include <sys/socket.h>
struct linger {
@@ -23,71 +20,66 @@ view plaincopy to clipboardprint?
当调用setsockopt之后,该选项产生的影响取决于linger结构体中 l_onoff和l_linger的值:
0 = l_onoff
**l_onoff = 0**
当l_onoff被设置为0的时候,将会关闭SO_LINGER选项,即TCP或则SCTP保持默认操作:close立即返回.l_linger值被忽略.
l_lineoff值非00 = l_linger
当调用close的时候,TCP连接会立即断开.send buffer中未被发送的数据将被丢弃,并向对方发送一个RST信息.值得注意的是由于这种方式是非正常的4中握手方式结束TCP链接所以TCP连接将不会进入TIME_WAIT状态这样会导致新建立的可能和就连接的数据造成混乱。具体原因详见我的上一篇文章《linux 网络编程之TIME_WAIT状态》
l_onoff和l_linger都是非0
在这种情况下回事的close返回得到延迟。调用close去关闭socket的时候内核将会延迟。也就是说如果send buffer中还有数据尚未发送该进程将会被休眠直到一下任何一种情况发生
**l_lineoff值非0l_linger = 0**
**当调用close的时候,TCP连接会立即断开.send buffer中未被发送的数据将被丢弃,并向对方发送一个**__RST信息__**.**值得注意的是由于这种方式是非正常的4中握手方式结束TCP链接所以TCP连接将不会进入TIME_WAIT状态这样会导致新建立的可能和就连接的数据造成混乱。具体原因详见我的上一篇文章《linux 网络编程之TIME_WAIT状态》
**l_onoff和l_linger都是非0**
在这种情况下调用close去关闭socket的时候内核将会延迟。也就是说如果send buffer中还有数据尚未发送该进程将会被休眠直到一下任何一种情况发生
1) send buffer中的所有数据都被发送并且得到对方TCP的应答消息这种应答并不是意味着对方应用程序已经接收到数据在后面shutdown将会具体讲道
2) 延迟时间消耗完。在延迟时间被消耗完之后send buffer中的所有数据都将会被丢弃
2) 延迟时间消耗完。在延迟时间被消耗完之后send buffer中的所有数据都将会被__丢弃__
上面1),2)两种情况中如果socket被设置为O_NONBLOCK状态程序将不会等待close返回send buffer中的所有数据都将会被丢弃。所以需要我们判断close的返回值。在send buffer中的所有数据都被发送之前并且延迟时间没有消耗完close返回的话close将会返回一个EWOULDBLOCK的error.
下面用几个实例来说明:
A. Close默认操作立即返回
A. __Close默认操作立即返回__
{{./1.jpg}}
此种情况close立即返回如果send buffer中还有数据close将会等到所有数据被发送完之后之后返回。由于我们并没有等待对方TCP发送的ACK信息所以我们只能保证数据已经发送到对方我们并不知道对方是否已经接受了数据。由于此种情况TCP连接终止是按照正常的4次握手方式需要经过TIME_WAIT。
此种情况,__close立即返回如果send buffer中还有数据close将会等到所有数据被发送完之后之后返回__。由于我们并没有等待对方TCP发送的ACK信息所以__我们只能保证数据已经发送到对方,我们并不知道对方是否已经接受了数据。__由于此种情况TCP连接终止是按照正常的4次握手方式需要经过TIME_WAIT。
B. l_onoff非0并且使之l_linger为一个整数
B. l_onoff非0并且使之l_linger为一个整数
{{./2.jpg}}
在这种情况下close会在接收到对方TCP的ACK信息之后才返回(l_linger消耗完之前)。但是这种ACK信息只能保证对方已经接收到数据并不保证对方应用程序已经读取数据。
在这种情况下,__close会在接收到对方TCP的ACK信息之后才返回(l_linger消耗完之前)。但是这种ACK信息只能保证对方已经接收到数据并不保证对方应用程序已经读取数据。__
C. l_linger设置值太小
C. l_linger设置值太小
{{./3.jpg}}
这种情况由于l_linger值太小在send buffer中的数据都发送完之前close就返回此种情况终止TCP连接l_linger = 0类似TCP连接终止不是按照正常的4步握手所以TCP连接不会进入TIME_WAIT状态那么client会向server发送一个RST信息.
这种情况,__由于l_linger值太小在send buffer中的数据都发送完之前close就返回此种情况终止TCP连接l_linger = 0类似TCP连接终止不是按照正常的4步握手所以TCP连接不会进入TIME_WAIT状态那么client会向server发送一个RST信息.__
D. Shutdown等待应用程序读取数据
D. Shutdown等待应用程序读取数据
{{./4.jpg}}
同上面的B进行对比调用shutdown后紧接着调用read,此时read会被阻塞,直到接收到对方的FIN,也就是说read是在server的应用程序调用close之后才返回的。当server应用程序读取到来自client的数据和FIN之后server会进入一个叫CLOSE_WAIT关于CLOSE_WAIT详见我的博客《 Linux 网络编程 之 TCP状态转换》 。那么如果server端要断开该TCP连接需要server应用程序调用一次close也就意味着向client发送FIN。这个时候说明server端的应用程序已经读取到client发送的数据和FIN。read会在接收到server的FIN之后返回。所以shutdown 可以确保server端应用程序已经读取数据了而不仅仅是server已经接收到数据而已。
同上面的B进行对比调用shutdown后紧接着调用read,此时read会被阻塞,直到接收到对方的FIN,也就是说read是在server的应用程序调用close之后才返回的。当server应用程序读取到来自client的数据和FIN之后server会进入一个叫CLOSE_WAIT关于CLOSE_WAIT详见我的博客《 Linux 网络编程 之 TCP状态转换》 。那么如果server端要断开该TCP连接需要server应用程序调用一次close也就意味着向client发送FIN。这个时候说明server端的应用程序已经读取到client发送的数据和FIN。read会在接收到server的FIN之后返回。所以__shutdown 可以确保server端应用程序已经读取数据了而不仅仅是server已经接收到数据而已。__
shutdown参数如下:
SHUT_RD:调用shutdown的一端receive buffer将被丢弃掉,无法接受数据,但是可以发送数据,send buffer的数据可以被发送出去
SHUT_WR:调用shutdown的一端无法发送数据,但是可以接受数据.该参数表示不能调用send.但是如果还有数据在send buffer中,这些数据还是会被继续发送出去的.
SO_REUSEADDR和SO_REUSEPORT
最近,看到CSDN的linux版块,有人提问,说为什么server程序重启之后,无法连接,需要过一段时间才能连接上.我想对于这个问题,有两种可能:一种可能就是该server一直停留在TIME_WAIT状态.这个时候,需要等待2MSL的时间才能重新连接上,具体细节原因请见我的另一篇文章《linux 网络编程之TIME_WAIT状态》
SO_REUSEADDR和SO_REUSEPORT
最近,看到CSDN的linux版块,有人提问,说为什么server程序重启之后,无法连接,需要过一段时间才能连接上.我想对于这个问题,有两种可能:
一种可能就是__该server一直停留在TIME_WAIT状态__.这个时候,需要等待2MSL的时间才能重新连接上,具体细节原因请见我的另一篇文章《linux 网络编程之TIME_WAIT状态》
另一种可能就是SO_REUSEADDR参数设置问题.关于TIME_WAIT的我就不在这里重述了,这里我讲一讲SO_REUSEADDR.
SO_REUSEADDR允许一个server程序listen监听并bind到一个端口,既是这个端口已经被一个正在运行的连接使用了.
SO_REUSEADDR允许一个server程序listen监听并bind到一个端口,即使这个端口已经被一个正在运行的连接使用了.
我们一般会在下面这种情况中遇到:
一个监听(listen)server已经启动
当有client有连接请求的时候,server产生一个子进程去处理该client的事物.
server主进程终止了,但是子进程还在占用该连接处理client的事情.虽然进程终止了,但是由于子进程没有终止,该socket的引用计数不会为0所以该socket不会被关闭.
server程序重启.
* 一个监听(listen)server已经启动
* 当有client有连接请求的时候,server产生一个子进程去处理该client的事物.
* server主进程终止了,但是子进程还在占用该连接处理client的事情.虽然进程终止了,但是由于子进程没有终止,该socket的引用计数不会为0所以该socket不会被关闭.
* server程序重启.
默认情况下,server重启,调用socket,bind,然后listen,会失败.因为该端口正在被使用.如果设定SO_REUSEADDR,那么server重启才会成功.因此,所有的TCP server都必须设定此选项,用以应对server重启的现象.
默认情况下,server重启,调用socket,bind,然后listen,会失败.因为该端口正在被使用.如果设定SO_REUSEADDR,那么server重启才会成功.因此,__所有的TCP server都必须设定此选项,用以应对server重启的现象.__
SO_REUSEADDR允许同一个端口上绑定多个IP.只要这些IP不同.另外,还可以在绑定IP通配符.但是最好是先绑定确定的IP,最后绑定通配符IP.一面系统拒绝.简而言之,SO_REUSEADDR允许多个server绑定到同一个port上,只要这些server指定的IP不同,但是SO_REUSEADDR需要在bind调用之前就设定.在TCP中,不允许建立起一个已经存在的相同的IP和端口的连接.但是在UDP中,是允许的.
SO_REUSEADDR允许同一个端口上绑定多个IP.只要这些IP不同.另外,还可以在绑定IP通配符.但是最好是先绑定确定的IP,最后绑定通配符IP.以免系统拒绝.简而言之,SO_REUSEADDR允许多个server绑定到同一个port上,只要这些server指定的IP不同,__但是SO_REUSEADDR需要在bind调用之前就设定__.在TCP中,不允许建立起一个已经存在的相同的IP和端口的连接.但是在UDP中,是允许的.

View File

@@ -4,10 +4,9 @@ Creation-Date: 2011-06-04T14:36:51+08:00
====== Socket数据发送中信号SIGPIPE及相关errno的研究 ======
Created 星期六 04 六月 2011
Socket数据发送中信号SIGPIPE及相关errno的研究
好久没做过C开发了最近重操旧业。
听说另外一个项目组socket开发遇到问题发送端和接受端数据大小不一致。建议他们采用writen的重发机制以避免信号中断错误。采用后还是有问题。PM让我帮忙研究下。
UNP n年以前看过很久没做过底层开发手边也没有UNP vol1这本书所以做了个测试程序研究下实际可能发生的情况了。
原文地址 http://blog.chinaunix.net/u/31357/showart_242605.html
听说另外一个项目组socket开发遇到问题发送端和接受端数据大小不一致。建议他们采用writen的重发机制以避免信号中断错误。采用后还是有问题。PM让我帮忙研究下。UNP n年以前看过很久没做过底层开发手边也没有UNP vol1这本书所以做了个测试程序研究下实际可能发生的情况了。
测试环境AS3和redhat 9(缺省没有nc)
@@ -67,12 +66,9 @@ main(int argc, char **argv)
exit(0);
}
为了方便观察错误输出lib/writen.c也做了修改加了些日志
/* include writen */
#include "unp.h"
@@ -112,20 +108,14 @@ Writen(int fd, void *ptr, size_t nbytes)
}
client.c放在tcpclieserv目录下修改了Makefile增加了client.c的编译目标
client: client.c
${CC} ${CFLAGS} -o $@ $< ${LIBS}
接着就可以开始测试了。
测试1 忽略SIGPIPE信号writen之前对方关闭接受进程
测试1 忽略SIGPIPE信号writen之前对方关闭接受进程
本机服务端:
nc -l -p 30000
@@ -141,7 +131,8 @@ Already write 40960, left 0, errno=0
Begin send 40960 data
Begin Writen 40960
writen error: Broken pipe(32)
结论:可见write之前对方socket中断发送端write会返回-1,errno号为EPIPE(32)
结论:__可见write之前对方socket中断发送端write会返回-1,errno号为EPIPE(32)__
测试2 catch SIGPIPE信号writen之前对方关闭接受进程
修改客户端代码catch sigpipe信号
@@ -167,7 +158,7 @@ Begin send 40960 data
Begin Writen 40960
Signal is 13
writen error: Broken pipe(32)
结论:可见write之前对方socket中断发送端write时会先调用SIGPIPE响应函数然后write返回-1,errno号为EPIPE(32)
结论:__可见write之前对方socket中断发送端write时会先调用SIGPIPE响应函数然后write返回-1,errno号为EPIPE(32)__
测试3 writen过程中对方关闭接受进程
@@ -186,7 +177,7 @@ Already write 589821, left 3506179, errno=0
Begin Writen 3506179
writen error: Connection reset by peer(104)
结论:可见socket write中对方socket中断发送端write会先返回已经发送的字节数,再次write时返回-1,errno号为ECONNRESET(104)
结论:__可见socket write中对方socket中断发送端write会先返回已经发送的字节数,再次write时返回-1,errno号为ECONNRESET(104)__
为什么以上测试都是对方已经中断socket后发送端再次write结果会有所不同呢。从后来找到的UNP5.12,5.13能找到答案
@@ -203,6 +194,3 @@ If the process either catches the signal and returns from the signal handler, or
以上解释了测试12的现象,write一个已经接受到RST的socket系统内核会发送SIGPIPE给发送进程如果进程catch/ignore这个信号write都返回EPIPE错误.
因此,UNP建议应用根据需要处理SIGPIPE信号至少不要用系统缺省的处理方式处理这个信号系统缺省的处理方式是退出进程这样你的应用就很难查处处理进程为什么退出。
原文地址 http://blog.chinaunix.net/u/31357/showart_242605.html

View File

@@ -0,0 +1,182 @@
Content-Type: text/x-zim-wiki
Wiki-Format: zim 0.4
Creation-Date: 2012-10-23T16:27:29+08:00
====== pthreads - POSIX threads ======
Created Tuesday 23 October 2012
http://linux.die.net/man/7/pthreads
===== Name =====
pthreads - POSIX threads
===== Description =====
POSIX.1 specifies __a set of interfaces__ (functions, header files) for threaded programming commonly known as **POSIX threads, or Pthreads**. A single process can contain multiple threads, all of which are executing the same program. These threads share **the same global memory** (data and heap segments), but each thread has **its own stack** (automatic variables).
POSIX.1 also requires that threads share a range of other attributes (i.e., these attributes are __process-wide__ rather than per-thread):
- process ID
- parent process ID
- process group ID and session ID
- controlling terminal
- user and group IDs
- open file descriptors
- record locks (see fcntl(2))
- signal dispositions
- file mode creation mask (umask(2))
- current directory (chdir(2)) and root directory (chroot(2))
- interval timers (**setitimer**(2)) and POSIX timers (timer_create(2))
- nice value (setpriority(2))
- resource limits (setrlimit(2))
- measurements of the consumption of CPU time (times(2)) and resources (getrusage(2))
As well as the stack, POSIX.1 specifies that various other attributes are distinct for each thread, including:
- thread ID (the **pthread_t** data type)
- signal mask (**pthread_sigmask**(3))
- the errno variable
- alternate signal stack (__sigaltstack__(2))
- real-time scheduling policy and priority (sched_setscheduler(2) and sched_setparam(2))
The following Linux-specific features are also per-thread:
- capabilities (see capabilities(7))
- CPU affinity (sched_setaffinity(2))
===== Pthreads function return values =====
Most pthreads functions return **0 on success, and an error number of failure**. Note that the pthreads functions __do not set errno__. For each of the pthreads functions that can return an error, POSIX.1-2001 specifies that the function can never fail with the error EINTR.
===== Thread IDs =====
Each of the threads in a process has a unique thread identifier (stored in the type pthread_t). This identifier is returned to the caller of **pthread_create(3)**, and a thread can obtain its own thread identifier using **pthread_self(3)**. Thread IDs are only __guaranteed to be unique within a process__. A thread ID may be reused after a terminated thread has been joined, or a detached thread has terminated. In all pthreads functions that accept a thread ID as an argument, that ID by definition refers to a thread in the same process as the caller.
===== Thread-safe functions =====
A thread-safe function is one that can be safely (i.e., it will deliver the same results regardless of whether it is) called from multiple threads at the same time.
POSIX.1-2001 and POSIX.1-2008 require that all functions specified in the standard shall be thread-safe, __except__ for the following functions:
asctime()
basename()
catgets()
crypt()
ctermid() if passed a non-NULL argument
ctime()
........
===== Async-cancel-safe functions =====
An async-cancel-safe function is one that can **be safely called** in an application where asynchronous cancelability is enabled (see **pthread_setcancelstate**(3)).
Only the following functions are required to be async-cancel-safe by POSIX.1-2001 and POSIX.1-2008:
pthread_cancel()
pthread_setcancelstate()
pthread_setcanceltype()
===== Cancellation Points =====
POSIX.1 specifies that certain functions must, and certain other functions may, be cancellation points. If a thread is __cancelable__, its cancelability type is __deferred__, and a cancellation request is __pending__ for the thread, then the thread is canceled when it calls a function that is a cancellation point.
The following functions are required to be cancellation points by POSIX.1-2001 and/or POSIX.1-2008:
accept()
aio_suspend()
clock_nanosleep()
.........
The following functions __may be cancellation points__ according to POSIX.1-2001 and/or POSIX.1-2008:
access()
asctime()
..........
An implementation may also mark other functions not specified in the standard as cancellation points. In particular, an implementation is likely to mark any nonstandard function that may block as a cancellation point. (This includes most functions that can touch files.)
===== Compiling on Linux =====
On Linux, programs that use the Pthreads API should be compiled using cc __-pthread__.
===== Linux Implementations of POSIX Threads =====
Over time, two threading implementations have been provided by the GNU C library on Linux:
* LinuxThreads
This is the original Pthreads implementation. Since glibc 2.4, this implementation is **no longer supported**.
* NPTL (Native POSIX Threads Library)
This is the modern Pthreads implementation. By comparison with LinuxThreads, NPTL provides __closer conformance__ to the requirements of the POSIX.1 specification and better performance when creating large numbers of threads. NPTL is available since glibc 2.3.2, and requires features that are present in the Linux 2.6 kernel**futex和实时信号**.
Both of these are so-called __1:1 implementations__, meaning that each thread maps to a kernel scheduling entity. Both threading implementations employ the Linux clone(2) system call. In NPTL, thread synchronization primitives (mutexes, thread joining, etc.) are implemented using the Linux __futex__(2) system call.
==== LinuxThreads ====
The notable features of this implementation are the following:
- In addition to the main (initial) thread, and the threads that the program creates using pthread_create(3), the implementation creates __a "manager" thread__. This thread handles thread creation and termination. (Problems can result if this thread is inadvertently killed.注意,管理线程是函数库**内部自动生成**的,而不是指调用进程。)
- Signals are used internally by the implementation. On Linux 2.2 and later, **the first three real-time signals** are used (see also signal(7)). On older Linux kernels, SIGUSR1 and SIGUSR2 are used. Applications must avoid the use of whichever set of signals is employed by the implementation.
- Threads __do not__ share process IDs. (In effect, LinuxThreads threads are **implemented as processes** which share more information than usual, but which do not share a common process ID.) LinuxThreads threads (including the manager thread) are **visible as separate processes** using ps(1).
The LinuxThreads implementation deviates from the POSIX.1 specification in a number of ways, including the following:
下面是LinuxThreads实现的与POSIX.1规范__不符__的地方
- Calls to getpid(2) return a different value in each thread.
- Calls to getppid(2) in threads other than the main thread return **the process ID of the manager thread**; instead getppid(2) in these threads should return the same value as getppid(2) in the main thread. 这里的main thread指的是最开始的(initial)线程。
- When one thread creates a new child process using fork(2), any thread should be able to wait(2) on the child. However, the implementation only allows the thread that created the child to wait(2) on it.
- When a thread calls execve(2), all other threads are terminated (as required by POSIX.1). However, the resulting process has the same PID as the thread that called execve(2): it should have the same PID as the main thread.
- Threads do not share user and group IDs. This can cause complications with set-user-ID programs and can cause failures in Pthreads functions if an application changes its credentials using seteuid(2) or similar.
- Threads do not share a common session ID and process group ID.
- Threads do not share record locks created using fcntl(2).
- The information returned by times(2) and getrusage(2) is per-thread rather than process-wide.
- Threads do not share semaphore undo values (see semop(2)).
- Threads do not share interval timers.
- Threads do not share a common nice value.
- POSIX.1 distinguishes the notions of signals that are directed to the process as a whole and signals that are directed to individual threads. According to POSIX.1, __a process-directed signal__ (sent using kill(2), for example) should be handled by a single, arbitrarily selected thread within the process. LinuxThreads does not support the notion of process-directed signals: signals may only be sent to specific threads.
- Threads have distinct alternate signal stack settings. However, a new thread's alternate signal stack settings are copied from the thread that created it, so that the threads initially share an alternate signal stack. (A new thread should start with no alternate signal stack defined. If two threads handle signals on their shared alternate signal stack at the same time, unpredictable program failures are likely to occur.)
==== NPTL ====
With NPTL, all of the threads in a process are placed in **the same thread group**; all members of a thread group share the same PID. NPTL does not employ a manager thread. NPTL makes internal use of the first two real-time signals (see also signal(7)); these signals cannot be used in applications.
NPTL still has at least __one nonconformance__ with POSIX.1:
- Threads do not share a common nice value.
Some NPTL nonconformances only occur with older kernels:
- The information returned by times(2) and getrusage(2) is per-thread rather than process-wide (fixed in kernel 2.6.9).
- Threads do not share resource limits (fixed in kernel 2.6.10).
- Threads do not share interval timers (fixed in kernel 2.6.12).
- Only the __main thread__ is permitted to start a new session using setsid(2) (fixed in kernel 2.6.16).
- Only the main thread is permitted to make the process into a process group leader using setpgid(2) (fixed in kernel 2.6.16).
- Threads have distinct alternate signal stack settings. However, a new thread's alternate signal stack settings are copied from the thread that created it, so that the threads initially share an alternate signal stack (fixed in kernel 2.6.16).
Note the following further points about the NPTL implementation:
-
If the stack size soft resource limit (see the description of RLIMIT_STACK in setrlimit(2)) is set to a value other than unlimited, then this value defines the default stack size for new threads. To be effective, this limit must be set before the program is executed, perhaps using the ulimit -s shell built-in command (limit stacksize in the C shell).
===== Determining the Threading Implementation =====
Since glibc 2.3.2, the __getconf(1)__ command can be used to determine the system's threading implementation, for example:
bash$ getconf GNU_LIBPTHREAD_VERSION
NPTL 2.3.4
With older glibc versions, a command such as the following should be sufficient to determine the default threading implementation:
bash$ $( ldd /bin/ls | grep libc.so | awk '{print $3}' ) | \
egrep -i 'threads|nptl'
Native POSIX Threads Library by Ulrich Drepper et al
[geekard@kb310 ~]$ __/lib/libpthread.so.0__
Native POSIX Threads Library by Ulrich Drepper et al
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Forced unwind support included.
[geekard@kb310 ~]$
===== Selecting the Threading Implementation: LD_ASSUME_KERNEL =====
On systems with a glibc that supports both LinuxThreads and NPTL (i.e., glibc 2.3.x), the LD_ASSUME_KERNEL environment variable can be used to override the dynamic linker's default choice of threading implementation. **This variable tells the dynamic linker to assume that it is running on top of a particular kernel version.** __By specifying a kernel version that does not provide the support required by NPTL, we can force the use of LinuxThreads.__ (The most likely reason for doing this is to run a (broken) application that depends on some nonconformant behavior in LinuxThreads.) For example:
bash$ $( **LD_ASSUME_KERNEL=2.2.5** ldd /bin/ls | grep libc.so | \
awk '{print $3}' ) | egrep -i 'threads|ntpl'
linuxthreads-0.10 by Xavier Leroy
===== See Also =====
clone(2), futex(2), gettid(2), proc(5), futex(7), sigevent(7), signal(7),
and various Pthreads manual pages, for example: pthread_attr_init(3), pthread_atfork(3), pthread_cancel(3), pthread_cleanup_push(3), pthread_cond_signal(3), pthread_cond_wait(3), pthread_create(3), pthread_detach(3), pthread_equal(3), pthread_exit(3), pthread_key_create(3), pthread_kill(3), pthread_mutex_lock(3), pthread_mutex_unlock(3), pthread_once(3), pthread_setcancelstate(3), pthread_setcanceltype(3), pthread_setspecific(3), pthread_sigmask(3), pthread_sigqueue(3), and pthread_testcancel(3)
===== Referenced By =====
core(5), intro(3), pthread_attr_getdetachstate(3), pthread_attr_getscope(3), pthread_attr_getstackaddr(3), pthread_attr_setaffinity_np(3), pthread_attr_setguardsize(3), pthread_attr_setinheritsched(3), pthread_attr_setschedparam(3), pthread_attr_setschedpolicy(3), pthread_attr_setstack(3), pthread_attr_setstacksize(3), pthread_cleanup_push_defer_np(3), pthread_getattr_np(3), pthread_getcpuclockid(3), pthread_join(3), pthread_kill_other_threads_np(3), pthread_setaffinity_np(3), pthread_setconcurrency(3), pthread_setschedparam(3), pthread_setschedprio(3), pthread_tryjoin_np(3), pthread_yield(3), sem_overview(7), vfork(2), xfs_copy(8)

View File

@@ -6,7 +6,7 @@ Creation-Date: 2011-06-03T21:37:25+08:00
Created 星期五 03 六月 2011
http://blog.csdn.net/factor2000/archive/2009/02/23/3929816.aspx
此选项指定函数close对面向连接的协议如何操作如TCP。内核缺省close操作是立即返回如果有数据残留在套接口缓冲区中则系统将试着将这些数据发送给对方。
此选项指定函数close对面向连接的协议如何操作如TCP。内核缺省close操作是立即返回如果有数据残留在套接口缓冲区中则系统将试着将这些数据发送给对方。
SO_LINGER选项用来改变此缺省设置。使用如下结构
@@ -19,12 +19,13 @@ struct linger {
有下列三种情况:
1、设置 l_onoff为0则该选项关闭l_linger的值被忽略等于内核缺省情况**close调用会立即返回**给调用者,如果可能将会传输任何未发送的数据(调用进程立即结束但进程对应的TCP发送缓冲区中可能还有未发送完的数据所以TCP连接可能会延迟一段时间后关闭这个是正常的TIME_WAIT状态)
2、设置 l_onoff为非0l_linger为0则套接口关闭时TCP夭折连接TCP将丢弃保留在套接口发送缓冲区中的任何数据并发送一个__RST__给对方而不是通常的四分组终止序列这避免了**TIME_WAIT**状态;
3、设置 l_onoff 为非0l_linger为0套接口关闭时内核将拖延一段时间由l_linger决定。如果套接口缓冲区中仍残留数据**进程将处于睡眠状态(注意close调用不是立即返回)**,直 到a所有数据发送完且被对方确认之后进行正常的终止序列描述字访问计数为0b延迟时间到。此种情况下应用程序检查close的返回值是非常重要的如果在数据发送完并被确认前时间到(超时)close将返回EWOULDBLOCK错误且套接口发送缓冲区中的任何数据都丢失。close的成功返回仅告诉我们发送的数据和FIN已由对方TCP确认它并不能告诉我们对方应用进程是否已读了数据。如果套接口设为非阻塞的它将不等待close完成。
2、设置 l_onoff为非0l_linger为0套接口关闭时TCP夭折连接TCP将**丢弃保留**在套接口发送缓冲区中的任何数据并发送一个__RST__给对方而不是通常的四分组终止序列这避免了**TIME_WAIT**状态;
3、设置 l_onoff 为非0l_linger为非0当套接口关闭时内核将拖延一段时间由l_linger决定。如果套接口缓冲区中仍残留数据**进程将处于睡眠状态(注意close调用不是立即返回)**,直 到a所有数据发送完且__被对方确认__之后进行正常的终止序列描述字访问计数为0b延迟时间到。此种情况下应用程序__检查close的返回值__是非常重要的如果在数据发送完并被确认前时间到(超时)close将__返回EWOULDBLOCK错误__且套接口发送缓冲区中的任何数据都__丢失__。close的成功返回仅告诉我们发送的数据和FIN__已由对方TCP确认__它并不能告诉我们对方应用进程是否已读了数据。如果套接口设为非阻塞的它将不等待close完成。
注释l_linger的单位依赖于实现: 4.4BSD假设其单位是时钟滴答百分之一秒但Posix.1g规定单位为秒。
下面的代码是一个使用SO_LINGER选项的例子使用30秒的超时时限
#define TRUE 1
#define FALSE 0
@@ -42,15 +43,15 @@ z = setsockopt(s,
if ( z )
perror("setsockopt(2)");
下面的例子显示了如何设置SO_LINGER的值来中止套接口s上的当前连接
下面的例子显示了如何设置SO_LINGER的值来**中止套接口s上的当前连接**
#define TRUE 1
#define FALSE 0
int z; /* Status code */
int s; /* Socket s */
struct linger so_linger;
...
so_linger.l_onoff = TRUE;
so_linger.l_linger = 0;
__so_linger.l_onoff = TRUE;__
__so_linger.l_linger = 0;__
z = setsockopt(s,
SOL_SOCKET,
SO_LINGER,
@@ -60,45 +61,7 @@ if ( z )
perror("setsockopt(2)");
**close(s); /* Abort connection */**
在上面的这个例子中当调用close函数时套接口s会立即中止。__中止的语义是通过将超时值设置为0来实现的__。
正常情况下TCP收到不在已有连接中的数据(但不包括序号是将来收到的哪些)时会自动发送RST给对方应用进程是不知晓的。
但应用进程可以通过将l_linger设为0然后调用close的方法来异常终止(不是通常的四次握手而是通过RST)与对方的通信。
/********** WINDOWS **********/
/* 当连接中断时,需要延迟关闭(linger)以保证所有数据都被传输所以需要打开SO_LINGER这个选项
* //注大致意思就是说SO_LINGER选项用来设置当调用closesocket时是否马上关闭socket
* linger的结构在/usr/include/linux/socket.h中定义//注这个结构就是SetSocketOpt中的Data的数据结构
*  struct linger
*  {
*   int l_onoff;  /* Linger active */ //低字节0和非0用来表示是否延时关闭socket
*   int l_linger; /* How long to linger */ //高字节,延时的时间数,单位为秒
*  };
*  如果l_onoff为0则延迟关闭特性就被取消。
* 如果非零,则允许套接口延迟关闭; l_linger字段则指明延迟关闭的时间
*/
更具体的描述如下:
1、若设置了SO_LINGER亦即linger结构中的l_onoff域设为非零并设置了零超时间隔则closesocket()不被阻塞立即执行不论是否有排队数据未发送或未被确认。这种关闭方式称为“强制”或“失效”关闭因为套接口的虚电路立即被复位且丢失了未发送的数据。在远端的recv()调用将以WSAECONNRESET出错。
2、若设置了SO_LINGER并确定了非零的超时间隔则closesocket()调用阻塞进程直到所剩数据发送完毕或超时。这种关闭称为“优雅”或“从容”关闭。请注意如果套接口置为非阻塞且SO_LINGER设为非零超时则closesocket()调用将以WSAEWOULDBLOCK错误返回。
3、若在一个流类套接口上设置了SO_DONTLINGER也就是说将linger结构的l_onoff域设为零则closesocket()调用立即返回。但是如果可能排队的数据将在套接口关闭前发送。请注意在这种情况下WINDOWS套接口实现将在一段不确定的时间内保留套接口以及其他资源这对于想用所以套接口的应用程序来说有一定影响。
SO_DONTLINGER 若为真则SO_LINGER选项被禁止。
SO_LINGER延迟关闭连接 struct linger上面这两个选项影响close行为
选项 间隔 关闭方式 等待关闭与否
SO_DONTLINGER 不关心 优雅 否
SO_LINGER 零 强制 否
SO_LINGER 非零 优雅 是

View File

@@ -0,0 +1,72 @@
Content-Type: text/x-zim-wiki
Wiki-Format: zim 0.4
Creation-Date: 2012-10-23T10:13:38+08:00
====== 使用sendfile() 提升网络文件发送性能 ======
Created Tuesday 23 October 2012
http://hily.me/blog/2011/01/use-sendfile-accelerate-file-sending/
偶见一好文清楚地阐述了什么是零拷贝Zero Copy以及 sendfile 的由来,不复述下实感不快。
原文见http://www.linuxjournal.com/article/6345
文章中列出了我们平时通过网络发送文件时会用到的两个系统调用:
read(file, tmp_buf, len);
write(socket, tmp_buf, len);
调用过程示意图如下:
{{./1.jpg}}
在用户空间调用 read() 读取文件时发生两次内存拷贝:
* DMA引擎将文件读取到内核的文件缓冲区
* 调用返回用户空间时将内核的文件缓冲区的数据复制到用户空间的缓冲区
接着调用 write() 把数据写入 socket 时,又发生了两次内存拷贝:
* 将用户空间的缓冲区的数据复制到内核的 socket 缓冲区
* 将内核 socket 缓冲区的数据复制到网络协议引擎
也就是说,在整个文件发送的过程中,发生了四次内存拷贝。
然后,数据读取到用户空间后并没有做过任何加工处理,因此通过网络发送文件时,
根本没有必要把文件内容复制到用户空间。
于是引入了 mmap()
tmp_buf = mmap(file, len);
write(socket, tmp_buf, len);
调用过程示意图:
{{./2.jpg}}
* 调用 mmap() 时会将文件直接读取到内核缓冲区并把内核缓冲区直接__共享__到用户空间
* 调用 write() 时,直接将内核缓冲区的数据复制到 socket 缓冲区
这样一来,就少了用户空间和内核空间之间的内存复制了。这种方式会有个问题,当前进程
在调用 write() 时,另一个进程把文件清空了,程序就会报出 SIGBUS 类型错误。
Linux Kernel __2.1__ 引进了 sendfile(),只需要一个系统调用来实现文件发送。
sendfile(socket, file, len);
调用过程示意图:
{{./3.jpg}}
* 调用 sendfile() 时会直接在内核空间把文件读取到内核的文件缓冲区
* 将内核的文件缓冲区的数据复制到内核的 socket 缓冲区中
* 将内核的 socket 缓冲区的数据复制到网络协议引擎
从性能上看这种方式只是少了一个系统调用而已还是做了3次拷贝操作。
Linux Kernel __2.4__ 改进了 sendfile(),调用接口没有变化:
sendfile(socket, file, len);
调用过程示意图:
{{./4.jpg}}
* 调用 sendfile() 时会直接在内核空间把文件读取到内核的文件缓冲区
* 内核的 socket 缓冲区中保存的是当前要发送的数据在内核的文件缓冲区中的位置和偏移量
* DMA gather copy 将内核的文件缓冲区的数据复制到网络协议引擎
这样就只剩下2次拷贝啦。
在许多 http server 中,都引入了 sendfile 的机制,如 nginx、lighttpd 等,它们正是利用
sendfile() 这个特性来实现高性能的文件发送的。
EOF

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

View File

@@ -6,8 +6,6 @@ Creation-Date: 2011-10-07T18:42:52+08:00
Created Friday 07 October 2011
http://yibin.us/archives/6817
在一些python讨论版里经常会见到一些“月经帖”类似于“我用python读取一个文件乱码”然后就会抱怨python的编码很麻烦其实不是python编码难搞定而是没有真正理解python的编码。
如在windows环境下的以下示例代码
#!/usr/bin/env python
@@ -21,9 +19,10 @@ if __name__=='__main__':
do()
此时的ansi.txt编码为ansi,我们在cmd窗口执行看到如下结果
{{~/sync/notes/zim/python/python中的编码/1.png}}
{{./1.png}}
此时一切正常如果还是用上面的脚本去读取utf8.txt文件是utf8编码就会得到下面的结果
{{~/sync/notes/zim/python/python中的编码/2.png}}
{{./2.png}}
经典的“乱码”出现了有朋友可能会说了我在python脚本里指定编码应该就解决了于是
#!/usr/bin/env python
@@ -40,7 +39,7 @@ if __name__=='__main__':
do()
再次运行:
{{~/sync/notes/zim/python/python中的编码/3.png}}
{{./3.png}}
OMG还是乱码。。。。
能不能正常输出中文不取决于#coding=utf-8也不取决于目标文件的编码而是取决于你的__终端输出设备__这里就是CMD窗口CMD窗口是不支持UTF-8的它只支持__GBK__所以我们要转码。
@@ -64,7 +63,7 @@ if __name__=='__main__':
do()
结果:
{{~/sync/notes/zim/python/python中的编码/4.png}}
{{./4.png}}
正常输出。
做一个小结:

View File

@@ -122,13 +122,3 @@ Base64编码可用于在HTTP环境下传递较长的__标识信息__。例如
MIME::Base64 Perl module
Firefox extension
emacs函数
===== 参见 =====
Radix-64
ASCII85
Quoted-printable
uuencode
yEnc
8BITMIME
URL

View File

@@ -9,10 +9,13 @@ Created Friday 14 October 2011
Python 2.7.2 (default, Jun 29 2011, 11:17:09)
[GCC 4.6.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> '张俊' #python2 会自动将字符串转换为合适的编码字节字符串
>>> '张俊' #python2 会自动将字符串转换为合适编码的字节字符串
'\xe5\xbc\xa0\xe4\xbf\x8a' #自动转换为utf-8编码的字节字符串
>>> u'张俊' #显式指定字符串类型为unicode类型 此类型字符串没有编码保存的是字符在unicode**字符集中的代码点(序号)**
u'\u5f20\u4fca'
>>> '张俊'.encode('utf-8') #python2 已经自动将其转化为utf-8类型编码因此再次编码(python2会将该字符串当作用ascii或unicode编码过)会出现错误。
Traceback (most recent call last):
File "<stdin>", line 1, in <module>

View File

@@ -0,0 +1,7 @@
Content-Type: text/x-zim-wiki
Wiki-Format: zim 0.4
Creation-Date: 2012-10-04T13:20:28+08:00
====== python笔记 ======
Created Thursday 04 October 2012

View File

@@ -0,0 +1,40 @@
Content-Type: text/x-zim-wiki
Wiki-Format: zim 0.4
Creation-Date: 2012-10-04T13:21:05+08:00
====== dict ======
Created Thursday 04 October 2012
Help on class dict in module __builtin__:
class dict(object)
| dict() -> new empty dictionary
| dict(mapping) -> new dictionary initialized from **a mapping object**'s #mapping为__一个__pairs对象
| (key, value) pairs
| dict(iterable) -> new dictionary initialized as if via:
| d = {}
| for **k, v** in iterable: #迭代器对象每次返回的元素必须是一个容器类型__容器中元素的个数为2__.**如[a,b], "ab",(a,b)**
| d[k] = v
| dict(__**kwargs)__ -> new dictionary initialized with the name=value pairs
| in the keyword argument list. For example: dict(one=1, two=2)
|
| Methods defined here:
>>> dict(__[('sape', 4139), ('guido', 4127), ('jack', 4098)]__)
{'sape': 4139, 'jack': 4098, 'guido': 4127}
>>> dict([(x, x**2) for x in (2, 4, 6)]) # use a list comprehension
{2: 4, 4: 16, 6: 36}
>>> dict(sape=4139, guido=4127, jack=4098)
{'sape': 4139, 'jack': 4098, 'guido': 4127}
tel = {'jack': 4098, 'sape': 4139}
>>> dc=dict(["df","12"]);dc #["df","12"]为一科迭代对象每次返回的元素为两个字符的str所以可以被unpack给key,value
{'1': '2', 'd': 'f'}
>>> dc=dict(["df",__"123"__]);dc
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: dictionary update sequence element __#1 has length 3; 2 is required__
>>>

View File

@@ -0,0 +1,20 @@
Content-Type: text/x-zim-wiki
Wiki-Format: zim 0.4
Creation-Date: 2012-10-04T13:20:56+08:00
====== float ======
Created Thursday 04 October 2012
>>> float("0xff")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: invalid literal for float(): 0xff
>>>
>>> __float.fromhex("0xfff")__
4095.0
>>>
>>> float("0.111")
0.111
>>>

View File

@@ -0,0 +1,37 @@
Content-Type: text/x-zim-wiki
Wiki-Format: zim 0.4
Creation-Date: 2012-10-04T13:20:52+08:00
====== int long ======
Created Thursday 04 October 2012
>>> 1&2 #按__位与__
0
>>> 0xff&0xf1 #按位与
241
>>> 0xff&0xf0
240
>>> __hex__(0xff&0xf0) #返回的__字符串__
'0xf0'
__与hex()类似, bin(), oct()等返回的都是int或long型的字符串代表__
>>> 1&&2 __#python没有&&, ||, !逻辑运算符但是有and, or, not而且这三个逻辑运算符返回的是最后一个元素的内容__
File "<stdin>", line 1
1&&2
^
SyntaxError: invalid syntax
>>> 1 and 2 __#返回的是最后一个元素的内容而不是True或False这里为2__
2
>>> 'fff' & 'dfad' __#str类型没有定义__and__方法所以没有位运算__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for &: 'str' and 'str'
>>> help(str)
>>> 'fff' and 'dfad'
'dfad'
>>>

View File

@@ -0,0 +1,25 @@
Content-Type: text/x-zim-wiki
Wiki-Format: zim 0.4
Creation-Date: 2012-10-04T13:21:01+08:00
====== list ======
Created Thursday 04 October 2012
>>> l=range(1,10)
>>> l
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> __l[1:8:-1]__
[]
>>> l[8:1:-1]
[9, 8, 7, 6, 5, 4, 3]
>>> __l[::-1]__
[9, 8, 7, 6, 5, 4, 3, 2, 1]
>>> l[-1::-1]
[9, 8, 7, 6, 5, 4, 3, 2, 1]
>>> __l[-1:-9:-1]__
[9, 8, 7, 6, 5, 4, 3, 2]
>>> l[:]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> l[::]
[1, 2, 3, 4, 5, 6, 7, 8, 9]

View File

@@ -0,0 +1,18 @@
Content-Type: text/x-zim-wiki
Wiki-Format: zim 0.4
Creation-Date: 2012-10-04T13:21:08+08:00
====== set ======
Created Thursday 04 October 2012
>>> set1=set(1,2,3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: set expected __at most 1 arguments,__ got 3
>>> set1=set((1,2,3))
>>> set1
set([1, 2, 3])
>>>

View File

@@ -0,0 +1,40 @@
Content-Type: text/x-zim-wiki
Wiki-Format: zim 0.4
Creation-Date: 2012-10-04T13:20:41+08:00
====== str ======
Created Thursday 04 October 2012
| join(...)
| S.join(iterable) -> string
|
| Return a string which is the concatenation of __the strings in the__
__ | iterable__. The separator between elements is S.
iterable迭代器对象每次返回的__必须是字符串对象__。
>>> ":".join("abcd")
'a:b:c:d'
>>> ":".join(['a','b','c','d'])
'a:b:c:d'
>>> ":".join(['a',__123__,'c'])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: sequence item 1: __expected string__, int found
>>> ":".join(['a',['ab'],'c'])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: sequence item 1: expected string, list found
>>>
| rsplit(...)
| S.rsplit([sep [,maxsplit]]) -> list of strings
|
| Return a list of the words in the string S, using sep as the
| delimiter string, starting at the end of the string and working
| to the front. If maxsplit is given, at most maxsplit splits are
| done. If sep is not specified or is __None__, any whitespace string
| is a separator.

View File

@@ -0,0 +1,38 @@
Content-Type: text/x-zim-wiki
Wiki-Format: zim 0.4
Creation-Date: 2012-10-04T13:47:32+08:00
====== unpack ======
Created Thursday 04 October 2012
>>> for k,v in ["fdf",23,"dfdf",33]:
... print k,v
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: __too many__ values to unpack
顺序容器类型如str, list, tuple__每次迭代时只能返回其中的一个元素__。
所以第一次返回循环返回**"fdf"**,但是它有三个元素最多只能赋值给两个
变量。
>>> for k,v in "dfdf":
... print k,v
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: __need more than 1 value__ to unpack
字符串迭代时每次返回其中的一个字符。所以最多只能unpack给一个变量。
>>> k,v="dfdf"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: __too many values to unpack__
unpack一个顺序容器类型时左边变量的数目必须要与容器中元素的个数相同。
>>> k,v="df"
>>> print k,v
d f
>>>

View File

@@ -0,0 +1,7 @@
Content-Type: text/x-zim-wiki
Wiki-Format: zim 0.4
Creation-Date: 2012-10-04T13:21:30+08:00
====== 内置函数 ======
Created Thursday 04 October 2012

View File

@@ -0,0 +1,121 @@
Content-Type: text/x-zim-wiki
Wiki-Format: zim 0.4
Creation-Date: 2012-10-04T22:01:26+08:00
====== 编写 Unix 管道风格的 Python 代码 ======
Created Thursday 04 October 2012
http://www.oschina.net/question/54100_11910
先推荐一份幻灯片David Beazley ("Python essiential reference", PLY 的作者) 在 PyCon2008 上报告的幻灯片,强烈推荐!!这篇文章的很多内容都来自或者受这份幻灯片的启发而来。
在上一篇文章里介绍了 Unix 管道的好处,那可不可以在写程序时也使用这样的思想呢?当然可以。看过 SICP 就知道,其实函数式编程中的 __map, filter__ 都可以看作是管道思想的应用。但其实管道的思想不仅可以在函数式语言中使用,只要语言支持定义函数,有能够存放一组数据的数据结构,就可以使用管道的思 想。
一个日志处理任务
这里直接以前面推荐的幻灯片里的例子来说明,应用场景如下:
某个目录及子目录下有一些 web 服务器的日志文件,日志文件名以 access-log 开头
日志格式如下
81.107.39.38 - ... "GET /ply/ply.html HTTP/1.1" 200 97238
81.107.39.38 - ... "GET /ply HTTP/1.1" 304 -
其中最后一列数字为发送的字节数,若为 - 则表示没有发送数据
目标是算出总共发送了多少字节的数据,实际上也就是要把日志记录的没一行的最后一列数值加起来
我不直接展示如何用 Unix 管道的风格来处理这个问题,而是先给出一些“不那么好”的代码,指出它们的问题,最后再展示管道风格的代码,并介绍如何使用 generator 来避免效率上的问题。想直接看管道风格的,点这里。
问题并不复杂,几个 for 循环就能搞定:
sum = 0
for path, dirlist, filelist in __os.walk(top)__:
for name in __fnmatch.filter__(filelist, "access-log*"):
# 对子目录中的每个日志文件进行处理
with open(name) as f:
for line in f:
if line[-1] == '-':
continue
else:
sum += int(line__.rsplit(None, 1)__[1])
利用 os.walk 这个问题解决起来很方便,由此也可以看出 python 的 for 语句做遍历是多么的方便,不需要额外控制循环次数的变量,省去了设置初始值、更新、判断循环结束条件等工作,相比 C/C++/Java 这样的语言真是太方便了。看起来一切都很美好。
然而,设想以后有了新的统计任务,比如:
1. 统计某个特定页面的访问次数
2. 处理另外的一些日志文件,日志文件名字以 error-log 开头
完成这些任务直接拿上面的代码过来改改就可以了,文件名的 pattern 改一下,处理每个文件的代码改一下。其实每次任务的处理中,找到特定名字为特定 pattern 的文件的代码是一样的,直接修改之前的代码其实就引入了重复。
如果重复的代码量很大,我们很自然的会注意到。然而 python 的 for 循环实在太方便了像这里找文件的代码一共就两行哪怕重写一遍也不会觉得太麻烦。for 循环的方便使得我们会忽略这样简单代码的重复。然而再怎么方便好用for 循环无法重用,只有把它放到函数中才能进行重用。
(先考虑下是你会如何避免这里的代码的重复。下面马上出现的代码并不好,是“误导性”的代码,我会在之后再给出“更好”的代码。)
因此我们__把上面代码中不变的部分提取成一个通用的函数可变的部分以参数的形式传入__得到下面的代码。
def generic_process(topdir, filepat, processfunc):
for path, dirlist, filelist in os.walk(top):
for name in fnmatch.filter(filelist, filepat):
with open(name) f:
processfunc(f)
sum = 0
# 很遗憾python 对 closure 中的变量不能进行赋值操作,
# 因此这里只能使用全局变量
def add_count(f):
global sum
for line in f:
if line[-1] == '-':
continue
else:
sum += int(line.rsplit(None, 1)[1])
generic_process('logdir', 'access-log*', add_count)
看起来不变和可变的部分分开了,然而 generic_process 的设计并不好。它除了寻找文件以外还调用了日志文件处理函数,因此在其他任务中很可能就无法使用。另外 add_count 的参数必须是 file like object因此测试时不能简单的直接使用字符串。
===== 管道风格的程序 =====
下面考虑用 Unix 的工具和管道我们会如何完成这个任务:
find logdir -name "access-log*" | \
xargs cat | \
grep '[^-]$' | \
awk '{ total += $NF } END { print total }'
find 根据文件名 pattern 找到文件cat 把所有文件内容合并输出到 stdoutgrep 从 stdin 读入,过滤掉行末为 - 的行awk 提取每行最后一列,将数值相加,最后打印出结果。(省掉 cat 是可以的,但这样一来 grep 就需要直接读文件而不是只从标准输入读。)
我们可以在 python 代码中模拟这些工具__Unix 的工具通过文本来传递结果,在 python 中可以使用 list__。
def find(topdir, filepat, processfunc):
files = []
for path, dirlist, filelist in os.walk(top):
for name in fnmatch.filter(filelist, filepat):
files.append(name)
return files
def cat(files):
lines = []
for file in files:
with open(file) as f:
for line in f:
lines.append(line)
return lines
def grep(pattern, lines):
result = []
import re
pat = re.compile(pattern)
for line in lines:
if pat.search(line):
result.append(line)
resurn result
lines = grep('[^-]$', cat(find('logdir', 'access-log*')))
col = (line.rsplit(None, 1)[1] for line in lines)
print sum(int(c) for c in col)
有了 find, cat, grep 这三个函数,只需要连续调用就可以像 Unix 的管道一样将这些函数组合起来。数据在管道中的变化如下图(简洁起见,过滤器直接标在箭头上
{{./1.gif}}
看起来现在的代码行数比最初直接用 for 循环的代码要多,但现在的代码就像 Unix 的那些小工具一样,每一个都更加可能被用到。我们可以把更多常用的 Unix 工具用 Python 来模拟,从而在 Python 代码中以 Unix 管道的风格来编写代码。
不过上面的代码性能很差,多个临时的 list 被创建。解决的办法是用 generator因为篇幅比较长具体做法放到下一篇文章中。

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@@ -177,7 +177,7 @@ This manual is meant as a brief introduction to features found in Bash. The Bash
===== 1 Introduction =====
* What is Bash?: A short description of Bash.
* What is Bash? :A short description of Bash.
* What is a shell?: A brief introduction to shells.
===== 1.1 What is Bash[bæʃ]? =====
@@ -275,9 +275,9 @@ This chapter briefly summarizes the shell's `__building blocks__': commands, con
When the shell reads input, it proceeds through a sequence of operations. If the input indicates the beginning of a comment, the shell ignores the comment symbol (#), and the rest of that line.
Otherwise, roughly speaking, the shell reads its input and divides the input into __words and operators__, employing the __quoting rules __to select which meanings to assign various words and characters.
Otherwise, roughly speaking, the shell reads its input and divides the input into __wordswords是一组连续的字符用metacharacter分割。 and operators(具有特定功能的字符序列由control operator和重定向符组成)__, employing the __quoting rules __to select which meanings to assign various words and characters.
The shell then parses these tokens into commands and other constructs, removes the **special meaning** of certain words or characters, expands others, redirects input and output as needed, executes the specified command, waits for the command's exit status, and makes that exit status available for further inspection or processing.
The shell then parses these tokens(由word和operator组成。) into commands and other constructs, removes the **special meaning** of certain words or characters, expands others, redirects input and output as needed, executes the specified command, waits for the command's exit status, and makes that exit status available for further inspection or processing.
==== 3.1.1 Shell Operation ====
@@ -299,7 +299,7 @@ The following is a brief description of the shell's operation when it reads and
* ANSI-C Quoting: How to expand ANSI-C sequences in quoted strings.
* Locale Translation: How to translate strings into different languages.
Quoting is used to __remove the special meaning of certain characters or words__ to the shell. Quoting can be used to disable special treatment for special characters, to prevent reserved words from being recognized as such, and to prevent parameter expansion.
Quoting is used to __remove the special meaning of certain characters or words__ to the shell. Quoting can be used to disable special treatment for special characters, to prevent reserved words from being recognized as such, and to prevent parameter expansion.
Each of the shell metacharacters (see Definitions) has special meaning to the shell and must be quoted if it is to__ represent itself__. When the command history expansion facilities are being used (see History Interaction), the history expansion character, usually__ !__, must be quoted to prevent history expansion. See Bash History Facilities, for more details concerning history expansion.
@@ -312,7 +312,8 @@ A non-quoted backslash \ is the Bash escape character. It preserves the __
=== 3.1.2.2 Single Quotes(单引号中的任何字符都无特殊含义,因此单引号中的\'是无效的) ===
Enclosing characters in single quotes (') preserves the__ literal value of each character__ within the quotes. A single quote may **not occur **between single quotes, even when preceded by a backslash.单引号中的\无特殊含义。
Enclosing characters in single quotes (') preserves the__ literal value of each character__ within the quotes. A single quote may **not occur **between single quotes, even when preceded by a backslash.
单引号中的\无特殊含义。
=== 3.1.2.3 Double Quotes(除$ ` \和!外,其它任何字符都无特殊含义。其中\只有当后接$ ` \ 或换行时才有特殊含义,否则为正常字符) ===
@@ -320,9 +321,7 @@ Enclosing characters in double quotes (") preserves the literal value of a
The backslash retains its special meaning__ only when followed by one of the following characters: $, `, ", \, or newline__. Within double quotes, backslashes that are followed by one of these characters are **removed**. Backslashes preceding characters without a special meaning are** left unmodified**. A double quote may be quoted within double quotes by preceding it with a backslash. If enabled, history expansion will be performed unless an ! appearing in double quotes is escaped using a backslash. The backslash preceding the ! is not removed.
单独列出的
===== 特殊规则 =====
=== 特殊规则 ===
__The special parameters * and @ have special meaning when in double quotes__ (see Shell Parameter Expansion).
@@ -369,7 +368,7 @@ __The expanded result is single-quoted__, as if the dollar sign had not been pre
[geekard@geekard ~]$ set |grep IFS
__IFS=$' \t\n'__
[geekard@geekard ~]$
原来是因为值中含有ANSI-C的转义字符所以使用了$'...'形式。
原来是因为值中含有ANSI-C的转义字符所以使用了 **$'...' **形式。
[geekard@geekard ~]$ echo ' \t\n' #正常情况下单引号中的内容**无任何特殊含义**,所以,\t\n并__不是__转义字符。
\t\n
[geekard@geekard ~]$ echo $' \t\n' #但是,如果使用$' \t\n'形式bash将其解释为__含有转义字符序列的特殊字符串__
@@ -385,7 +384,7 @@ cd
=== 3.1.2.5 Locale-Specific Translation ===
A double-quoted string preceded by a dollar sign ($) will cause the string to be translated according to the c**urrent locale**. If the current locale is__ C or POSIX__, the dollar sign is ignored. If the string is translated and replaced, the replacement is **double-quoted**.
A double-quoted string preceded by a dollar sign ($) will cause the string to be translated according to the **current locale**. If the current locale is__ C or POSIX__, the dollar sign is ignored. If the string is translated and replaced, the replacement is **double-quoted**.
Some systems use the message catalog selected by the LC_MESSAGES shell variable. Others create the name of the message catalog from the value of the TEXTDOMAIN shell variable, possibly adding a suffix of .mo. If you use the TEXTDOMAIN variable, you may need to set the TEXTDOMAINDIR variable to the location of the message catalog files. Still others use both variables in this fashion: TEXTDOMAINDIR/LC_MESSAGES/LC_MESSAGES/TEXTDOMAIN.mo.
@@ -407,32 +406,31 @@ More complex shell commands are composed of simple commands __arranged together
* Compound Commands: Shell commands for control flow.
* Coprocesses: Two-way communication between commands.
最后四个都是将简单命令组合为复杂命令的方法它们__对外是一个整体__可以被重定向。
3.2.1 Simple Commands
==== 3.2.1 Simple Commands ====
A simple command is the kind of command encountered most often. It's just a** sequence of words separated by **__blanks__**, terminated by one of the shell's **__control operators__ (see Definitions). The first word generally specifies a command to be executed, with the rest of the words being that command's arguments.
The return status (see Exit Status) of a simple command is its exit status as provided by the posix 1003.1 waitpid function, or __128+n__ if the command was terminated by **signal n**.
==== 3.2.2 Pipelines ====
A pipeline is a sequence of simple commands separated by one of the control operators | or |&.
The format for a pipeline is
[time [-p]] [!] command1 [ [| or |&] command2 ...]
[time [-p]] [!] command1 [ [| or __|& __] command2 ...]
The output(一般是标准输出) of each command in the pipeline is connected via a pipe to the input of the next command. That is, each command reads the previous command's output. This connection is performed before any redirections specified by the command. 管道在任何重定向操作之前完成。
The output (一般是标准输出) of each command in the pipeline is connected via a pipe to the input of the next command. That is, each command reads the previous command's output. This connection is performed before any redirections specified by the command. 管道在任何重定向操作之前完成。
If |& is used, the __standard error__ of command1 is connected to command2's standard input through the pipe; it is shorthand for __2>&1 |__. This implicit redirection of the standard error is performed after any redirections specified by the command.
The reserved word time causes timing statistics to be printed for the pipeline once it finishes. The statistics currently consist of __elapsed (wall-clock) time and user and system time__ consumed by the command's execution. The -p option changes the output format to that specified by posix. The TIMEFORMAT variable may be set to a format string that specifies how the timing information should be displayed. See Bash Variables, for a description of the available formats. The use of time as a reserved word permits the timing of shell builtins, shell functions, and pipelines. An external time command cannot time these easily.
The reserved word __time__ causes timing statistics to be printed for the pipeline once it finishes. The statistics currently consist of __elapsed (wall-clock) time and user and system time__ consumed by the command's execution. The -p option changes the output format to that specified by posix. The TIMEFORMAT variable may be set to a format string that specifies how the timing information should be displayed. See Bash Variables, for a description of the available formats. The use of time as a reserved word permits the timing of shell builtins, shell functions, and pipelines. An external time command cannot time these easily.
If the pipeline is not executed asynchronously (see Lists), the shell waits for all commands in the pipeline to complete.
Each command in a pipeline is executed __in its own subshell __(see Command Execution Environment). The exit status of a pipeline is the exit status of the__ last command__ in the pipeline, unless the** pipefail option** is enabled (see The Set Builtin). If pipefail is enabled, the pipeline's return status is the value of the last (rightmost) command to exit with a non-zero status, or zero if all commands exit successfully. If the reserved word ! precedes the pipeline, the exit status is the logical negation of the exit status as described above. The shell waits for all commands in the pipeline to terminate before returning a value.
管道线中各命令都在**各自的subshell**中执行,只有当最后一个命令执行完毕时,整个命令才结束,同时整个管道线命令的退出值为**最后一个命令**的退出值(而非中间某个命令的退出值)。如果打开了pipefail选项则管道线中任何一个命令执行失败(退出非0),则整个管道线退出。
管道线中各命令都在__各自__**的subshell**中执行,只有当最后一个命令执行完毕时(不管中间的命令是否执行成功),整个命令才结束,同时整个管道线命令的退出值为**最后一个命令**的退出值(而非中间某个命令的退出值)。如果打开了pipefail选项则管道线中任何一个命令执行失败(退出非0),则整个管道线退出。
==== 3.2.3 Lists of Commands ====
@@ -506,7 +504,7 @@ Note that wherever a ; appears in the description of a command's syntax, i
An alternate form of the for command is also supported: 借鉴了C的语法规则
for __(( expr1 ; expr2 ; expr3 )) __; do commands ; done
#这里的expr值得是算术表达式。
First, the arithmetic expression expr1 is evaluated according to the rules described below (see Shell Arithmetic). The arithmetic expression expr2 is then evaluated repeatedly __until it evaluates to zero__. Each time expr2 evaluates to a non-zero value, commands are executed and the arithmetic expression expr3 is evaluated. If any expression is omitted, it behaves as if it evaluates to 1. The return value is the exit status of the last command in list that is executed, or false if any of the expressions is invalid.
The** break** and **continue** builtins (see Bourne Shell Builtins) may be used to control loop execution.
@@ -532,7 +530,7 @@ The** break** and **continue** builtins (see Bourne Shell Builtins) may be used
case will selectively execute the command-list corresponding to the **first** pattern that matches word. If the shell option nocasematch (see the description of shopt in The Shopt Builtin) is enabled, the match is performed without regard to the case of alphabetic characters. The __|__ is used to separate multiple patterns, and the ) operator** terminates a pattern list**. A list of patterns and an associated command-list is known as a__ clause__.
Each clause must be terminated with **;;, ;&, or ;;&.** The word undergoes tilde expansion, parameter expansion, command substitution, arithmetic expansion, and quote removal **before** matching is attempted. Each pattern undergoes tilde expansion, parameter expansion, command substitution, and arithmetic expansion.
pattern使用的是bash的匹配模式多个pattern只能使用|相连,表示**或关系**__。bash会对模式和command-list中的内容进行“~”扩展、参数扩展、命令替换和算术扩展__。
pattern使用的是__bash的匹配模式__多个pattern只能使用|相连,表示**或关系**__。bash会对模式和command-list中的内容进行“~”扩展、参数扩展、命令替换和算术扩展__。
There may be an arbitrary number of case clauses, each terminated by a ;;, ;&, or ;;&. The first pattern that matches determines the command-list that is executed.
@@ -551,7 +549,6 @@ pattern使用的是bash的匹配模式多个pattern只能使用|相连,表
If the ;; operator is used, no subsequent matches are attempted after the first pattern match. Using __;&__ in place of ;; causes execution to continue with the command-list associated with the **next clause**, if any. Using ;;& in place of ;; causes the shell to __test__ the patterns in the next clause, if any, and execute any associated command-list on a successful match.
使用;;终止符号时bash只会执行其前面的command-list然后退出case结构使用;&结构时bash还会__接着执行下一个__pattern对应的commandlist使用;;&时bash会__先判断__下一个pattern是否匹配若是就执行对应的commandlist。
The return status is zero if no pattern is matched. Otherwise, the return status is the exit status of the command-list executed.
* select
The select construct allows the __easy generation of menus__. It has almost the same syntax as the for command:
@@ -573,9 +570,9 @@ The **PS3 **prompt is then displayed and a line is read from the standard input.
done
* ((...))
* ((...)) #会被shell当作命令来执行返回结果为true或false。
(( expression ))
(( expression )) #expression必须为算术表达式不能是命令不会被扩展。
The__ arithmetic expression__ is evaluated according to the rules described below (see Shell Arithmetic). If the value of the expression is non-zero, the return status is 0; otherwise the return status is 1. This is exactly equivalent to
@@ -587,7 +584,7 @@ The **PS3 **prompt is then displayed and a line is read from the standard input.
Return a status of 0 or 1 depending on the evaluation of the __conditional expression__ **expression**. Expressions are composed of the primaries described below in Bash Conditional Expressions. __Word splitting and filename expansion are not performed __on the words between the [ [ and ] ]; tilde expansion, parameter and variable expansion, arithmetic expansion, command substitution, process substitution, and **quote removal** are performed. Conditional operators such as -f must be unquoted to be recognized as primaries.
When used with [[, The __<__** and **__>__ operators __sort lexicographically__ using the current locale.
When used with [[, The __<__** and **__>__ operators __sort lexicographically(对字符串而言,不能用于数字)__ using the current locale.
When the__ == and !=__ operators are used, the string to the right of the operator is __considered a pattern__ and matched according to the rules described below in Pattern Matching. If the shell option nocasematch (see the description of shopt in The Shopt Builtin) is enabled, the match is performed without regard to the case of alphabetic characters. The return value is 0 if the string matches (==) or does not match (!=)the pattern, and 1 otherwise. Any part of the pattern may be quoted to force it to be matched as a string.
@@ -606,6 +603,7 @@ The **PS3 **prompt is then displayed and a line is read from the standard input.
The && and || operators do not evaluate expression2 if the value of expression1 is sufficient to determine the return value of the entire conditional expression.
对于[...]或test命令而言其中不能有&&或||操作符,它们必须要放在[..]或test的外面但是[[可以在内部使用它们。
=== 3.2.4.3 Grouping Commands ===
@@ -613,9 +611,9 @@ Bash provides two ways to group a list of commands to be __executed as a unit__.
()
( list ) #在一个__子shell中__执行list中的命令。括号是运算符因此它与list之间可以没有空格。
( list ) #在一个__子shell中__执行list中的命令。括号是运算符因此它与list之间可以没有空格。
Placing a list of commands between parentheses causes __a subshell environment __to be created (see Command Execution Environment), and each of the commands in list to be executed in **that subshell**. Since the list is executed in a subshell, variable assignments do not remain in effect after the subshell completes.
Placing a list of commands between parentheses causes __a subshell environment __to be created (see Command Execution Environment), and each of the commands in list to be executed in **that subshell(正常情况下list中的每个命令都是在不同的subshell中执行的。)**. Since the list is executed in a subshell, variable assignments do not remain in effect after the subshell completes.
{}
{ list__; __} #在__当前shell__中执行list中的命令注意__最后的分号不可少。大括号是关键字因此它与list之间必须要有空格。__
@@ -632,7 +630,7 @@ A coprocess is a shell command preceded by the __coproc reserved word__. A copro
The format for a coprocess is:
coproc [NAME] **com{** #注意是**单条命令**,如果使用合命令则必须用{}引住。
coproc [NAME] **com{ } ** #注意是**单条命令**,如果使用合命令则必须用{}引住。
This creates a coprocess named NAME. If NAME is not supplied, the default name is __COPROC__. NAME must not be supplied if command is a simple command (see Simple Commands); otherwise, it is interpreted as the first word of the simple command.
@@ -1335,7 +1333,8 @@ The exit status of an executed command is the value returned by the **waitpid**
For the shell's purposes, a command which exits with __a zero exit status has succeeded__. A non-zero exit status indicates failure. This seemingly counter-intuitive scheme is used so there is one well-defined way to indicate success and a variety of ways to indicate various failure modes. When a command __terminates on a fatal signal whose number is N, Bash uses the value 128+N as the exit status__.
* If a command is not found, the child process created to execute it returns a status of __127__. If a command is found but is not executable, the return status is __126__.
* If a command is not found, the child process created to execute it returns a status of
* If a command is found but is not executable, the return status is
* If a command fails because of an error **during expansion or redirection**, the exit status is greater than zero.
* The exit status is used by the Bash __conditional commands__ (see Conditional Constructs) and some of the __list constructs__ (see Lists).

View File

@@ -34,8 +34,6 @@ ls: cannot access thisfiledoesntexist: No such file or directory #coproc的出
#let the output of the coprocess go to stdout
$ __{ coproc mycoproc { awk '{print "foo" $0;fflush()}' ;} >&3 ;} 3>&1__
{....} 分组命令代表在**当前shell中**执行其中的命令所以3>&1表示将{...}中使用的3描述符与__当前shell__的1描述符相连接。
而 {...}中的>&3是在coproc与当前shell的pipe建立之后执行的其标准输出的描述符其环境中的3相连后实际上是与当前shell的标准输出相连。

View File

@@ -113,7 +113,6 @@ What happens if someone kills your script while critical-section is running? The
if [ ! -e $lockfile ]; then
**trap "rm -f $lockfile; exit" INT TERM EXIT #在捕获信号前需安装信号及其执行的命令**
~~ #上述命令有一个bug存在重复触发的死循环。~~
touch $lockfile
critical-section
rm $lockfile

View File

@@ -57,7 +57,7 @@ exit 0
**********竞争处理
if ( __set -o noclobber__; echo "$$" > "$lockfile") 2> /dev/null; #如果lockfile存在则**含有重定向的命令**出错返回
then
trap 'rm -f "$lockfile"; exit $?' INT TERM EXIT
trap 'rm -f "$lockfile"; __exit $?__' INT TERM EXIT
critical-section
rm -f "$lockfile"
trap __-__ INT TERM EXIT
@@ -69,7 +69,6 @@ fi
if [ ! -e $lockfile ]; then
**trap "rm -f $lockfile; exit" INT TERM EXIT #在捕获信号前需安装信号及其执行的命令**
#上述命令有一个bug存在重复触发的死循环。
touch $lockfile
critical-section
rm $lockfile
@@ -78,7 +77,7 @@ else
echo "critical-section is already running"
fi
****************Counted loops
****************Counted loops---------------
# Three expression for loop:
for__ (( i = 0; i < 20; i++ ))__
do

View File

@@ -28,7 +28,7 @@ Created Friday 23 December 2011
**bar,** i=
[geekard@geekard ~]$ echo ${i:=bar}, i=$i #如果i变量未初始化或初始化为空则参数扩展为__字符串bar__i变量__设置为字符串bar__。
**bar**, i=**bar**
[geekard@geekard ~]$ unset i; echo ${i:?bar}, i=$i; date #如果变量i未初始化或初始化为空则显示**i: =bar**同时__停止当前命令和后续命令的执行__。
[geekard@geekard ~]$ unset i; echo ${i:?bar}, i=$i; date #如果变量i未初始化或初始化为空则显示**i: bar**同时__停止当前命令和后续命令的执行__。
bash:** i: bar**
[geekard@geekard ~]$ echo ${i:+bar}, i=$i #如果变量i存在且不为空则参数扩展为字符串__bar__的值否则扩展为__空值__。
, i=

View File

@@ -97,14 +97,14 @@ done
注意:
1. <$pipe放在while的里或外面是有区别的前者每次loop都重新打开$pipe文件后者在整个loop中只打开文件一次。
1. <$pipe放在while的里或外面是有区别的前者每次loop都重新打开$pipe文件后者在整个loop中只打开文件一次。
2. __bash对pipe是行缓冲__。
-----------------------------
You should __explicitly exit from the trap command__, otherwise the script will continue running past it. You should also catch a few other signals.
So: trap "rm -f $pipe" EXIT
Becomes: trap "rm -f $pipe; exit" INT TERM EXIT
Becomes: trap "rm -f $pipe; exit" INT TERM EXIT
Excellent article. Thank you!
----------
@@ -113,4 +113,3 @@ Not really necessary in this case
__The EXIT trap gets executed when the shell exits regardless of why it exits so trapping INT and TERM aren't really necessary in this case.__
However, your point about "exit" is good: trapping a signal removes the default action that occurs when a signal occurs, so if the default action is to exit the program and you want that to happen in addition to executing your code, you need to include an exit statement in your code.

View File

@@ -67,12 +67,10 @@ dfs! s
[geekard@geekard ~]$
*********** 字符串中若含有空格、换行、TAB键则必须要用引号包围
[geekard@geekard ~]$ echo "dfd\df"
dfd\df
[geekard@geekard ~]$ echo 'df #字符串中可以包含换行但是C语言的字符串中不行(必须要使用转义字符)。
[geekard@geekard ~]$ echo 'df #__Shell字符串中可以包含换行__但是C语言的字符串中不行(必须要使用转义字符)。
> df'
df
df

View File

@@ -48,7 +48,8 @@ http://molinux.blog.51cto.com/2536040/469296
4
[root@localhost ~]#
[root@localhost ~]# A[0]=9 [root@localhost ~]# A[10]=1
[root@localhost ~]# A[0]=9
[root@localhost ~]# A[10]=1
[root@localhost ~]# echo ${A[*]}
9 1
[root@localhost ~]# echo ${#A[*]}

View File

@@ -8,7 +8,7 @@ Created Thursday 22 December 2011
===== 条件测试命令: =====
* test 和[ 是**等价**的条件测试命令都可以单独执行但__无输出值只有退出状态0表示测试为真非0表示测试为假。__
* 为**关键字**而非命令,是[ ]的**增强版本**,里面还可以用&&、||、<、>等逻辑和关系运算符但__不能有算术运算符__类似语言的语法~~因此返回值0表示测试为假返回值1表示测试为真这与test和[的返回状态值意义恰好相反,~~一般只用在条件测试中。
* [ [ 和 ] ] 为**关键字**而非命令,是[ ]的**增强版本**,里面还可以用&&、||、<、>等逻辑和关系运算符但__不能有算术运算符__类似语言的语法,一般只用在条件测试中。
* __((...))比较特殊先对__**算术、关系、逻辑表达式**__计算(求值),如果结果非空或非零,则返回状态为真(0),否则返回假(1);注意:没有输出值,只有退出状态值。__
@@ -16,7 +16,7 @@ Created Thursday 22 December 2011
==== 注意: ====
* 前三个是__专门用来做条件测试__的(因为它们作为命令执行时,没有输出值)而最后一个是shell用于__命令替换的语法__。
* 前三个是__专门用来做条件测试__的(因为它们可以作为命令执行,执行结果表明是真还是假)而最后一个是shell用于__算术替换的语法__。
* 前两个__只能对__数值、字符串、文件做测试而第三个__只能对__算术、关系、逻辑表达式做条件测试。
[geekard@geekard ~]$ **((0))**; echo $?
@@ -38,7 +38,7 @@ Created Thursday 22 December 2011
[geekard@geekard ~]$ __((i=2+3))__; echo i=$i, $? # 对表达式求值将结果5赋给变量i,__ 5非0__故双括号返回真。
i=5, 0
[geekard@geekard ~]$
[geekard@geekard ~]$ unset i; __$((i=2+3))__; echo i=$i,$? #__ shell__对$((...))中的表达式进行计算把__执行计算结果当作命令来运行__。
[geekard@geekard ~]$ unset i; __$((i=2+3))__; echo i=$i,$? #__ shell__对$((...))中的表达式进行计算把__执行计算结果当作命令来运行__。也就是说__$(())不能作为命令来执行__。
bash: 5: command not found
i=5,127
[geekard@geekard ~]$
@@ -67,7 +67,7 @@ bash: 5: command not found
5
0
0
[geekard@geekard ~]$ ((2+3)); echo $?; __echo ((2+3))__** #shell不会自动对((..))求值替换**
[geekard@geekard ~]$ ((2+3)); echo $?; __echo ((2+3))__** #shell不会自动对((..))求值替换,可以使用$(())**
bash: syntax error near unexpected token `('
[geekard@geekard ~]$
[geekard@geekard ~]$ echo `((2+3))` #双括号没有输出只有退出状态故echo命令输出一空行
@@ -90,14 +90,14 @@ bash: syntax error near unexpected token `('
(2)字符串测试
= 等于
!= 不相等
-z字符串 字符串长度则为真
-n字符串 字符串长度不则为真
-z字符串 字符串长度为0则为真
-n字符串 字符串长度不为0则为真
(3)文件测试
-e文件名 文件存在为真
-r文件名 文件存在且为只读时为真
-w文件名 文件存在且可写时为真
-x文件名 文件存在且可执行为真
-s文件名 如果文件存在且长度不0
**-s**文件名 如果文件存在且长度不0
-d文件名 文件存在且为目录时为真
-f文件名 文件存在且为普通文件为真
-c文件名 文件存在且为字符类型特殊文件为真