hello world!
分类: C/C++
2011-07-18 07:50:41
8.1 File Descriptors:
In the most general case, before you read and write a file, you must inform the system of your intent to do so, a process called opening the file. If you are going to write on a file it may also be necessary to create it or to discard its previous contents. The system checks your right to do so (Does the file exist? Do you have permission to access it?) and if all is well, returns to the program a small non-negative integer called a file descriptor. Whenever input or output is to be done on the file, the file descriptor is used instead of the name to identify the file. (A file descriptor is analogous to the file pointer used by the standard library, or to the file handle of MS-DOS.) All information about an open file is maintained by the system; the user program refers to the file only by the file descriptor.
8.2 Low Level I/O-Read and Write:
Input and output uses the read and write system calls, which are accessed from C programs through two functions called read and write. For both, the first argument is a file descriptor. The second argument is a character array in your program where the data is to go to or to come from. The third argument is the number is the number of bytes to be transferred.
int n_read = read(int fd, char *buf, int n);
int n_written = write(int fd, char *buf, int n);
8.3 Open, Create, Close, Unlink:
Exercise 8-1. Rewrite the program cat from Chapter 7 using read, write, open, and close instead of their standard library equivalents. Perform experiments to determine the relative speeds of the two versions.
#include
#include
#include
#include
void fileCopy(int ifp, int ofp);
/***
cat: concatenate files, version 1
***/
int main(int argc, char *argv[])
{
int fp;
char *prog=argv[0];
if(argc==1) /* no args; copy standard input */
fileCopy(0,1);
else
while(--argc>0)
if((fp=open(*++argv,O_RDONLY))==-1){
fprintf(stderr,"%s: can't open %s \n",prog,*argv);
exit(1);
} else{
fileCopy(fp,1);
close(fp);
}
if(ferror(stdout)){
fprintf(stderr,"%s: error writing stdout\n",prog);
exit (2);
}
exit (0);
}
/* error: print an error meesage and die */
void error(char *fmt,...)
{
va_list args;
va_start(args,fmt);
fprintf(stderr,"error:");
vfprintf(stderr,fmt,args);
fprintf(stderr,"\n");
va_end(args);
exit (1);
}
/***
fileCopy: copy file ifp to file ofp
***/
void fileCopy(int ifp, int ofp)
{
int n;
char buf[BUFSIZ];
while((n=read(ifp,buf,BUFSIZ))>0)
if(write(ofp,buf,n)!=n)
error("cat:write error");
}
8.4 Random Access-Lseek:
Input and output are normally sequential: each read or write takes place at a position in the file right after the previous one. When necessary, however, a file can be read or written in any arbitrary order. The system call lseek provides a way to move around in a file without reading or writing any data:
long lseek(int fd, long offset, int origin);
sets the current position in the file whose descriptor is fd to offset, which is taken relative to the location specified by origin. Subsequent reading or writing will begin at that position. origin can be 0, 1, or 2 to specify that offset is to be measured from the beginning, from the current position, or from the end of the file respectively. For example, to append to a file (the redirection >> in the UNIX shell, or "a" for fopen), seek to the end before writing:
lseek(fd, 0L, 2);
To get back to the beginning (``rewind''),
lseek(fd, 0L, 0);
Notice the 0L argument; it could also be written as (long) 0 or just as 0 if lseek is properly declared.
8.4 Example-An implementation of Fopen and Getc:
Exercise 8-2. Rewrite fopen and _fillbuf with fields instead of explicit bit operations. Compare code size and execution speed.
Exercise 8-3. Design and write _flushbuf, fflush, and fclose.
#include
#include
#include "syscalls.h"
#define PERMS 0666
FILE *fopen(char *name, char *mode)
{
int fd;
FILE *fp;
if(*mode!='r'&&*mode!='w'&&*mode!='a')
return NULL;
for(fp=_iob;fp<_iob+OPEN_MAX;fp++)
if((fp->flag&(_READ | _WRITE))==0)
break; /* find free slot*/
if((fp>=_iob+OPEN_MAX)) /* no free slots*/
return NULL;
if(*mode=='w')
fd=creat(name,PERMS);
else if(*mode=='a'){
if((fd=open(name,O_WRONLY,0))==-1)
fd=creat(name,PERMS);
lseek(fd,0L,SEEK_END);
}else
fd=open(name,O_RDONLY,0);
if(fd==-1) /* couldn't access name*/
return NULL;
fp->fd=fd;
fp->cnt=0;
fp->base=NULL;
fp->flag=((*mode=='r')?_READ:_WRITE);
return fp;
}
/***
_fillbuf: allocate and fill input buffer
***/
int _fillbuf(FILE *fp)
{
int bufsize;
if((fp->flag&(_READ|_EOF|_ERR))!=_READ)
return EOF;
bufsize=(fp->flag & _UNBUF) ? 1:BUFSIZ;
if(fp->base=NULL)
if((fp->base=(char *)malloc(bufsize))==NULL)
return EOF; /*can't get buffer */
fp->ptr=fp->base;
fp->cnt=read(fp->fd,fp->ptr,bufsize); /* read BUFSIZ or 1 char(s)*/
if(--fp->cnt <0){
if(--fp->cnt==-1)
fp->flag|=EOF;
else
fp->flag|=_ERR;
fp->cnt=0;
return EOF;
}
return (unsigned char )*fp->ptr++;
}
/***
_flushbuf: allocate and flush output buffer
***/
int _flushbuf(int x ,FILE *fp)
{
unsigned nc; /* # of chars to flush */
int bufsize; /* size of buffer alloc */
if(fp<_iob||fp>=_iob+OPEN_MAX)
return EOF; /* error: invalid pointer*/
if((fp->flag&(_WRITE|_ERR))!=_WRITE)
return EOF;
bufsize=(fp->flag&_UNBUF)?1:BUFSIZ;
if(fp->base==NULL){
if((fp->base=(char *)malloc(bufsize))==NULL){
fp->flag|=_ERR; /* can't get buffer*/
return EOF;
}
}else{ /* buffer already exists*/
nc=fp->ptr-fp->base;
if(write(fp->fd,fp->base,nc)!=nc){
fp->flag|=_ERR;
return EOF;
}
}
fp->ptr=fp->base; /* beginning of buffer*/
*fp->ptr++=(char)x; /* save current char*/
fp->cnt=bufsize-1;
return x;
}
/***
fflush:fflush buffer associated with file fp
***/
int fflush(FILE *fp)
{
int rc =0;
if(fp<_iob || fp>=_iob+OPEN_MAX)
return EOF;
if(fp->flag&_WRITE)
rc=_flushbuf(0,fp);
fp->ptr=fp->base;
fp->cnt=(fp->flag&_UNBUF)?1:BUFSIZ;
return rc;
}
/***
fclose: fclose file
***/
int fclose(FILE *fp)
{
int rc;
if((rc=fflush(fp))!=EOF){ /* anything to flush*/
free(fp->base);
fp->ptr=NULL;
fp->cnt=0;
fp->base=NULL;
fp->flag&=(_READ|_WRITE);
}
return rc;
}
/***
fseek: seek with a file pointer
***/
int fseek(FILE *fp,long offset,int origin)
{
unsigned nc; /* # of chars to flush*/
long rc=0; /* return code */
if(fp->flag & _READ){
if(orgin==1)
offset-=fp->cnt; /*remember chars in buffer*/
rc=lseek(fp->fd,offset,origin);
fp->cnt=0;
}else if (fp->flag&_WRITE){
if((nc=fp->ptr-fp->base)>0)
if(write(fp->fd,fp->base,nc)!=nc)
rc=-1;
if(rc!=-1)
rc=lseek(fp->fd,offset,origin);
}
return (rc==-1)?-1:0;
}
8.5 Example-Listing Directories :
Let us begin with a short review of UNIX file system structure. A directory is a file that contains a list of filenames and some indication of where they are located. The ``location'' is an index into another table called the ``inode list.'' The inode for a file is where all information about the file except its name is kept. A directory entry generally consists of only two items, the filename and an inode number.
The Dirent structure contains the inode number and the name. The maximum length of filename component is NAME_MAX, which is a system-dependent value. opendir returns pointer to a structure called DIR, analogous to FILE, which is used by readdir and closed. This information is collected into a file called dirent.h.
#define NAME_MAX 14 /* longest filename component; */
/* system-dependent */
typedef struct { /* portable directory entry */
long ino; /* inode number */
char name[NAME_MAX+1]; /* name + '\0' terminator */
} Dirent;
typedef struct { /* minimal DIR: no buffering, etc. */
int fd; /* file descriptor for the directory */
Dirent d; /* the directory entry */
} DIR;
DIR *opendir(char *dirname);
Dirent *readdir(DIR *dfd);
void closedir(DIR *dfd);
The system call stat takes a filename and returns all of the information in the inode for that file, or -1 if there is an error. That is,
char *name;
struct stat stbuf;
int stat(char *, struct stat *);
stat(name, &stbuf);
fills the structure stbuf with the inode information for the file name. The structure describing the value returned by stat is in
struct stat /* inode information returned by stat */
{
dev_t st_dev; /* device of inode */
ino_t st_ino; /* inode number */
short st_mode; /* mode bits */
short st_nlink; /* number of links to file */
short st_uid; /* owners user id */
short st_gid; /* owners group id */
dev_t st_rdev; /* for special files */
off_t st_size; /* file size in characters */
time_t st_atime; /* time last accessed */
time_t st_mtime; /* time last modified */
time_t st_ctime; /* time originally created */
};
Most of these values are explained by the comment fields. The types like dev_t and ino_t are defined in
The st_mode entry contains a set of flags describing the file. The flag definitions are also included in
#define S_IFMT 0160000 /* type of file: */
#define S_IFDIR 0040000 /* directory */
#define S_IFCHR 0020000 /* character special */
#define S_IFBLK 0060000 /* block special */
#define S_IFREG 0010000 /* regular */
/* ... */
Now we are ready to write the program fsize. If the mode obtained from stat indicates that a file is not a directory, then the size is at hand and can be printed directly. If the name is a directory, however, then we have to process that directory one file at a time; it may in turn contain sub-directories, so the process is recursive.
Exercise 8-5. Modify the fsize program to print the other information contained in the inode entry.
#include
#include
#include
#include
#include
#include
#include
void fsize(char *);
void dirwalk(char *dir,void (*fcn)(char *));
int main(int argc, char *argv[])
{
if(argc==1)
fsize(".");
else
while(--argc>0)
fsize(*++argv);
return 0;
}
#define MAX_PATH 1024
/***
dirwalk: apply fcn to all files in dir
***/
void dirwalk(char *dir,void (*fcn)(char *))
{
char name[MAX_PATH];
struct dirent *dp;
DIR *dfd; /* 目录流 */
if((dfd=opendir(dir))==NULL){
fprintf(stderr,"dirwalk: can't open %s\n",dir);
return ;
}
while ((dp=readdir(dfd))!=NULL){
if(strcmp(dp->d_name,".")==0 || strcmp(dp->d_name,"..")==0)
continue; /* skip self and parent*/
if(strlen(dir)+strlen(dp->d_name)+2>sizeof(name))
fprintf(stderr,"dirwalk: name %s %s too long\n",dir,dp->d_name);
else {
sprintf(name,"%s/%s",dir,dp->d_name);
(*fcn)(name);
}
}
closedir(dfd);
}
/***
fsize: printf the name of file "name"
***/
void fsize(char *name)
{
struct stat stbuf;
if(stat(name,&stbuf)==-1){
fprintf(stderr,"fsize:cant't access %s\n",name);
return ;
}
if((stbuf.st_mode & S_IFMT)==S_IFDIR) /* is a direction*/
dirwalk(name,fsize);
printf("%8d %s\n",stbuf.st_size,name);
}