📄 libasync.c
字号:
/* * Library for Posix async read operations with hints. * Author: Don Capps * Company: Iozone * Date: 4/24/1998 * * Two models are supported. First model is a replacement for read() where the async * operations are performed and the requested data is bcopy()-ed back into the users * buffer. The second model is a new version of read() where the caller does not * supply the address of the buffer but instead is returned an address to the * location of the data. The second model eliminates a bcopy from the path. * * To use model #1: * 1. Call async_init(&pointer_on_stack,fd,direct_flag); * The fd is the file descriptor for the async operations. * The direct_flag sets VX_DIRECT * * 2. Call async_read(gc, fd, ubuffer, offset, size, stride, max, depth) * Where: * gc ............ is the pointer on the stack * fd ............ is the file descriptor * ubuffer ....... is the address of the user buffer. * offset ........ is the offset in the file to begin reading * size .......... is the size of the transfer. * stride ........ is the distance, in size units, to space the async reads. * max ........... is the max size of the file to be read. * depth ......... is the number of async operations to perform. * * 3. Call end_async(gc) when finished. * Where: * gc ............ is the pointer on the stack. * * To use model #2: * 1. Call async_init(&pointer_on_stack,fd,direct_flag); * The fd is the file descriptor for the async operations. * The direct_flag sets VX_DIRECT * 2. Call async_read(gc, fd, &ubuffer, offset, size, stride, max, depth) * Where: * gc ............ is the pointer on the stack * fd ............ is the file descriptor * ubuffer ....... is the address of a pointer that will be filled in * by the async library. * offset ........ is the offset in the file to begin reading * size .......... is the size of the transfer. * stride ........ is the distance, in size units, to space the async reads. * max ........... is the max size of the file to be read. * depth ......... is the number of async operations to perform. * * 3. Call async_release(gc) when finished with the data that was returned. * This allows the async library to reuse the memory that was filled in * and returned to the user. * * 4. Call end_async(gc) when finished. * Where: * gc ............ is the pointer on the stack. * * To use model #1: (WRITES) * 1. Call async_init(&pointer_on_stack,fd,direct_flag); * The fd is the file descriptor for the async operations. * * 2. Call async_write(gc, fd, ubuffer, size, offset, depth) * Where: * gc ............ is the pointer on the stack * fd ............ is the file descriptor * ubuffer ....... is the address of the user buffer. * size .......... is the size of the transfer. * offset ........ is the offset in the file to begin reading * depth ......... is the number of async operations to perform. * * 4. Call end_async(gc) when finished. * Where: * gc ............ is the pointer on the stack. * * Notes: * The intended use is to replace calls to read() with calls to * async_read() and allow the user to make suggestions on * what kind of async read-ahead would be nice to have. * The first transfer requested is guarenteed to be complete * before returning to the caller. The async operations will * be started and will also be guarenteed to have completed * if the next call specifies its first request to be one * that was previously performed with an async operation. * * The async_read_no_copy() function allows the async operations * to return the data to the user and not have to perform * a bcopy of the data back into the user specified buffer * location. This model is faster but assumes that the user * application has been modified to work with this model. * * The async_write() is intended to enhance the performance of * initial writes to a file. This is the slowest case in the write * path as it must perform meta-data allocations and wait. */#include <sys/types.h>#include <aio.h>#if defined(solaris) || defined(linux) || defined(SCO_Unixware_gcc)#else#include <sys/timers.h>#endif#include <sys/errno.h>#include <unistd.h>#ifndef bsd4_4#include <malloc.h>#endif#ifdef VXFS#include <sys/fs/vx_ioctl.h>#endif#if defined(OSFV5) || defined(linux)#include <string.h>#endif#if defined(linux)#include <unistd.h>#include <stdio.h>#include <stdlib.h>#endif#if ((defined(solaris) && defined(__LP64__)) || defined(__s390x__))/* If we are building for 64-bit Solaris, all functions that return pointers * must be declared before they are used; otherwise the compiler will assume * that they return ints and the top 32 bits of the pointer will be lost, * causing segmentation faults. The following includes take care of this. * It should be safe to add these for all other OSs too, but we're only * doing it for Solaris now in case another OS turns out to be a special case. */#include <stdio.h>#include <stdlib.h>#include <strings.h> /* For the BSD string functions */#endifvoid mbcopy(char *source, char *dest, size_t len);#if !defined(solaris) && !defined(off64_t) && !defined(_OFF64_T) && !defined(__off64_t_defined) && !defined(SCO_Unixware_gcc)typedef long long off64_t;#endif#if defined(OSFV5)#include <string.h>#endifextern long long page_size;extern int one;/* * Internal cache entrys. Each entry on the global * cache, pointed to by async_init(gc) will be of * this structure type. */char version[] = "Libasync Version $Revision: 3.23 $";struct cache_ent { struct aiocb myaiocb; /* For use in small file mode */#ifdef _LARGEFILE64_SOURCE #if defined(__CrayX1__) aiocb64_t myaiocb64; /* For use in large file mode */#else struct aiocb64 myaiocb64; /* For use in large file mode */#endif #endif long long fd; /* File descriptor */ long long size; /* Size of the transfer */ struct cache_ent *forward; /* link to next element on cache list */ struct cache_ent *back; /* link to previous element on the cache list */ long long direct; /* flag to indicate if the buffer should be */ /* de-allocated by library */ char *real_address; /* Real address to free */ volatile void *oldbuf; /* Used for firewall to prevent in flight */ /* accidents */ int oldfd; /* Used for firewall to prevent in flight */ /* accidents */ size_t oldsize; /* Used for firewall to prevent in flight */ /* accidents */};/* * Head of the cache list */struct cache { struct cache_ent *head; /* Head of cache list */ struct cache_ent *tail; /* tail of cache list */ struct cache_ent *inuse_head; /* head of in-use list */ long long count; /* How many elements on the cache list */ struct cache_ent *w_head; /* Head of cache list */ struct cache_ent *w_tail; /* tail of cache list */ long long w_count; /* How many elements on the write list */ };long long max_depth;extern int errno;struct cache_ent *alloc_cache();struct cache_ent *incache();void async_init();void end_async();int async_suspend();int async_read();void takeoff_cache();void del_cache();void async_release();void putoninuse();void takeoffinuse();struct cache_ent *allocate_write_buffer();size_t async_write();void async_wait_for_write();void async_put_on_write_queue();void async_write_finish();/* On Solaris _LP64 will be defined by <sys/types.h> if we're compiling * as a 64-bit binary. Make sure that __LP64__ gets defined in this case, * too -- it should be defined on the compiler command line, but let's * not rely on this. */#if defined(_LP64)#if !defined(__LP64__)#define __LP64__#endif#endif/***********************************************//* Initialization routine to setup the library *//***********************************************/voidasync_init(gc,fd,flag)struct cache **gc;int fd;int flag;{#ifdef VXFS if(flag) ioctl(fd,VX_SETCACHE,VX_DIRECT);#endif if(*gc) { printf("Warning calling async_init two times ?\n"); return; } *gc=(struct cache *)malloc((size_t)sizeof(struct cache)); if(*gc == 0) { printf("Malloc failed\n"); exit(174); } bzero(*gc,sizeof(struct cache));#if defined(__AIX__) || defined(SCO_Unixware_gcc) max_depth=500;#else max_depth=sysconf(_SC_AIO_MAX);#endif}/***********************************************//* Tear down routine to shutdown the library *//***********************************************/voidend_async(gc)struct cache *gc;{ del_cache(gc); async_write_finish(gc); free((void *)gc);}/***********************************************//* Wait for a request to finish *//***********************************************/intasync_suspend(struct cache_ent *ce){#ifdef _LARGEFILE64_SOURCE #ifdef __LP64__ const struct aiocb * const cblist[1] = {&ce->myaiocb};#else const struct aiocb64 * const cblist[1] = {&ce->myaiocb64};#endif#else const struct aiocb * const cblist[1] = {&ce->myaiocb};#endif#ifdef _LARGEFILE64_SOURCE #ifdef __LP64__ return aio_suspend(cblist, 1, NULL);#else return aio_suspend64(cblist, 1, NULL);#endif#else return aio_suspend(cblist, 1, NULL);#endif}/************************************************************************* * This routine is a generic async reader assist funtion. It takes * the same calling parameters as read() but also extends the * interface to include: * stride ..... For the async reads, what is the distance, in size units, * to space the reads. Note: Stride of 0 indicates that * you do not want any read-ahead. * max ..... What is the maximum file offset for this operation. * depth ..... How much read-ahead do you want. * * The calls to this will guarentee to complete the read() operation * before returning to the caller. The completion may occur in two * ways. First the operation may be completed by calling aio_read() * and then waiting for it to complete. Second the operation may be * completed by copying the data from a cache of previously completed * async operations. * In the event the read to be satisfied is not in the cache then a * series of async operations will be scheduled and then the first * async read will be completed. In the event that the read() can be * satisfied from the cache then the data is copied back to the * user buffer and a series of async reads will be initiated. If a * read is issued and the cache contains data and the read can not * be satisfied from the cache, then the cache is discarded, and * a new cache is constructed. * Note: All operations are aio_read(). The series will be issued * as asyncs in the order requested. After all are in flight * then the code will wait for the manditory first read. *************************************************************************/int async_read(gc, fd, ubuffer, offset, size, stride, max, depth)struct cache *gc;long long fd;char *ubuffer;off64_t offset;long long size;long long stride;off64_t max;long long depth;{ off64_t a_offset,r_offset; long long a_size; struct cache_ent *ce,*first_ce=0; long long i; ssize_t retval=0; ssize_t ret; long long start = 0; long long del_read=0; a_offset=offset; a_size = size; /* * Check to see if it can be completed from the cache */ if((ce=(struct cache_ent *)incache(gc,fd,offset,size))) {#ifdef _LARGEFILE64_SOURCE #ifdef __LP64__ while((ret=aio_error(&ce->myaiocb))== EINPROGRESS) { async_suspend(ce); }#else while((ret=aio_error64(&ce->myaiocb64))== EINPROGRESS) { async_suspend(ce); }#endif#else while((ret=aio_error(&ce->myaiocb))== EINPROGRESS) { async_suspend(ce); }#endif if(ret) { printf("aio_error 1: ret %d %d\n",ret,errno); }#ifdef _LARGEFILE64_SOURCE #ifdef __LP64__ retval=aio_return(&ce->myaiocb);#else#if defined(__CrayX1__) retval=aio_return64((aiocb64_t *)&ce->myaiocb64);#else retval=aio_return64((struct aiocb64 *)&ce->myaiocb64);#endif#endif#else retval=aio_return(&ce->myaiocb);#endif if(retval > 0) {#ifdef _LARGEFILE64_SOURCE #ifdef __LP64__ mbcopy((char *)ce->myaiocb.aio_buf,(char *)ubuffer,(size_t)retval);#else mbcopy((char *)ce->myaiocb64.aio_buf,(char *)ubuffer,(size_t)retval);#endif#else mbcopy((char *)ce->myaiocb.aio_buf,(char *)ubuffer,(size_t)retval);#endif }#ifdef _LARGEFILE64_SOURCE #ifdef __LP64__ if(retval < ce->myaiocb.aio_nbytes)#else if(retval < ce->myaiocb64.aio_nbytes)#endif#else if(retval < ce->myaiocb.aio_nbytes)#endif { printf("aio_return error1: ret %d %d\n",retval,errno);#ifdef _LARGEFILE64_SOURCE #ifdef __LP64__ printf("aio_return error1: fd %d offset %ld buffer %lx size %d Opcode %d\n", ce->myaiocb.aio_fildes, ce->myaiocb.aio_offset, (long)(ce->myaiocb.aio_buf), ce->myaiocb.aio_nbytes, ce->myaiocb.aio_lio_opcode#else printf("aio_return error1: fd %d offset %lld buffer %lx size %d Opcode %d\n", ce->myaiocb64.aio_fildes, ce->myaiocb64.aio_offset, (long)(ce->myaiocb64.aio_buf), ce->myaiocb64.aio_nbytes, ce->myaiocb64.aio_lio_opcode#endif#else printf("aio_return error1: fd %d offset %d buffer %lx size %d Opcode %d\n", ce->myaiocb.aio_fildes, ce->myaiocb.aio_offset, (long)(ce->myaiocb.aio_buf), ce->myaiocb.aio_nbytes, ce->myaiocb.aio_lio_opcode#endif ); } ce->direct=0; takeoff_cache(gc,ce); }else { /* * Clear the cache and issue the first request async() */ del_cache(gc); del_read++; first_ce=alloc_cache(gc,fd,offset,size,(long long)LIO_READ);again:#ifdef _LARGEFILE64_SOURCE #ifdef __LP64__ ret=aio_read(&first_ce->myaiocb);#else ret=aio_read64(&first_ce->myaiocb64);#endif#else ret=aio_read(&first_ce->myaiocb);#endif if(ret!=0) { if(errno==EAGAIN) goto again; else printf("error returned from aio_read(). Ret %d errno %d\n",ret,errno); } } if(stride==0) /* User does not want read-ahead */ goto out; if(a_offset<0) /* Before beginning of file */ goto out; if(a_offset+size>max) /* After end of file */ goto out; if(depth >=(max_depth-1)) depth=max_depth-1; if(depth==0) goto out; if(gc->count > 1) start=depth-1; for(i=start;i<depth;i++) /* Issue read-aheads for the depth specified */ { r_offset=a_offset+((i+1)*(stride*a_size)); if(r_offset<0) continue; if(r_offset+size > max) continue; if((ce=incache(gc,fd,r_offset,a_size))) continue; ce=alloc_cache(gc,fd,r_offset,a_size,(long long)LIO_READ);#ifdef _LARGEFILE64_SOURCE #ifdef __LP64__ ret=aio_read(&ce->myaiocb);#else ret=aio_read64(&ce->myaiocb64);#endif#else ret=aio_read(&ce->myaiocb);#endif if(ret!=0) { takeoff_cache(gc,ce); break; } } out: if(del_read) /* Wait for the first read to complete */ {#ifdef _LARGEFILE64_SOURCE #ifdef __LP64__ while((ret=aio_error(&first_ce->myaiocb))== EINPROGRESS) { async_suspend(first_ce); }#else while((ret=aio_error64(&first_ce->myaiocb64))== EINPROGRESS) { async_suspend(first_ce); }#endif#else while((ret=aio_error(&first_ce->myaiocb))== EINPROGRESS) { async_suspend(first_ce); }#endif if(ret) printf("aio_error 2: ret %d %d\n",ret,errno);#ifdef _LARGEFILE64_SOURCE #ifdef __LP64__ retval=aio_return(&first_ce->myaiocb);#else retval=aio_return64(&first_ce->myaiocb64);#endif#else retval=aio_return(&first_ce->myaiocb);#endif#ifdef _LARGEFILE64_SOURCE #ifdef __LP64__ if(retval < first_ce->myaiocb.aio_nbytes)#else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -