在
使fread和fwrite的运用更加的强壮文中,讲解了如何使用fread和fwrite。我们会发现,其实在Unix/Linux系统中,用fread和fwrite并不是一个最佳选择,因为C标准库屏蔽了一些错误信息,我们仅仅能够使用ferror函数来判断是否出现了错误,但是是何种错误,我们无法知晓。对于一个使用中断的系统而言,我们都希望对于系统调用是可从入的,所有有些网友喜欢在struct sigaction的sa_flags中将SA_RESTART标记加入,虽然我们达到了所希望的,但是同时我们的代价就是无法实现某些功能,比如说使用时钟中断来实现I/O读写超时功能。当然我们依然可以使用select或者poll函数来实现这种功能,不过是否现存所有unix/linux都支持它们并且其他平台呢?对于信号捕捉函数而言,一般都不指定SA_RESTART标记,因为这些可能会对程序的移植性带来一些问题(比如说将部分依赖于SA_RESTART的程序移植到一个不关心SA_RESTART的程序中去,导致我们可能不得不改动所有相关的中断处理函数,并且这种改动也可能是无法进行的)。
那么如何来解决这些问题呢?我觉得我们可能要在read/write上封装一层,比如提供readn/writen。它的主要功能是减少平台之间的关联作用,比如在Unix/Linux系统中,有中断干扰。但是在windows系统中没有这个方面的干扰。对于上层而言,它只要知道readn/writen成功的时候返回>=0的值,否则返回-1。对于具体是用read/write还是用fread/fwrite实现并不关心。比如说,我们在unix/linux平台我们可能会偏重于read/write来实现,在windows平台我们可能偏重于fread/fwrite来实现。
为了给网友一个演示,我在unix/linux平台写了一段程序,它仅仅是作为参考。希望对网友有所帮助!
#include <iostream>
#include <memory.h>
#include <assert.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
using namespace std;
#define __rwFn( fd, opFn, buf, count ) ({ /
ssize_t __realCount; /
for ( ; ; ) /
{ /
__realCount = opFn( fd, buf, count ); /
if ( __realCount == -1 && errno == EINTR ) /
continue; /
break; /
} /
__realCount; /
})
#ifndef __rwFn
ssize_t readn( int fd, void *buf, size_t count )
{
for ( ; ; )
{
ssize_t loaded = read( fd, buf, count );
if ( loaded == -1 && errno == EINTR )
continue;
return loaded;
}
}
ssize_t writen( int fd, const void *buf, size_t count )
{
for ( ; ; )
{
ssize_t saved = write( fd, buf, count );
if ( saved == -1 && errno == EINTR )
continue;
return saved;
}
}
#else
#define readn( fd, buf, count ) __rwFn( fd, read, buf, count )
#define writen( fd, buf, count ) __rwFn( fd, write, buf, count )
#endif
class AutoFile
{
int fd;
AutoFile( const AutoFile &rhs );
AutoFile& operator=( const AutoFile &rhs );
public:
AutoFile( const char *file, int flags )
: fd ( -1 )
{
assert( file != NULL );
fd = open( file, flags );
assert( fd != -1 );
}
operator int ()
{
return fd;
}
~AutoFile()
{
if ( fd != -1 );
close( fd );
}
};
int main( void )
{
AutoFile fd( "./test.cpp", O_RDONLY );
struct stat st_buf;
int rs = fstat( fd, &st_buf );
assert( rs != -1 );
assert( st_buf.st_blksize > 0 );
cout << "Good I/O block size = " << st_buf.st_blksize << endl;
ssize_t count = (ssize_t) st_buf.st_blksize;
char *buf = new char[ count ];
assert( buf != NULL );
for ( ssize_t loaded; (loaded = readn( fd, buf, count )) > 0 ; )
{
ssize_t saved = writen( STDOUT_FILENO, buf, loaded );
assert( saved == loaded );
}
delete[] buf;
return 0;
}
那么如何来解决这些问题呢?我觉得我们可能要在read/write上封装一层,比如提供readn/writen。它的主要功能是减少平台之间的关联作用,比如在Unix/Linux系统中,有中断干扰。但是在windows系统中没有这个方面的干扰。对于上层而言,它只要知道readn/writen成功的时候返回>=0的值,否则返回-1。对于具体是用read/write还是用fread/fwrite实现并不关心。比如说,我们在unix/linux平台我们可能会偏重于read/write来实现,在windows平台我们可能偏重于fread/fwrite来实现。
为了给网友一个演示,我在unix/linux平台写了一段程序,它仅仅是作为参考。希望对网友有所帮助!
#include <iostream>
#include <memory.h>
#include <assert.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
using namespace std;
#define __rwFn( fd, opFn, buf, count ) ({ /
ssize_t __realCount; /
for ( ; ; ) /
{ /
__realCount = opFn( fd, buf, count ); /
if ( __realCount == -1 && errno == EINTR ) /
continue; /
break; /
} /
__realCount; /
})
#ifndef __rwFn
ssize_t readn( int fd, void *buf, size_t count )
{
for ( ; ; )
{
ssize_t loaded = read( fd, buf, count );
if ( loaded == -1 && errno == EINTR )
continue;
return loaded;
}
}
ssize_t writen( int fd, const void *buf, size_t count )
{
for ( ; ; )
{
ssize_t saved = write( fd, buf, count );
if ( saved == -1 && errno == EINTR )
continue;
return saved;
}
}
#else
#define readn( fd, buf, count ) __rwFn( fd, read, buf, count )
#define writen( fd, buf, count ) __rwFn( fd, write, buf, count )
#endif
class AutoFile
{
int fd;
AutoFile( const AutoFile &rhs );
AutoFile& operator=( const AutoFile &rhs );
public:
AutoFile( const char *file, int flags )
: fd ( -1 )
{
assert( file != NULL );
fd = open( file, flags );
assert( fd != -1 );
}
operator int ()
{
return fd;
}
~AutoFile()
{
if ( fd != -1 );
close( fd );
}
};
int main( void )
{
AutoFile fd( "./test.cpp", O_RDONLY );
struct stat st_buf;
int rs = fstat( fd, &st_buf );
assert( rs != -1 );
assert( st_buf.st_blksize > 0 );
cout << "Good I/O block size = " << st_buf.st_blksize << endl;
ssize_t count = (ssize_t) st_buf.st_blksize;
char *buf = new char[ count ];
assert( buf != NULL );
for ( ssize_t loaded; (loaded = readn( fd, buf, count )) > 0 ; )
{
ssize_t saved = writen( STDOUT_FILENO, buf, loaded );
assert( saved == loaded );
}
delete[] buf;
return 0;
}