EPOLL学习笔记

EPOLL学习笔记
EPOLL学习笔记

epoll学习笔记

epoll有两种模式,Edge Triggered(简称ET)和Level Triggered(简称LT).在采用这两种模式时要注意的是,如果采用ET模式,那么仅当状态发生变化时才会通知,而采用LT模式类似于原来的select/poll操作,只要还有没有处理的事件就会一直通知.

以代码来说明问题:

首先给出server的代码,需要说明的是每次accept的连接,加入可读集的时候采用的都是ET模式,而且接收缓冲区是5字节的,也就是每次只接收5字节的数据: #include

#include

#include

#include

#include

#include

#include

#include

#include

using namespace std;

#define MAXLINE5

#define OPEN_MAX100

#define LISTENQ20

#define SERV_PORT5000

#define INFTIM1000

void setnonblocking(int sock)

{

int opts;

opts=fcntl(sock,F_GETFL);

if(opts<0)

{

perror("fcntl(sock,GETFL)");

exit(1);

}

opts=opts|O_NONBLOCK;

if(fcntl(sock,F_SETFL,opts)<0)

{

perror("fcntl(sock,SETFL,opts)");

exit(1);

}

}

int main()

{

int i,maxi,listenfd,connfd,sockfd,epfd,nfds;

ssize_t n;

char line[MAXLINE];

socklen_t clilen;

//声明epoll_event结构体的变量,ev用于注册事件,数组用于回传要处理的事件

struct epoll_event ev,events[20];

//生成用于处理accept的epoll专用的文件描述符

epfd=epoll_create(256);

struct sockaddr_in clientaddr;

struct sockaddr_in serveraddr;

listenfd=socket(AF_INET,SOCK_STREAM,0);

//把socket设置为非阻塞方式

//setnonblocking(listenfd);

//设置与要处理的事件相关的文件描述符

ev.data.fd=listenfd;

//设置要处理的事件类型

ev.events=EPOLLIN|EPOLLET;

//ev.events=EPOLLIN;

//注册epoll事件

epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);

bzero(&serveraddr,sizeof(serveraddr));

serveraddr.sin_family=AF_INET;

char*local_addr="127.0.0.1";

inet_aton(local_addr,&(serveraddr.sin_addr));//htons(SERV_PORT);

serveraddr.sin_port=htons(SERV_PORT);

bind(listenfd,(sockaddr*)&serveraddr,sizeof(serveraddr));

listen(listenfd,LISTENQ);

maxi=0;

for(;;){

//等待epoll事件的发生

nfds=epoll_wait(epfd,events,20,500);

//处理所发生的所有事件

for(i=0;i

{

if(events[i].data.fd==listenfd)

{

connfd=accept(listenfd,(sockaddr*)&clientaddr,&clile n);

if(connfd<0){

perror("connfd<0");

exit(1);

}

//setnonblocking(connfd);

char*str=inet_ntoa(clientaddr.sin_addr);

cout<<"accapt a connection from"<

//设置用于读操作的文件描述符

ev.data.fd=connfd;

//设置用于注测的读操作事件

ev.events=EPOLLIN|EPOLLET;

//ev.events=EPOLLIN;

//注册ev

epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev);

}

else if(events[i].events&EPOLLIN)

{

cout<<"EPOLLIN"<

if((sockfd=events[i].data.fd)<0)

continue;

if((n=read(sockfd,line,MAXLINE))<0){

if(errno==ECONNRESET){

close(sockfd);

events[i].data.fd=-1;

}else

std::cout<<"readline error"<

}else if(n==0){

close(sockfd);

events[i].data.fd=-1;

}

line[n]='\0';

cout<<"read"<

//设置用于写操作的文件描述符

ev.data.fd=sockfd;

//设置用于注测的写操作事件

ev.events=EPOLLOUT|EPOLLET;

//修改sockfd上要处理的事件为EPOLLOUT

//epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);

}

else if(events[i].events&EPOLLOUT)

{

sockfd=events[i].data.fd;

write(sockfd,line,n);

//设置用于读操作的文件描述符

ev.data.fd=sockfd;

//设置用于注测的读操作事件

ev.events=EPOLLIN|EPOLLET;

//修改sockfd上要处理的事件为EPOLIN

epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);

}

}

}

return0;

}

下面给出测试所用的Perl写的client端,在client中发送10字节的数据,同时让client在发送完数据之后进入死循环,也就是在发送完之后连接的状态不发生改变--既不再发送数据,也不关闭连接,这样才能观察出server的状态:

#!/usr/bin/perl

use IO::Socket;

my$host="127.0.0.1";

my$port=5000;

my$socket=IO::Socket::INET->new("$host:$port")or die"create socket error$@"; my$msg_out="1234567890";

print$socket$msg_out;

print"now send over,go to sleep\n";

while(1)

{

sleep(1);

}

运行server和client发现,server仅仅读取了5字节的数据,而client其实发送了10字节的数据,也就是说,server仅当第一次监听到了EPOLLIN事件,由于没有读取完数据,而且采用的是ET模式,状态在此之后不发生变化,因此server 再也接收不到EPOLLIN事件了.

(友情提示:上面的这个测试客户端,当你关闭它的时候会再次出发IO可读事件给server,此时server就会去读取剩下的5字节数据了,但是这一事件与前面描述的ET性质并不矛盾.)

如果我们把client改为这样:

#!/usr/bin/perl

use IO::Socket;

my$host="127.0.0.1";

my$port=5000;

my$socket=IO::Socket::INET->new("$host:$port")or die"create socket error$@"; my$msg_out="1234567890";

print$socket$msg_out;

print"now send over,go to sleep\n";

sleep(5);

print"5second gone send another line\n";

print$socket$msg_out;

while(1)

{

sleep(1);

}

可以发现,在server接收完5字节的数据之后一直监听不到client的事件,而当client休眠5秒之后重新发送数据,server再次监听到了变化,只不过因为只是读取了5个字节,仍然有10个字节的数据(client第二次发送的数据)没有接收完.

如果上面的实验中,对accept的socket都采用的是LT模式,那么只要还有数据留在buffer中,server就会继续得到通知,读者可以自行改动代码进行实验.

基于这两个实验,可以得出这样的结论:ET模式仅当状态发生变化的时候才获得通知,这里所谓的状态的变化并不包括缓冲区中还有未处理的数据,也就是说,如果要采用ET模式,需要一直read/write直到出错为止,很多人反映为什么采用ET模式只接收了一部分数据就再也得不到通知了,大多因为这样;而LT模式是只要有数据没有处理就会一直通知下去的.

补充说明一下这里一直强调的"状态变化"是什么:

1)对于监听可读事件时,如果是socket是监听socket,那么当有新的主动连接到来为状态发生变化;对一般的socket而言,协议栈中相应的缓冲区有新的数据为状态发生变化.但是,如果在一个时间同时接收了N个连接(N>1),但是监听socket只accept了一个连接,那么其它未accept的连接将不会在ET模式下给监听socket发出通知,此时状态不发生变化;对于一般的socket,就如例子中而言,如果对应的缓冲区本身已经有了N字节的数据,而只取出了小于N字节的数据,那么残存的数据不会造成状态发生变化.

2)对于监听可写事件时,同理可推,不再详述.

而不论是监听可读还是可写,对方关闭socket连接都将造成状态发生变化,比如在例子中,如果强行中断client脚本,也就是主动中断了socket连接,那么都将

造成server端发生状态的变化,从而server得到通知,将已经在本方缓冲区中的数据读出.

把前面的描述可以总结如下:仅当对方的动作(发出数据,关闭连接等)造成的事件才能导致状态发生变化,而本方协议栈中已经处理的事件(包括接收了对方的数据,接收了对方的主动连接请求)并不是造成状态发生变化的必要条件,状态变化一定是对方造成的.所以在ET模式下的,必须一直处理到出错或者完全处理完毕,才能进行下一个动作,否则可能会发生错误.

另外,从这个例子中,也可以阐述一些基本的网络编程概念.首先,连接的两端中,一端发送成功并不代表着对方上层应用程序接收成功,就拿上面的client测试程序来说,10字节的数据已经发送成功,但是上层的server并没有调用read读取数据,因此发送成功仅仅说明了数据被对方的协议栈接收存放在了相应的buffer中,而上层的应用程序是否接收了这部分数据不得而知;同样的,读取数据时也只代表着本方协议栈的对应buffer中有数据可读,而此时时候在对端是否在发送数据也不得而知.

epoll精髓

在linux的网络编程中,很长的时间都在使用select来做事件触发。在linux 新的内核中,有了一种替换它的机制,就是epoll。

相比于select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。因为在内核中的select实现中,它是采用轮询来处理的,轮询的fd数目越多,自然耗时越多。并且,在linux/posix_types.h头文件有这样的声明:

#define__FD_SETSIZE1024

表示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队列里

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),并且无论后面的数据是否被使用,因此他们具有同样的职能。因为即使使用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操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表.

ET(edge-triggered)是高速工作方式,只支持no-block socket。在这种模式下,

当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了(比如,你在发送,接收或者接收请求,或者发送接收的数据少于一定量时导致了一个EWOULDBLOCK错误)。但是请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once),不过在TCP协议中,ET模式的加速效用仍需要更多的benchmark确认(这句话不理解)。

在许多测试中我们会看到如果没有大量的idle-connection或者dead-connection,epoll的效率并不会比select/poll高很多,但是当我们遇到大量的idle-connection(例如WAN环境中存在大量的慢速连接),就会发现epoll的效率大大高于select/poll。(未测试)

另外,当使用epoll的ET模型来工作时,当产生了一个EPOLLIN事件后,

读数据的时候需要考虑的是当recv()返回的大小如果等于请求的大小,那么很有可能是缓冲区还有数据未读完,也意味着该次事件还没有处理完,所以还需要再次读取:

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是非阻塞时,如返回此错误,表示写缓冲队列已满,

//在这里做延时后再重试.

if(errno==EAGAIN)

{

usleep(1000);

continue;

}

return-1;

}

if((size_t)tmp==total)

return buflen;

total-=tmp;

p+=tmp;

}

return tmp;

}

在linux的网络编程中,很长的时间都在使用select来做事件触发。在linux 新的内核中,有了一种替换它的机制,就是epoll。

相比于select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。因为在内核中的select实现中,它是采用轮询来处理的,轮询的fd数目越多,自然耗时越多。并且,在linux/posix_types.h头文件有这样的声明:

#define__FD_SETSIZE1024

表示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队列里

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),并且无论后面的数据是否被使用,因此他们具有同样的职能。因为即使使用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操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表.

ET(edge-triggered)是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了(比如,你在发送,接收或者接收请求,或者发送接收的数据少于一定量时导致了一个EWOULDBLOCK错误)。但是请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once),不过在TCP协议中,ET模式的加速效用仍需要更多的benchmark确认(这句话不理解)。

在许多测试中我们会看到如果没有大量的idle-connection或者

dead-connection,epoll的效率并不会比select/poll高很多,但是当我们遇到大量的idle-connection(例如WAN环境中存在大量的慢速连接),就会发现epoll的效率大大高于select/poll。(未测试)

另外,当使用epoll的ET模型来工作时,当产生了一个EPOLLIN事件后,

读数据的时候需要考虑的是当recv()返回的大小如果等于请求的大小,那么很有可能是缓冲区还有数据未读完,也意味着该次事件还没有处理完,所以还需要再次读取:

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是非阻塞时,如返回此错误,表示写缓冲队列已满,

//在这里做延时后再重试.

if(errno==EAGAIN)

{

usleep(1000);

continue;

}

return-1;

}

if((size_t)tmp==total)

return buflen;

total-=tmp;

p+=tmp;

}

return tmp;

}

epoll有两种模式,Edge Triggered(简称ET)和Level Triggered(简称LT).在采用这两种模式时要注意的是,如果采用ET模式,那么仅当状态发生变化时才会通知,而采用LT模式类似于原来的select/poll操作,只要还有没有处理的事件就会一直通知.

以代码来说明问题:

首先给出server的代码,需要说明的是每次accept的连接,加入可读集的时候采用的都是ET模式,而且接收缓冲区是5字节的,也就是每次只接收5字节的数据:

#include

#include

#include

#include

#include

#include

#include

#include

#include

using namespace std;

#define MAXLINE5

#define OPEN_MAX100

#define LISTENQ20

#define SERV_PORT5000

#define INFTIM1000

void setnonblocking(int sock)

{

int opts;

opts=fcntl(sock,F_GETFL);

if(opts<0)

{

perror("fcntl(sock,GETFL)");

exit(1);

}

opts=opts|O_NONBLOCK;

if(fcntl(sock,F_SETFL,opts)<0)

{

perror("fcntl(sock,SETFL,opts)");

exit(1);

}

}

int main()

{

int i,maxi,listenfd,connfd,sockfd,epfd,nfds;

ssize_t n;

char line[MAXLINE];

socklen_t clilen;

//声明epoll_event结构体的变量,ev用于注册事件,数组用于回传要处理的事件struct epoll_event ev,events[20];

//生成用于处理accept的epoll专用的文件描述符

epfd=epoll_create(256);

struct sockaddr_in clientaddr;

struct sockaddr_in serveraddr;

listenfd=socket(AF_INET,SOCK_STREAM,0);

//把socket设置为非阻塞方式

//setnonblocking(listenfd);

//设置与要处理的事件相关的文件描述符

ev.data.fd=listenfd;

//设置要处理的事件类型

ev.events=EPOLLIN|EPOLLET;

//ev.events=EPOLLIN;

//注册epoll事件

epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);

bzero(&serveraddr,sizeof(serveraddr));

serveraddr.sin_family=AF_INET;

char*local_addr="127.0.0.1";

inet_aton(local_addr,&(serveraddr.sin_addr));//htons(SERV_PORT); serveraddr.sin_port=htons(SERV_PORT);

bind(listenfd,(sockaddr*)&serveraddr,sizeof(serveraddr));

listen(listenfd,LISTENQ);

maxi=0;

for(;;){

//等待epoll事件的发生

nfds=epoll_wait(epfd,events,20,500);

//处理所发生的所有事件

for(i=0;i

{

if(events[i].data.fd==listenfd)

{

connfd=accept(listenfd,(sockaddr*)&clientaddr,&clilen);

if(connfd<0){

perror("connfd<0");

exit(1);

}

//setnonblocking(connfd);

char*str=inet_ntoa(clientaddr.sin_addr);

cout<<"accapt a connection from"<

//设置用于读操作的文件描述符

ev.data.fd=connfd;

//设置用于注测的读操作事件

ev.events=EPOLLIN|EPOLLET;

//ev.events=EPOLLIN;

//注册ev

epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev);

}

else if(events[i].events&EPOLLIN)

{

cout<<"EPOLLIN"<

if((sockfd=events[i].data.fd)<0)

continue;

if((n=read(sockfd,line,MAXLINE))<0){

if(errno==ECONNRESET){

close(sockfd);

events[i].data.fd=-1;

}else

std::cout<<"readline error"<

}else if(n==0){

close(sockfd);

events[i].data.fd=-1;

}

line[n]='\0';

cout<<"read"<

//设置用于写操作的文件描述符

ev.data.fd=sockfd;

//设置用于注测的写操作事件

ev.events=EPOLLOUT|EPOLLET;

//修改sockfd上要处理的事件为EPOLLOUT

//epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);

}

else if(events[i].events&EPOLLOUT)

{

sockfd=events[i].data.fd;

write(sockfd,line,n);

//设置用于读操作的文件描述符

ev.data.fd=sockfd;

//设置用于注测的读操作事件

ev.events=EPOLLIN|EPOLLET;

//修改sockfd上要处理的事件为EPOLIN

epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);

}

}

}

return0;

}

下面给出测试所用的Perl写的client端,在client中发送10字节的数据,同时让client在发送完数据之后进入死循环,也就是在发送完之后连接的状态不发生改变--既不再发送数据,也不关闭连接,这样才能观察出server的状态:

#!/usr/bin/perl

use IO::Socket;

my$host="127.0.0.1";

my$port=5000;

my$socket=IO::Socket::INET->new("$host:$port")or die"create socket error$@";

my$msg_out="1234567890";

print$socket$msg_out;

print"now send over,go to sleep\n";

while(1)

{

sleep(1);

}

运行server和client发现,server仅仅读取了5字节的数据,而client其实发送了10字节的数据,也就是说,server仅当第一次监听到了EPOLLIN事件,由于没有读取完数据,而且采用的是ET模式,状态在此之后不发生变化,因此server 再也接收不到EPOLLIN事件了.

如果我们把client改为这样:

#!/usr/bin/perl

use IO::Socket;

my$host="127.0.0.1";

my$port=5000;

my$socket=IO::Socket::INET->new("$host:$port")or die"create socket error$@";

my$msg_out="1234567890";

print$socket$msg_out;

print"now send over,go to sleep\n";

sleep(5);

print"5second gonesend another line\n";

print$socket$msg_out;

while(1)

{

sleep(1);

}

可以发现,在server接收完5字节的数据之后一直监听不到client的事件,而当client休眠5秒之后重新发送数据,server再次监听到了变化,只不过因为只是读取了5个字节,仍然有10个字节的数据(client第二次发送的数据)没有接收完.

如果上面的实验中,对accept的socket都采用的是LT模式,那么只要还有数据留在buffer中,server就会继续得到通知,读者可以自行改动代码进行实验.

基于这两个实验,可以得出这样的结论:ET模式仅当状态发生变化的时候才获得通知,这里所谓的状态的变化并不包括缓冲区中还有未处理的数据,也就是说,如果要采用ET模式,需要一直read/write直到出错为止,很多人反映为什么采用

ET模式只接收了一部分数据就再也得不到通知了,大多因为这样;而LT模式是只要有数据没有处理就会一直通知下去的.

补充说明一下这里一直强调的"状态变化"是什么:

1)对于监听可读事件时,如果是socket是监听socket,那么当有新的主动连接到来为状态发生变化;对一般的socket而言,协议栈中相应的缓冲区有新的数据为状态发生变化.但是,如果在一个时间同时接收了N个连接(N>1),但是监听socket只accept了一个连接,那么其它未accept的连接将不会在ET模式下给监听socket发出通知,此时状态不发生变化;对于一般的socket,就如例子中而言,如果对应的缓冲区本身已经有了N字节的数据,而只取出了小于N字节的数据,那么残存的数据不会造成状态发生变化.

2)对于监听可写事件时,同理可推,不再详述.

而不论是监听可读还是可写,对方关闭socket连接都将造成状态发生变化,比如在例子中,如果强行中断client脚本,也就是主动中断了socket连接,那么都将造成server端发生状态的变化,从而server得到通知,将已经在本方缓冲区中的数据读出.

把前面的描述可以总结如下:仅当对方的动作(发出数据,关闭连接等)造成的事件才能导致状态发生变化,而本方协议栈中已经处理的事件(包括接收了对方的数据,接收了对方的主动连接请求)并不是造成状态发生变化的必要条件,状态变化一定是对方造成的.所以在ET模式下的,必须一直处理到出错或者完全处理完毕,才能进行下一个动作,否则可能会发生错误.

另外,从这个例子中,也可以阐述一些基本的网络编程概念.首先,连接的两端中,一端发送成功并不代表着对方上层应用程序接收成功,就拿上面的client测试程序来说,10字节的数据已经发送成功,但是上层的server并没有调用read读取数据,因此发送成功仅仅说明了数据被对方的协议栈接收存放在了相应的buffer中,而上层的应用程序是否接收了这部分数据不得而知;同样的,读取数据时也只代表着本方协议栈的对应buffer中有数据可读,而此时时候在对端是否在发送数据也不得而知.

epoll为什么这么快

epoll是多路复用IO(I/O Multiplexing)中的一种方式,但是仅用于linux2.6以上内核,在开始讨论这个问题之前,先来解释一下为什么需要多路复用IO.

以一个生活中的例子来解释.

假设你在大学中读书,要等待一个朋友来访,而这个朋友只知道你在A号楼,但是不知道你具体住在哪里,于是你们约好了在A号楼门口见面.

百万用户同时在线游戏服务器架构实现

百万用户在线网络游戏服务器架构实现 一、前言 事实上100万游戏服务器,在面对大量用户访问、高并发请求方面,基本的解决方案集中在这样几个环节:使用高性能的服务器、高效率的编程语言、高性能的数据库、还有高性能的架构模型。但是除了这几个方面,还没法根本解决面临的高负载和高并发问题。 当然用户不断地追求更高的机器性能,而升级单一的服务器系统,往往造成过高的投入和维护成本,性价比大大低于预期。同时全天候的可用性的要求也不能满足要求,如果服务器出现故障则该项服务肯定会终止。所以单独追求高性能的服务器不能满足要求,目前基本的解决方案是使用集群技术做负载均衡,可以把整体性能不高的服务器做成高可扩展性,高可用性,高性能的,满足目前的要求。 目前解决客户端和服务器进行底层通讯的交互的双向I/O模型的服务器的成熟方案。 1.windows下,比较成熟的技术是采用IOCP,完成端口的服务器模型。 2.Linux下,比较成熟的技术是采用Epoll服务器模型, Linux 2.6内核中提供的System Epoll 为我们提供了一套完美的解决方案。 目前如上服务器模型是完全可以达到5K到20K的同时在线量的。但5K这样的数值离百万这样的数值实在相差太大了,所以,百万人的同时在线是单台服务器肯定无法实现的。 而且目前几个比较成熟的开发框架,比如ICE,ACE等。这样,当采用一种新的通信技术来实现通信底层时,框架本身就不用做任何修改了(或修改很少),而功能很容易实现,性能达到最优。目前采用的ace框架个不错的选择方案,可以不受操作系统的影响,移植比较方便。 对于数据库选择可有许多成熟的方案,目前大多数选择的mysql Master/slave模式,以及oracle RAC方案。基本可以满足目前的要求,但具体的瓶颈不是在数据库本身,应该还是硬件磁盘I/O的影响更大些。建议使用盘阵。这有其他成熟的方案,比如采用NAS解决分布数据存储。 其实最为关键的是服务器的架构和实现,数据流量的负载均衡,体系的安全性,关键影响度,共享数据的处理等等多个方面对100万用户的数据处理有影响,所以都要全面的考虑。 二、高性能的服务器 1.网络环境 目前采用Client/Server架构来开发网络游戏,客户端和服务器一般通过TCP/UDP协议进

[工作流]建立一个工作流程的简明过程

建立一个工作流程的简明过程 引言:有很多刚接触通达OA的用户在刚开始使用OA 工作流时会问,我怎样把这个模块用起来呢,或者诸如,从哪里开始入手,表单如何建立,流程怎么设计,咋新建工作模块怎么就看不到流程等等。为此,我总结了建立一个简单流程雏形的必要项目,在基于工作流使用详解的基础上,再讲解一下从表单—流程—建立工作的简明过程。 图1 1.设计表单 在系统管理—工作流设置—设计表单,填写表单名称和所属部门,保存。 注:刚建立的的表单是在未分类下的,这点您不用怀疑,或者也不用想把它移到那个分类下,它会在之后建立的流程调用表单时,随流程的分类而分类。 A.表单可以用其他软件制作好,保存成TXT、HTM或HTML格式文件导入,或是在智能设计器新建。 B.添加控件,进入智能设计器右侧一列是工作流表单基本元素---控件,这些都需要根据需要手动添加,填写控件名称、基本属性确定,保存表单,此后您还可以点击预览表单参看效果。

图2 至于具体每个控件的说明参照《通达2010版工作流使用详解》。 2设计流程 在系统管理—工作流设置—设计流程,填写名称、选择所属部门,表单和分类等项,保 存。之后您就可以在该分类下找到流程进入流程设计器 图3 打开流程设计器,有两种视图方式,图形视图和列表视图可切换。 图形视图下的操作都是通过鼠标右击来选择。(如果这里没有显示出彩色图形,请设置 您的IE 安全相关项)

图4 列表视图下的操作可以直接按显示选择。 图5 A.依次新建步骤,选择下一步骤来控制流程走向(默认是按照建立的顺序依次排序),保存。 B.经办权限:建立步骤之后最好先把经办权限设置好,这关系到最后走工作流时能否顺利选人转交,如果因为没有设置经办权限而不能转交,还要返回到设计阶段岂不是影响工作效率?当然如果是自由流程就另当别论,但是有一点不管是自由流程还是固定流程都不能例外,那就是第一步的经办权限,一定要设置,否则在新建工作的时候是看不到该流程的。 C.可写字段:要想在实际办理工作的时候对表单进行一些填写审批的操作,那就一定是

SD模块用户操作手册

目录 (1)标准订单用于有赊销的情况,常用于省公司销售和分公司有赊销的销售。(2)标准订单(参照合同)用于有赊销的情况,同样用于省公司销售和分公司有赊销的销售,但该订单适用于事前签订了合同的情况。 (3)现金订单(无应收)多用于市(县)公司无赊销的销售。 (4)库割流程在同一油库内将属于省公司的油料库割给分公司,只是油料所有权的转移,并不发生实际的油料移动,没有损耗,仅仅是帐面的处理。 (5)寄售补货订单用于将油品从我方油库转移给寄售方,但发出货物的物权仍在我方,不转移给客户,作为我方在客户方的寄售库存。 (6)寄售发货订单在寄售方将寄售货物的售出部分与我方进行结算时使用。 (7)直发流程在某客户与公司有一笔销售业务往来,客户到炼厂直接提货时使用。(8)销售代保管发货流程描述的是对客户寄存在我公司的代保管量进行发货的过程。 (9)合同是在销售业务进行之前,业务员与客户按照销售需求协商,呈销售经理审批核准后与客户签订的。 (10)错误发票处理流程 (11)退货流程是指销售业务发生后,客户因质量等原因要求退货时而发生的。(12)发票打印流程 (13)分公司提货单打印

一、标准订单流程 (一) 创建标准订单 1. SAP交易代码VA01 2.输入:销售组织分销渠道部门销售办事处(订单类型ZS03) 3.输入订单相关内容:售达方采购订单编号(填入销售人员)交货工厂付款 条件物料订单数量 4.点击“”,通过修改销售指导附加价输入价格,再点击“”返回 5.点击“”(显示凭证项目细节),点“会计”,输入收付方式,”指定”栏位内输入 销售办公室的代号 6.点击“装运”,输入:装运类型特殊处理标志 7.保存订单,记录订单号 (二)针对标准订单创建交货单并发货过帐 1. SAP交易代码VL01N 2.输入:装运地点订单号 3.输入交货单相关内容:评估类型库位,点击“外部细节”,选择运输方式 4.转到/表头/文本,输入:提单流水号 5. 点击“发货过帐”,记录交货单号 (三)开立发票 1.SAP交易代码VF01 2.输入交货单号,转到/表头/文本,输入:开票员姓名,点击“(执行)”,保存 发票

Windows Socket五种IO模型——代码全攻略

Windows Socket五种I/O模型——代码全攻略 Winsock 的I/O操作: 1、两种I/O模式 阻塞模式:执行I/O操作完成前会一直进行等待,不会将控制权交给程序。套接字默认为阻塞模式。可以通过多线程技术进行处理。 非阻塞模式:执行I/O操作时,Winsock函数会返回并交出控制权。这种模式使用起来比较复杂,因为函数在没有运行完成就进行返回,会不断地返回WSAEWOULDBLOCK错误。但功能强大。 为了解决这个问题,提出了进行I/O操作的一些I/O模型,下面介绍最常见的三种: Windows Socket五种I/O模型——代码全攻略 如果你想在Windows平台上构建服务器应用,那么I/O模型是你必须考虑的。Windows操作系统提供了选择(Select)、异步选择(WSAAsyncSelect)、事件选择(WSAEventSelect)、重叠I/O(Overlapped I/O)和完成端口(Completion Port)共五种I/O模型。每一种模型均适用于一种特定的应用场景。程序员应该对自己的应用需求非常明确,而且综合考虑到程序的扩展性和可移植性等因素,作出自己的选择。 我会以一个回应反射式服务器(与《Windows网络编程》第八章一样)来介绍这五种I/O模型。 我们假设客户端的代码如下(为代码直观,省去所有错误检查,以下同): #include #include #define SERVER_ADDRESS "137.117.2.148" #define PORT 5150 #define MSGSIZE 1024 #pragma comment(lib, "ws2_32.lib") int main() { WSADA TA wsaData; SOCKET sClient; SOCKADDR_IN server; char szMessage[MSGSIZE]; int ret; // Initialize Windows socket library WSAStartup(0x0202, &wsaData);

工作流引擎讲解

什么是工作流引擎,工作流引擎有什么作用,为什么需要工作流管理系统,在这里我们主要研究它的好处,你想要理解它的好处,就得知道不使用它会带来什么样的坏处。 现在我们来讲工作流,什么是工作流?所谓的工作流就是通常所说的业务流程,那么所谓的业务流程换句话来讲就是多个人在一起去完成一件事情。这就可以称之为工作流。流程的本质就是一个参与者参与到一个过程里面来 那么现在我们就想为什么需要工作流管理系统,工作流管理系统能给我们带来什么好处。我们就从这个角度出发来了解JBPM 工作流引擎 下面我们就来看关于为什么需要工作流管理系统,以及它给我们带来的好处。 实际上它带来的好处就是使某些容易变化的东西抽象出去,我们能够通过某种方式改变它,然后你就可以对你的某些核心部分不需要做什么变动 现在就通过一个小例子来讲这个工作流引擎到底是一个什么东西 举个请假流程的例子 一个请假的过程 重点讲解UML 里面的内容,确定UML 里面流程图的讲解顺序 请假流程 现在只看左边的内容,右边的后面再讲,我是方便讲解就将这点东西放到这个空白的地方 一个简单的流程图Main 模拟出请假的过程

对提交请假单进行分析 用一个用户来表示普通用户和审批者,只不过他们的权限不同,他们都能够登录到这个系统 现在我们来看用户和请假单,分析他们之间的关系,用户和请假单之间的联系有请假,用户填了一个请假单就创建了一个请假单对象,他们之该是一对多的关系。因为某一个用户可以请多次假 对吧(其实一般是一个请假单对应一个请假者,这个需求就应该得到客户的确定,客户说了算)那么用户和这个请假单之间还有没有其他联系? 接下来是提交请假单。我首先将请假单提交给张三,那么张三就能够看到这个请假单,如果用户将请假单提交给张三,那么就可以在张三和请假单之间建立一个待审关系 他们之间的关系也是一对多的关系,因为张三可以同时审核几个请假单,就是这意思,一个请假单等待的用户是一个,从现在的需求来看。那么两者之间还有另外一个联系那就是已审,一个用户可以审批过多个请假单,请假单也可以被多个用户审批 比如张三审批以后交给李四审批,李四审批以后交给王五审批,其实这个已审就是记录审批信息的,比如审批时间,审批意见,把它放在审批关联里边 这个就是一个基本的概念,了解这个概念之后我们就考虑它的设计,JBPM 实际上就是协助我们把这个请假单从一个用户手上转递到另一个用户手上。当把这个模型分析清楚了我们就要去实现它。 这里重点分析提交,怎样去提交,在SSH 架构体下,提交请假单这个业务逻辑,你可能就需要这样一个业务逻辑类,里边可能有这么一个方法专门来进行提交操作的,那么这个方法怎样设计,以及这个方法怎样去实现。了解这点你就可以了解JBPM 干什么的,能给我们带来什么好处 (用自己的话说明一下提交请假单的过程 <读一下那段伪代码>) 在这个过程里边写这些代码是比较麻烦的,现在还只是一个固定的流程,假设我现在在这里边变化一下 那么整个方案都要变动。 我现在希望有一个会签的功能 比如我现在要将这个这样的功能,把这个请假单同时提交给多个审批者审批。 那这个时候你就不能够在请假单中间增加一个外键, 把它整成审批者什么的,

epoll精髓

epoll精髓 在linux的网络编程中,很长的时间都在使用select来做事件触发。在linux新的内核中,有了一种替换它的机制,就是epoll。 相比于select,epoll最大的好处在于它不会随着监听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()的返回值,第二个参数表示动作,用三个宏来表示:

Activiti工作流入门详解完整教学教程

Activiti入门教程详解完整教程 1.A ctiviti介绍 Activiti是由Alfresco软件在2010年5月17日发布的业务流程管理(BPM)框架,它是覆盖了业务流程管理,工作流,服务协作等领域的一个开源,灵活的,易扩展的可执行流程语言框架。 Activiti基于Apache许可的开源BPM平台,创始人Tom Baeyens是JBoss JBPM的项目架构师,它的特色是提供了eclipse插件,开发人员可以通过插件直接绘画出业务流程图。 1.1工作流引擎 ProcessEngine对象,这是Activiti工作的核心。负责生成流程运行时的各种实例及数据,监控和管理流程的运行。 1.2BPMN 业务流程建模与标注(Business Process Model and Notation,BPMN),描述流程的基本符号,包括这些图元如何组合成一个业务流程图(Business Process Diagram)

2.准备环境 2.1Activiti软件环境 1)JDK1.6或者更高版本 2)支持的数据库有:h2,mysql,oracle,mysql,db2等 3)支持Activiti运行的jar包,可以通过maven依赖引入 4)开发环境为Eclipse3.7或者以上版本,myeclipse为8.6版本2.2安装流程设计器(eclipse插件) 1)打开Help →Install New Software →Add 输入Name: Activiti Designer Location: https://www.360docs.net/doc/2c7189533.html,/designer/update/ 输入完成后,单击OK按钮等待下载完成后安装。 安装完成后在菜单选项中会出现Activiti的目录选项

SAP系统操作手册及问题解决方案

SAP系统操作手册及问题解决方案 会计期间关闭 首先要打开相应的会计年度和记帐期间(OB52)。其次,维护FI的Number Range(FBN1)和CO 中的Number Range(KANK)。特别是CO Number range中的COIN业务一定要分配Number range 新建工厂后新建订单Shipping Point找不到 有一个shippingpointdetermine,一点要维护 路径: 后勤执行--装运--基本装运功能--装运点和收货点确认--分配装运点 原来是物料类型的问题 新建工厂并分配后,migo初始化库存,系统提示出错 No stock posting possible for this material 后来发现是物料类型定义里面数量更新没有打上勾 物流通用--主数据--基础设置--物料类型--定义物料类型属性 再次经过验证不是这里的问题,而是shippingpointdetermine没有定义的缘故 Business Area Account Assignment 4.1.7上的Business Area Account Assignment可把我害惨了,我配置完Sales Area后,由于没有Assignment到BusinessArea,所以系统老是提醒我没有Define这鬼东西,后来自己研究之后才发现4.6C和4.7.1之间的细微差别,4.6C在这里是自动Assign的,但4.7.1是需要自己Assign的。 路径:

sap customizing implementation guide-->enterprise structure-->assignment-->sales and distribution-->business area account assignment-->define rules by sales area SAP学习手册IV 请教各位,我已经在测试系统里,归档了销售订单以及其发票,会计凭证和交货单,但是我想看看归档的效果,请问R3系统有可以查看到归档数据的功能吗?另外,交货单还对应一张物料凭证,我就是直接归档交货单了,不知道是不是应该先归档物料凭证再归档交货单?因为归档发票的时候,需要先归档发票对应的会计凭证,再归档发票。否则系统不让通过。 怎样做归档的资料在本论坛前几天我发的帖子里有人提供了,基本上是STEP BY STEP的教,很详细。你去那下载吧。只是归档不同的数据要选择不同的归档对象就可以了。如归档销售订单用的归档对象是SD_VBAK, 归档交货单用RV_LIKP, 归档销售发票用:SD_VBRK, 归档会计凭证用: FI_DOCUMNT我提的问题是如何查看已经被归档的数据?在每个归档对象中,都有一个管理功能,你选择一下,就可以查看该归档对象所归档的全部内容,系统按日期排列但是我归档的销售订单,数量字段都显示为空。其实这些订单都有数量。不知道是没有把数量字段拷贝到归档文件,还是读取程序有误没有显示数量字段。请帮我再看看好吗?选择某一次归档会话,点击“√”:然后系统显示这次会话归档的销售订单清单,但是奇怪的是,所有的销售订单数量字段为空,如下图: 为何计量单位显示为****** 导致无法使用,如何解决? 计量单位是在后台设置的。系统中有一个基本计量单位,你现在所看到的计量单位是自己定义的。可以任意设置,只要填对两者的换算关系就行了。物料主数据的单位,不是在你当前语言环境下创建的吧, 看看你的物料主数据是否在英文环境下被建立,没有建立中文单位. 请教!怎么删掉SM37中Active状态的进程?有几个进程的状态是ACTIVE 而且执行的时间已经很长了。现在想把进程DELETE,可是用什么方法都无法办到。SM50吧,但是在sm37里不是有stop这个功能么?在SM50中看不到这个进程。而且STOP和CANCEL都用了,可是还是么有用啊! 选择好,然后CANCEL,再DELETE,不就OK了嘛不管是CANCEL还是DELETE 都没有效果啊! 可能是这支程式坏掉了,找更高权限的管理员清吧, 试一下check status,不行就重启应用, menu job->status check 不行的话,可能表里有不良数据,要么重启,sm65, check table consistency。要么,直接查一个个表,比较麻烦,改坏掉就不好了 我装了一台测试机,想进行数据操作时,发现时间上有问题,请各位高手指点,界面如下: F-60试试看。 你第一次开物料账可以用OMSY把物料账开到当前时间,以后就用MMPV开物料账而会计账就是你所说的F-60以上三个代码我试过了,可水平实在太差,还是搞不定,能不能帮忙确认讲解一下! 1.OMSY: 我的界面如下,我将第一条记录改为2005后,其它记录却不能修改!

sapsd操作手册

sapsd操作手册 1

前言 《SD模块用户培训手册》是燕化ERP整合项目SD模块部分流程的操作方法规范, 按章节分步骤对本次整合过程中变动较大部分做了必要的介绍。此《手册》作为燕山石化ERP整合项目SD模块最终用户的培训材料和ERP系统中进行销售分销管理及操作的补充。 SD模块整合前在两个公司代码下共有销售业务流程35个, 整合后归并为14个销售业务流程; 原有单据类型72 个, 现有单据类型34个; 原有条件类型33个, 现有条件类型21个。 在《SD模块用户培训手册》的编写过程中, 把ERP管理理念与本企业实际业务有机相结合, 并总结了上线一年的实践经验, 适应新的销售体制改革需求, 结合企业各项管理制度, 理顺规范业务流程, 力求提升分公司的销售管理水平。 SD模块整合工作得到石化盈科顾问的技术指导、燕山石化ERP整合项目指导委员会的关心和相关专业部门的支持, 在此表示感谢! 由于编写时间仓促、理论实践水平有限, 如有疏漏和错误之处, 恳请广大读者给予指正, 发现系统中业务管理存在的不足请及时提出, 以便我们加以完善。 本书编写成员: 中石化燕山分公司: 孙秀军、李旭、徐晓光、尹庆伟、佟欣 ERP技术顾问: 熊霞

燕化ERP整合项目SD模块用户培训手册编写小组 12月06日

目录 概述................................................................................................ 错误!未定义书签。 一、销售范围简介........................................................................... 错误!未定义书签。 1、销售组织结构图........................................................................ 错误!未定义书签。2、销售范围相关定义.................................................................. 错误!未定义书签。二销售业务流程.............................................................................. 错误!未定义书签。 第一章 SD_001: 客户主数据维护流程......................................... 错误!未定义书签。 一、业务流程简介........................................................................... 错误!未定义书签。 1、新增客户的审批流程................................................................ 错误!未定义书签。2、业务流程各步骤说明.............................................................. 错误!未定义书签。二SAP系统操作 ............................................................................. 错误!未定义书签。 1、SAP交易代码........................................................................... 错误!未定义书签。2、操作细部描述.......................................................................... 错误!未定义书签。 第二章 SD_002: 价格主数据维护流程......................................... 错误!未定义书签。 一条件类型 ..................................................................................... 错误!未定义书签。二SAP系统操作过程 ..................................................................... 错误!未定义书签。 1、完成该步骤的SAP角色名称 .................................................. 错误!未定义书签。 2、SAP交易代码............................................................................ 错误!未定义书签。

OA工作流使用详解

信达OA工作流使用详解 概念篇 随着企业管理信息化进程的不断深入,协同应用软件的概念已深入人心,而工作流系统正是协同应用软件的核心。通过应用IT技术来规范工作流程、提高工作的执行效率和准确度,是工作流系统主要解决的问题。 信达OA的工作流功能正是为这一需求而设计,可实现业务或公文的申请、审批、会签、登记、操作等环节的管理,可将工作过程进行记录,便于日后检查。并实现数据的规范化录入、查询、统计和存档。 简单来说,工作流就是把一项工作化解为多个步骤,由多人协同来完成。这里所指的工作是形形色色的,在办公室里进行的很多不同类型的工作,都可以用工作流系统来表达,信达OA的工作流由表单和流程这2个重要因素构成:表单:在工作流系统中,与工作相关的数据都可以通过“表单”来体现,“表单”是数据的载体,相当于现实工作中的各类工作单;在表单以外,还可以通过附件文件来传递数据与信息。 流程:在工作流系统中,工作过程都可以通过“流程”的定义来体现,“流程”是工作过程的描述,代表了一种制度或规范。 工作流系统相当于一个“万能模块”,通过合理的流程设置,规范化运作,可对我们的日常办公和企业管理提供极大的便利。 信达OA工作流的一些概念: 1、工作流就是几个人协同完成一项工作,简单而言,就是几个人按次序填写同一张“表单”,填写会签意见和传递附件。 2、表单可以由软件用户自行设计(一般由管理员设计好)。 3、信达OA的表单格式可以用网页设计工具或word等设计,设计好后复制、粘贴到“表单智能设计器”中,再添加表单控件就可以了。 4、每个流程对应一个表单,不同流程可以共用同一个表单。 5、流程分为固定流程和自由流程两种,固定流程由固定步骤组成,用户事先需定义好,自由流程无需定义流程步骤。 6、固定流程的每个步骤都需要设定经办人、可写字段、下一步骤等信息。 7、固定流程第一个步骤设定的经办人,才有权新建该流程(道理可想而知)。 8、执行中的工作和已完成的工作,都可以通过工作查询功能进行查询。 9、任何流程都可以指定监控人员和查询人员,监控人员可随时转交下步或终止流程(自由流程),监控人员和查询人员都可以查询该流程全部工作。

心通达OA工作流使用说明书

心通达OA工作流使用说明书 【工作流使用详解】 2018-3-21 北京高速波软件有限公司

版权说明 本文件中出现的任何文字叙述、文档格式、插图、照片、方法、过程等内容,除非另有特别注明,版权均属北京高速波软件有限公司所有,受到有关产权及版权等法律保护。任何个人、机构未经北京高速波软件有限公司的书面授权许可,不得复制、引用或传播本文件的任何片断,无论通过电子形式或非电子形式。

目录 工作流设计 (5) 1.1分类设置 (5) 1.1.1表单分类 (5) 1.1.2流程分类 (6) 1.2自动编号设置 (8) 1.2.1自动编号设置 (8) 1.2.2自动编号预览 (9) 1.2.3编号管理 (10) 1.3设计表单 (10) 1.3.1新建表单 (10) 1.3.2编辑表单 (11) 1.3.3预览 (12) 1.3.4删除 (12) 1.3.5导入 (12) 1.3.6导出 (13) 1.3.7表单智能设计器 (13) 1.3.8表单控件 (14)

设计流程 (32) 1.4.1流程管理 (32) 1.5新建工作 (53) 1.6我的工作 (55) 1.7工作查询 (56) 1.8工作监控 (56)

工作流设计 1.1分类设置 1.1.1表单分类 进入工作流-工作流设置-分类设置, 建立表单分类。表单分类更方便了表单的管理,把不同性质的表单放在不同的分类下,也方便了表单的查找。同时根据表单分类的所属部门,实现了表单分类按部门进行独立管理的目的。 新建表单分类:首先点击【新建】按钮,根据具体需求选择表单父分类,填写相应的表单分类排序号,表单分类名称,以及所属部门后保存。

sap_sd_操作手册

前言 《SD模块用户培训手册》是燕化ERP整合项目SD模块部分流程的操作方法规范,按章节分步骤对本次整合过程中变动较大部分做了必要的介绍。此《手册》作为燕山石化ERP整合项目SD模块最终用户的培训材料和ERP系统中进行销售分销管理及操作的补充。 SD模块整合前在两个公司代码下共有销售业务流程35个,整合后归并为14个销售业务流程;原有单据类型72 个,现有单据类型34个;原有条件类型33个,现有条件类型21个。 在《SD模块用户培训手册》的编写过程中,把ERP管理理念与本企业实际业务有机相结合,并总结了上线一年的实践经验,适应新的销售体制改革需求,结合企业各项管理制度,理顺规范业务流程,力求提升分公司的销售管理水平。 SD模块整合工作得到石化盈科顾问的技术指导、燕山石化ERP整合项目指导委员会的关心和相关专业部门的支持,在此表示感谢! 由于编写时间仓促、理论实践水平有限,如有疏漏和错误之处,恳请广大读者给予指正,发现系统中业务管理存在的不足请及时提出,以便我们加以完善。 本书编写成员: 中石化燕山分公司:孙秀军、李旭、徐晓光、尹庆伟、佟欣 ERP技术顾问:熊霞 燕化ERP整合项目SD模块用户培训手册编写小组 2005年12月06日 目录 概述5 一、销售范围简介5 1、销售组织结构图5 2、销售范围相关定义6 二销售业务流程7

第一章 SD_001:客户主数据维护流程7 一、业务流程简介7 1、新增客户的审批流程7 2、业务流程各步骤说明7 二SAP系统操作8 1、SAP交易代码8 2、操作细部描述8 第二章 SD_002:价格主数据维护流程15 一条件类型15 二SAP系统操作过程15 1、完成该步骤的SAP角色名称15 2、SAP交易代码15 3操作细部描述16 三、其他项目说明23 四、显示价格主数据24 五、冻结价格主数据25 第三章 SD_003:标准销售合同维护流程27 一业务流程简介27 1业务流程各步骤说明27 二SAP系统操作过程27 1SAP交易代码27 2操作细部描述27 3更改销售合同29 4显示合同29 第四章 SD_004 标准订单处理流程29 一销售业务与订单类型简介29 1、订单类型29 2、业务模式所适用订单类型29 二SAP系统操作30 1创建燕化量价销售订单30 2显示订单列表34 3显示订单凭证流36 4订单的后续操作冻结及拒绝40 第五章、SD_006:化工统销集成业务流程42 一、买断模式业务流程简介42 1、注意事项:42 2、业务流程图42

web应用框架-活字格工作流功能详解(下)

概述 本章节讲述了实际应用场景下如何灵活使用活字格工作流,包括工作流流程条的使用,以及工作流命令与批量工作流命令的设置。 业务场景描述 为了让大家能够在实际应用场景中理解工作流中的状态,普通流程,审批流程,以请休假管理模块为例,企业员工在提交请假申请单时,根据部门的不同判断是否需要经过人力资源部核准剩余年假,然后根据请假天数提交部门领导审批,部门领导审批状态为审批流程:小于3天,提交到部门副经理,小于5天,提交到部门经理,小于7天,公司副经理,小于10天,公司经理,大于等于10天,集团董事长,部门领导审批结束后提交人力资源部扣除年假流程结束。在流程流转到人资部结算时,设置提醒,要求提交给人资部结算1个小时以内每20分钟提醒一次,直到人资部结算提交流程。管理员可以随意将流程撤转到对应的环节上。 流程图如下: 请休假申请流程(包含普通流程+部门领导审批流程)

部门领导审批流程

工作流设置

通过上一章节流程功能点的讲解,配合流程图,我们可以将请休假流程在活字格中得以体现。在学习应用场景时,建议大家先学习了解下工作流中所有的功能,那样更方便大家针对特定流程去设计。 表和页面的创建在这里我就不为大家一一介绍了,直入主题,开启请休假表的工作流。在请休假表开启工作流时,表名后会带一个工作流的小图标。操作步骤如下: 1. 新建状态,整个工作流包含了起草,人资部核准,部门领导审批,人资部结算以及结束5个状态。其中起草,人资部核准,人资部结算,结束均为普通流程,部门领导审批为审批流程。

2. 如流程图所示,起草环节根据创建者的部门区分流转环节,若创建者部门=活字格开发部,提交人资部核准,担当者为人力资源部经理,,若创建者部门=活字格业务部,提交部门领导审批。此时就需要在条件中添加不同的分支。条件中选择创建者的扩展属性部门。 3. 当创建者属于活字格开发部时,提交给人资部核准,人资部正常提交给部门领导审批。普通流程后跟审批流程,不用设置担当者。

JIRA工作流详解文档

JIRA 工作流介绍 "工作流"是一个问题经过其生命周期的若干步骤和阶段的变迁。工作流通常代表着实际的业务处理流程。JIRA通过一定数量的状态对问题进行处理来模拟问题的生命周期。 每个状态代表了生命周期的一个阶段,通常由一个有意义的名字来表示。本文将介绍如何创建或定制一个工作流,同时对相应的概念进行解释。如果你已经定义了一个工作流,请参见激活工作流一节来进行使用。 JIRA所有的版本都带有一个默认的工作流。需要说明的是JIRA标准版中只有一个默认工作流,且不能被编辑,但是在JIRA企业版和专业版中,您可以增加新的工作流来定制问题的生命周期。 ?JIRA企业版中,支持多个活动的工作流。每一个工作流可以与某一个特定项目和特定的问题类型相关联。这样就可以实现不同的项目(或者一个项目中的不同问题类型)采用不同的工作流。 ?JIRA专业版仅支持一个活动的工作流。即在JIRA专业版中用户可以定义任意多个工作流,但在某一时刻,系统中的所有问题只能使用其中的一个工作流。 注意:处于非激活状态的工作流才能够被编辑。如果您想修改一个处于激活状态的工作流的话,可以拷贝这个工作流(参考下面介绍 的’Createing a Workflow’),然后修改这个拷贝的工作流,修改之后再激活使用它。 关于工作流步骤和变迁 一个工作流是由步骤和变迁组成。 ?一个工作流的步骤标识工作流中一个问题的一个阶段或者说是’状态’。在某一时刻一个问题只能存在于一个在步骤中。每个工作流步骤都相应有一个"连接的" 状态。每个工作流必须有至少一个步骤,该步骤叫做初始步骤。当一个问题被移动到某一步骤时,它的的’状态’被更新为该步骤"连接的"状态。 第 1 页共 23 页

SAP系统用户操作手册-SD-V1.5(一)SD主数据

SD主数据 说明: 1、不改变各部分对应字体、字号; 2、系统操作界面截屏中,标注符号从以下标识选取即可: 3、版本控制: 版本号日期更改者版本备注 1.0 2012-05-31 胡瑞媛 景伟 周俊 杨靓 张晶 王丽娜 初始版本: 客户主数据 信贷主数据;托盘回收 价格主数据;销售开票(参考财务操作手册) 业务员主数据 数量合同;按单采购;免费发货;借/贷项;其它 标准销售订单(按单/库);来料加工;退货;换货 ERP项目 SAP系统用户操作手册 销售与分销(SD) 7 8 10 1 12 2 4 5 6 7 8 9 10 11 3 13 1 4 15

目录 1SD主数据 (6) 1.1客户主数据 (6) 1.1.1创建客户主数据 (6) 1.1.2更改客户主数据 (30) 1.1.3显示客户主数据 (33) 1.1.4系统操作要点说明 (36) 1.2信贷主数据 (37) 1.2.1创建/更改信贷主数据 (37) 1.2.2显示信贷主数据 (41) 1.2.3显示信贷更改 (43) 1.2.4信贷主数据清单 (45) 1.2.5系统操作要点说明 (47) 1.3销售价格主数据 (47) 1.3.1创建条件记录 (47) 1.3.2更改条件记录 (50) 1.3.3显示条件记录 (53) 1.3.4系统操作要点说明 (56) 1.4业务员主数据 (57) 1.4.1创建业务员主数据 (57) 1.4.2更改业务员主数据 (67) 1.4.3显示业务员主数据 (70)

1.4.4系统操作要点说明 (73) 1.5物料清单(销售BOM) (75) 1.5.1创建物料清单(销售BOM) (75) 1.5.2更改物料清单(销售BOM) (79) 1.5.3显示物料清单(销售BOM) (83) 1.5.4系统操作要点说明 (85) 2销售订单业务............................................................................................... 错误! 2.1数量合同 ........................................................................................ 错误! 2.1.1创建数量合同 ........................................................................... 错误! 2.1.2更改数量合同 ........................................................................... 错误! 2.1.3显示数量合同 ........................................................................... 错误! 2.1.4系统操作要点说明.................................................................... 错误! 2.2销售订单-标准 ............................................................................... 错误! 2.2.1创建销售订单 ........................................................................... 错误! 2.2.2销售订单审批 ........................................................................... 错误! 2.2.3更改销售订单 ........................................................................... 错误! 2.2.4显示销售订单 ........................................................................... 错误! 2.2.5系统操作要点说明.................................................................... 错误! 2.3销售订单-按单采购........................................................................ 错误! 2.4销售订单-免费发货........................................................................ 错误! 2.5销售订单-借/贷项 .......................................................................... 错误! 2.6销售订单-其它 ............................................................................... 错误!

epoll IO多路复用技术

一、编目1 1. Epoll 是何方神圣? Epoll 可是当前在Linux 下开发大规模并发网络程序的热门人选,Epoll 在Linux2.6 内核中正式引入,和select 相似,其实都I/O 多路复用技术而已,并没有什么神秘的。 其实在Linux 下设计并发网络程序,向来不缺少方法,比如典型的Apache 模型(Process Per Connection ,简称PPC ),TPC (Thread Per Connection )模型,以及select 模型和poll 模型,那为何还要再引入Epoll 这个东东呢?那还是有得说说的… 2. 常用模型的缺点 如果不摆出来其他模型的缺点,怎么能对比出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 在这点上使用了“共享内存”,这个内存拷贝也省略了。

相关文档
最新文档