Chinaunix首页 | 论坛 | 博客
  • 博客访问: 306340
  • 博文数量: 63
  • 博客积分: 1482
  • 博客等级: 上尉
  • 技术积分: 1185
  • 用 户 组: 普通用户
  • 注册时间: 2011-03-12 19:06
个人简介

hello world!

文章分类

全部博文(63)

分类: C/C++

2011-07-18 07:50:41

The C programming language –第八章学习 Chapter 8 – The Linux System Interface

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     //close(), read(), write(),

#include      //some FLAGS: O_RDNOLY,O_WRONLY,O_RDWR...

 

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 , and typically looks like this:

   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 , which must be included too.

The st_mode entry contains a set of flags describing the file. The flag definitions are also included in ; we need only the part that deals with file type:

   #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              /* 文件的打开标识符 mode_t*/

#include             /* 文件读写打开关闭函数和lseek标识符*/

#include          /* 数据类型定义 dev_t ...*/

#include           /* 描述文件标志,目录结构 stat...*/

#include             /* 目录访问相关 opendir... */

 

 

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);

}

 

 

阅读(1498) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~