Chinaunix首页 | 论坛 | 博客
  • 博客访问: 128525
  • 博文数量: 62
  • 博客积分: 1476
  • 博客等级: 上尉
  • 技术积分: 662
  • 用 户 组: 普通用户
  • 注册时间: 2009-12-03 16:38
文章分类

全部博文(62)

文章存档

2010年(14)

2009年(48)

我的朋友

分类: C/C++

2009-12-07 22:08:07

    今天继续连载文件IO操作,不过不再是使用,而是使用与操作系统相关的低级IO,此文适用于Unix/Linux中的低级文件IO操作。


/*******************************************************************************
* header file
*    int open( const char* filename, int oflag, ... ); O_CREAT->[mode_t mode]
*        e.g. open( "~/tmp.dat", O_RDWR|O_CREAT|O_EXCL, 0755 );
* note: return fd >= 0 when success.
*    oflag:
*        O_RDONLY,O_WRONLY,O_RDWR
*
*        O_APPEND: append at tail
*        O_CREAT: create when non-exist
*        O_EXCL: together with O_CREAT, if exist, open fail, else create
*        O_NDELAY: non-block for FIFO or SOCKET
*        O_NONBLOCK: rw return without waiting
*        O_NOCTTY: flag for TTY
*        O_TRUNC: if regular file exists, clear file to 0B even O_RDONLY
*
*        O_DSYNC: block when write file
*        O_SYNC: block when writing, and update atime, mtime, ctime
*        O_RSYNC|O_DSYNC: same with O_DSYNC when writing (set R flag)
*        O_RSYNC|O_SYNC:    same with O_SYNC when writing (set R flag)
*    mode: when create file, mode & (~umask)
*        S_IRWX[UGO]:        0700, 0070, 0007
*        S_IRUSR[GRP OTH]:    0400, 0040, 0004
*        S_IWUSR[GRP OTH]:    0200, 0020, 0002
*        S_IXUSR[GRP OTH]:    0100, 0010, 0001
* int fcntl( int fildes, int cmd, ... );
*    int fcntl( int fildes, int cmd );
*        cmd: F_GETFD, F_GETFL, F_GETOWN.(fd tag, oflag, get SIG pid,gid)
*    int fcntl( int fildes, int cmd, int arg );
*        cmd: F_SETFD, F_SETFL, F_SETOWN, F_DUPFD.(set arg)
*    int fcntl( int fildes, int cmd, struct flock *arg );
*        cmd: F_GETLK, F_SETLK, F_SETLKW.(F_SETLKW is blocked F_SETLK)
*        struct flock
*        {
*            short l_type;    F_RDLCK, F_WRLCK, F_UNLCK
*            short l_whence;    SEEK_SET, SEEK_CUR, SEEK_END
*            long l_start;    offset of lock start position
*            long l_len;    lock length, 0 means locking to file end
*            short l_pid;    the pid of lock owner
*        }
* header file
*    int close( int fides ); return 0(success) or -1
*    int unlink( char *path ); only rm file when lnk=0, return 0 or -1
*
*    ssize_t read( int fildes, void *buf, size_t nbytes );
*    ssize_t write( int fildes, void *buf, size_t nbytes );
*    int fsync( int fildes ); similar to fflush(), return 0 or -1(failed)
*
*    off_t lseek( int fildes, off_t offset, int whence );
*
*    int dup( int fildes ); return new file descriptor, or -1( failed )
*    int dup2( int fildes, int fildes2 ); dup2(2,fd), stderr>fd, redirect
* note:
*    dup() duplicates fildes to available least file descriptor.
*    dup2() duplicates fildes to fildes2, if fildes2 is open, close it.
*
*******************************************************************************/

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <memory.h>
#include <signal.h>
#include <assert.h>

int main( int ac, char *av[] )
{
    int fd;
    char buf[50], *cmd = "ls -l tmp.dat";

    if ( (fd=open("./tmp.dat", O_RDWR|O_CREAT|O_EXCL, 0755)) < 0 )
    {
        fprintf( stderr, "open file tmp.dat error!\n" );
        return 1;
    }

    memset( buf, 0, sizeof(buf) );

    printf( "\nCreate file tmp.dat successfully, Now check it.\n" );
    printf( "%s\n", cmd );
    system( cmd );
    
    printf( "\nWrite %d bytes.\n\t%s\n", write(fd, cmd, strlen(cmd)), cmd );
    
    printf( "\nWriting finished. Now read from file:\n" );
    fsync(1); //fflush(stdout);

    
    sleep(3);
    
    lseek( fd, 0, SEEK_SET );
    
    printf( "\nRead %d bytes.\n", read(fd, buf, sizeof(buf)) );
    printf( "\t%s\n\n", buf );
    
    close( fd );

    unlink("./tmp.dat");
    printf("Remove it after checking.\n\n");

    extern void testlck(void);
    extern void testrw(void);

    testlck();
    testrw();

    return 0;
}

/*******************************************************************************
* file lock functions lib *
*******************************************************************************/

int seelck( int fd, int start, int len )
{
    struct flock arg;
    
    arg.l_type = F_WRLCK;
    arg.l_whence = SEEK_SET;
    arg.l_start = start;
    arg.l_len = len;

    if ( fcntl(fd, F_GETLK, &arg) == -1 )
    {
        fprintf( stderr, "See lock failed.\n" );
        return 0; /* get lock info failed */
    }
    else if ( arg.l_type == F_UNLCK )
    {
        fprintf( stderr, "No lock from %d to %d\n", start, len );
    }
    else if ( arg.l_type == F_WRLCK )
    {
        fprintf( stderr, "Write lock from %d to %d by pid = %d.\n",
                start, len, arg.l_pid );
    }
    else if ( arg.l_type == F_RDLCK    )
    {
        fprintf( stderr, "Read lock from %d to %d by pid = %d.\n",
                start, len, arg.l_pid );
    }

    return 1;
}

int readlck( int fd, int start, int len ) /* get read lock, if get, return 1 */
{
    struct flock arg;

    arg.l_type = F_RDLCK;
    arg.l_whence = SEEK_SET;
    arg.l_start = start;
    arg.l_len = len;

    if ( fcntl(fd, F_SETLKW, &arg) == -1 )
    {
        fprintf( stderr, "[%d] set read lock failed.\n", getpid() );
        return 0;
    }
    else
    {
        fprintf(stderr, "[%d] set read lock from %d to %d.\n",
                getpid(), start, len );
        return 1;
    }
}

int writelck( int fd, int start, int len ) /* get write lock, failed return 0 */
{
    struct flock arg;

    arg.l_type = F_WRLCK;
    arg.l_whence = SEEK_SET;
    arg.l_start = start;
    arg.l_len = len;

    if ( fcntl(fd, F_SETLKW, &arg) == -1 )
    {
        fprintf( stderr, "[%d] set write lock failed.\n", getpid() );
        return 0;
    }
    else
    {
        fprintf(stderr, "[%d] set write lock from %d to %d.\n",
                getpid(), start, len );
        return 1;
    }
}

int releaselck( int fd, int start, int len ) /* release lock, return 1 or 0 */
{
    struct flock arg;

    arg.l_type = F_UNLCK;
    arg.l_whence = SEEK_SET;
    arg.l_start = start;
    arg.l_len = len;

    if ( fcntl(fd, F_SETLKW, &arg) == -1 )
    {
        fprintf( stderr, "[%d] release lock failed.\n", getpid() );
        return 0;
    }
    else
    {
        fprintf(stderr, "[%d] release lock from %d to %d.\n",
                getpid(), start, len );
        return 1;
    }
}

void testlck()
{
    int fd;
    struct flock arg;

    if ( (fd = open("./tmp.dat", O_RDWR|O_CREAT|O_EXCL, 0755)) < 0 )
    {
        fprintf( stderr, "open file ./tmp.dat failed.\n" );
        return;
    }
    seelck( fd, 0, 10 );
    readlck( fd, 0, 10 );
    seelck( fd, 11, 20 );
    writelck( fd, 11, 20 );
    sleep(2);
    releaselck( fd, 0, 10 );
    releaselck( fd, 11, 20 );
    
    close(fd);
    remove("./tmp.dat");

    return;
}

/*******************************************************************************
* blocked R/W functions lib *
*******************************************************************************/

int readf( int fd, void *buf, int size )
{
    int readcnt = 0, leftcnt = size; /* counter of read and left bytes */
    char *curbuf = buf;        /* pointer of current buffer */
    
    assert( buf != NULL );

    while ( leftcnt > 0 )
    {    /* if don't read size bytes, block here */
        if ( (readcnt = read(fd, curbuf, leftcnt)) < 0 )
        {
            if ( errno != EINTR )
            {
                errno = 0;
                return -1;
            }

            errno = 0;
            readcnt = 0;
            continue;
        }
        else if ( readcnt == 0 )    /* reach EOF */
            return EOF;

        leftcnt -= readcnt;
        curbuf += readcnt;
    }

    return 0;
}

int writef( int fd, void * buf, int size)
{
    int leftcnt = size, writecnt;
    char *curbuf = buf;

    assert( buf != NULL );

    while ( leftcnt > 0 )
    {
        if ( (writecnt = write(fd, curbuf, leftcnt)) <= 0 )
        {
            if ( errno != EINTR )
            {
                errno = 0;
                return -1;
            }

            errno = 0;
            writecnt = 0;
            continue;
        }
        leftcnt -= writecnt;
        curbuf += writecnt;
    }

    return 0;
}

void testrw()
{
    char buf[10];
    int size = sizeof(buf);

    memset(buf, 0, size);

    printf("Now test block read/write, please enter some thing\n");

    readf(0, buf, size);
    writef(1, buf, size);

    return;
}


进程调用mmap()时,只是在进程空间内新增了一块相应大小的缓冲区,并设置了相应的访问标识,但并没有建立进程空间到物理页面的映射。因此,第一次访问该空间时,会引发一个缺页异常。

1,malloc 和 mmap 极其相似,它们俩都不消耗物理内存,但是如果物理内存有富余的话,就会给它们用。
2,当物理内存紧张时(比如别的程序又需要申请一大笔内存),就会换页,将暂时用不到的页面交换到磁盘上去。
malloc 是交换到 swap(包括 swap 分区和 swap file),但是 mmap 则是交换到文件里去。

推论:
1,swap 分区总是有限的,因此所有进程总共可以 malloc 的内存总是有限的,
但是 mmap 则只要磁盘空间足够,总是能成功,且不影响其它应用程序的正常运行(【重要】也就是不会随机 drop 倒霉进程)。
2,考虑只读的情况,mmap 不会比 malloc 更慢,因为它们俩的策略是相同的,都是优先使用物理内存,缺页时从磁盘上 load 页面。

所以映射了1G大小的文件,只有在访问具体的地址时才实际进行物理内存的分配。当然也可不分配,直接多写文件,这是实现上的事情。


mmap函数是unix/linux下的系统调用,来看《Unix Netword programming》卷二12.2节有详细介绍。
mmap系统调用并不是完全为了用于共享内存而设计的。它本身提供了不同于一般对普通文件的访问方式,进程可以像读写内存一样对普通文件的操作。而Posix或系统V的共享内存IPC则纯粹用于共享目的,当然mmap()实现共享内存也是其主要应用之一。
mmap系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必再调用read(),write()等操作。mmap并不分配空间, 只是将文件映射到调用进程的地址空间里, 然后你就可以用memcpy等操作写文件, 而不用write()了.写完后用msync()同步一下, 你所写的内容就保存到文件里了. 不过这种方式没办法增加文件的长度, 因为要映射的长度在调用mmap()的时候就决定了.

简单说就是把一个文件的内容在内存里面做一个映像,内存比磁盘快些。
基本上它是把一个档案对应到你的virtual memory 中的一段,并传回一个指针。

以后对这段 memory 做存取时,其实就是对那个档做存取。
它就是一种快速 file I/O 的东东,而且使用上和存取 memory 一样方便,只不过会占掉你的 virutal memory。
#include
#include //文件状态结构
#include
#include //mmap头文件

void *mmap(void *start, size_t length, int prot , int flags, int fd, off_t offset);

mmap开启记忆体对映。
start指定记忆体位置,通常都是用NULL。offset指定档案要在那里开始对映,通常都是用0。

int munmap(void *start, size_t length);

int msync(const void *start, size_t length, int flags);
如果开启记忆体对映是希望写入档案中,那麽修改过的记忆体会在一段时间内与档案稍稍有点不同。如果您希望立即将资料写入档案中,可使用msync。

start为记忆体开始位置,length为长度。

flags则有三个:
MS_ASYNC : 请Kernel快将资料写入。
MS_SYNC : 在msync结束返回前,将资料写入。
MS_INVALIDATE : 让核心自行决定是否写入,仅在特殊状况下使用

例子:

if( (fp = open("./data.bin",O_RDONLY) ) < 0 )
{
cout<<" Can not open !"<<endl;
exit(0);
}
if( (fstat(fp,&stat_data) ) < 0 )
{
cout<<" fstat error !";
exit(0);
}
if( ( start_fp =mmap(NULL,stat_data.st_size,
PROT_READ,MAP_SHARED,fd_denseindex,0 )) == (void *)-1)
{
cout<<"mmaperror !"<<endl;
exit(0);
}

这样便能从start_fp开始读取数据了!
阅读(610) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~