510 likes | 645 Views
Advanced UNIX. 240-491 Special Topics in Comp. Eng. 2 Semester 2, 2000-2001. Objectives look at low-level operations for handling files. 16. Low-level File I/O. Overview. 1. Basic Operations 2. Why use Low-level Operations? 3. A Simple Example 4. File Descriptors
E N D
Advanced UNIX 240-491 Special Topics in Comp. Eng. 2Semester 2, 2000-2001 • Objectives • look at low-level operations for handling files 16. Low-level File I/O
Overview 1. Basic Operations 2. Why use Low-level Operations? 3. A Simple Example 4. File Descriptors 5. File Permissions Again continued
6. open() 12. lseek() 7. creat() 13. Hotel Guests 8. close() 14. unlink() 9. read() 15. fcntl() 10. write() 16. Duplicating FDs 11. copyfile 17. File Pointers
1. Basic Operations • Name Meaningopen() Opens a file for reading or writing.creat() Creates an empty file for writing.close() Closes an open file.read() Reads data from a file.write() Writes data to a file.lseek() Moves to a specified byte in file.unlink() Removes a file.
2. Why use Low-level Operations? • No buffering of I/O • useful for network programming and reading/writing to certain peripherals (e.g. tape drives) • No conversion of I/O • no transformation of input to integers, floats, etc.; output can be any byte sequence • Building blocks for more complex I/O.
3. A Simple Example: oprd.c #include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>void main(){ int fd, nread; char buf[1024]; if ((fd = open("data-file", O_RDONLY)) < 0) { perror("open"); exit(-1); } nread = read(fd, buf, 1024); close(fd); buf[nread] = '\0'; /* so can print */ printf("buf: %s\nnread: %d\n", buf, nread);}
4. File Descriptors • A file descriptor is a small, non-negative integer. • A file descriptor identifies an open file to the low-level operations. • Standard descriptors: 0 represents stdin1 stdout2 stderr
5. File Permissions Again • Earlier chmod examples used letters to represent permissions: • e.g. chmod a+r file • Most low-level operations (and chmod) represent permissions as octal values: • e.g. chmod 0644 file
File Permissions as Octals Octal Symbolic Meaning of Permission0400 r-- --- ---0200 -w- --- ---0100 --x --- ---0040 --- r-- ---0020 --- -w- ---0010 --- --x ---0004 --- --- r--0002 --- --- -w-0001 --- --- --x
Example • Add octal values together to get the complete file permission:Octal Meaning0400 r-- --- --- 0200 -w- --- --- 0040 + + --- r-- --- 0004 --- --- r-- ==== =========== 0644 rw- r-- r-- • Use in chmod: chmod 0644 file
6. open() an octal value (as in chmod), used when creating a file • #include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>int open(char *pathname, int flag /*, mode_t mode */ ); • Return file descriptor if ok, -1 for error
Some open() Flags O_RDONLY Open file for reading only.O_WRONLY Open file for writing only.O_RDWR Open file for reading & writing.Possibly combined with:O_APPEND Append to file when writing.O_CREAT Create file if is does not exist (requires the 3rd modeargument).O_EXCL Return -1 error if file is to be created and already exists.
Using Bitwise OR • open()flags can be combined with the bitwise OR operator ‘|”: fd = open(file, O_WRONLY | O_APPEND); • Now each write(fd, buf, BUFSIZE)means append to the end of the file.
7. creat() • #include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>int creat(char *pathname, mode_t mode); • Creates a new file, or truncates an old one. • Return file descriptor if ok, -1 on error.
mode gives access permission of resulting new file:0644 (rw- r-- r--) • mode only has meaning if the file is new • so it cannot be used to change the mode of an existing file
Create a new file #include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>void main(){ int fd; if ((fd = creat(“new-file”,0644)) < 0){ printf(“cannot create new-file\n”); exit(1); } /* rest of program */
Replacing creat()with open() • creat() only opens a file for writing; to read it the file must be closed and opened for reading. • creat() can be replaced by open():open(“new-file”, O_WRONLY | O_CREAT | O_TRUNC, 0644)
Other Versions • creat() version which can read and write:open(“new-file”, O_RDWR | O_CREAT | O_TRUNC, 0644) • Avoid truncation of old file (append instead): open(“new-file”, O_WRONLY|O_CREAT|O_APPEND, 0644)
One User at a Time • Create lock if it doesn’t already exist, otherwise fail (and return -1): fd = open(“lock”, O_WRONLY|O_CREAT|O_EXCL, 0644); • Use lock to protect access to another file • open “accounts” only if lock can be opened • after processing “accounts”, delete lock
Diagram of Locking program 1 program 2 "lock" file :if ((fd = open("lock", O_WRONLY | O_CREAT | O_EXCL, 0644)) < 0)) do nothingelse { open "accounts" : close "accounts" delete "lock"} :if ((fd = open("lock", O_WRONLY | O_CREAT | O_EXCL, 0644)) < 0)) do nothingelse { open "accounts" : close "accounts" delete "lock"} "accounts" file
8. close() • #include <unistd.h>int close(int fd); • Close a file: return 0 if ok, -1 on error. • Useful since the number of open files is limited (~20)
9. read() • #include <unistd.h>int read(int fd, void *buffer, unsigned int nbytes); • Attempts to read nbytes into the buffer array • no conversion of bytes (characters) • may read less than specified amount (e.g. at end) • Returns the number of bytes read, 0 if end of file, -1 on error.
The Read-Write Pointer • The R-W Pointer records the position of the next byte in the file to be read (or written). • It is implicit (hidden) in each use of read() and write() read(..., n) 1 1+n
Count Characters: countchars.c #include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#define SIZE 512 /* block of chars read at a time */int main(){ char buffer[SIZE]; int fd, j; : continued
long total = 0; if ((fd = open(“foo”, O_RDONLY)) < 0){ printf(“cannot open foo\n”); exit(1); } while((j = read(fd, buffer, SIZE)) > 0) total += j; printf(“total chars: %ld\n”, total);close(fd); return 0;}
Notes • UNIX systems are often configured to read/write in blocks of 512 or 1024 bytes (the diskblocking factor). • Can use BUFSIZ from stdio.h which contains a default disk blocking factor.
10. write() • #include <unistd.h>int write(int fd, void *buffer, unsigned int nbytes); • Returns number of bytes written if ok, -1 on error. • write() starts at the current R-W pointer position, and pointer is moved as file is written.
Example :int fd;char header1[512], header2[512]; :if ((fd = creat(“foo”, 0644)) != -1) :write(fd, header1, 512);write(fd, header2, 512); :
11. copyfile #include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>int copyfile(char *nm1, char *nm2);int main(){ int result; result = copyfile(“test.in”, “test.out”); printf("Result code of copy: %d\n", result); return 0;} continued
int copyfile(char *nm1, char *nm2){ int infd, outfd, nread; char buffer[BUFSIZ]; if ((infd = open(nm1,O_RDONLY))< 0) return -1; if ((outfd = creat(nm2,0644)) < 0){ close(infd); return -2; } : continued
while ((nread = read(infd, buffer,BUFSIZ)) > 0) { if (write(outfd, buffer, nread) < nread) { close(infd); close(outfd); return -3; /* write error */ } } close(infd); close(outfd); return 0;}
copyfile() & BUFSIZ $ time copyfile • BUFSIZ Real time User time System time1 3:42.8 4.1 3:24.864 0:27.3 0.0 0:05.1511 0:24.0 0.0 0:01.9512 0:22.3 0.0 0:01.0513 0:25.1 0.0 0:02.44096 0:13.3 0.0 0:00.98192 0:12.9 0.0 0:01.1 • Most saving is made by reducing no. of system calls.
12. lseek() • #include <sys/types.h>#include <unistd.h>long int lseek(int fd, long int offset, int start); • Change position of R-W pointer: return new file offset if ok, -1 on error. • Start names (and numbers): • SEEK_SET 0 start of file • SEEK_CUR 1 current R-W pointer posn • SEEK_END 2 end of file
Examples • :fd = open(filename, O_RDWR);lseek(fd, 0L, SEEK_END);write(fd, buffer, BUFSIZ);: • /* get filesize in bytes */long int filesize;filesize = lseek(fd, 0L, SEEK_END);
13. Hotel Guests • The guests file has the format: 1 2 3 5000 . . . . . . . . . . name of guest, ending in '\n'; always 41 chars
Task • Write the function: int get_guest(int fd, int room_num, char *name); • Return 1 if the guest in room_num is found (name is also bound); 0 otherwise.
Code #include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#define NAMELEN 41#define NROOMS 5000 : continued
int main(){ int fd, i; char name[NAMELEN]; if ((fd = open(“guests”,O_RDONLY))== -1){ fprintf(stderr,“Cannot read guests\n”); exit(1); } for (i=1; i <= NROOMS; i++) if (get_guest(fd, i, name) == 1) printf(“Room %d, %s\n”, i, name); else printf(“Error in room %d\n”, i); return 0;} continued
int get_guest(int fd, int rnum, char *nm){ long int offset; int nread;offset = (rnum-1)*NAMELEN; if (lseek(fd, offset, SEEK_SET) < 0) return 0; nread = read(fd, nm, NAMELEN); if (nread < NAMELEN) return 0; /* read() error */ else nm[nread-1] = ‘\0’; /* overwrite \n */ return 1;}
14. unlink() • #include <unistd.h>int unlink(char *pathname); • Remove a file: return 0 of ok, -1 on error. • e.g.unlink(“/tmp/tmpfile”); • Must have permissions to execute and write in the file’s directory • for the cd and change to the contents
15. fcntl() • #include <sys/types.h>#include <unistd.h>#include <fcntl.h>int fcntl(int fd, int cmd, int arg); • Used to view/alter the behaviour of the file. • Two (of 10) cmd values: F_GETFL get current status flags F_SETFL set a status flag
Some File Status Flags for fcntl() Flag DescriptionO_RDONLY Open for reading only (0).O_WRONLY Open for writing only (1).O_RDWR Open for reading and writing (2).O_APPEND Append on each write. :
Using O_ACCMODE • O_RDONLY, O_WRONLY and O_RDWR are not separate bits (e.g. not 0, 2, 4), and so their values can be hard to test. • One solution is to use the O_ACCMODE mask.
:int accmode, val;if ((val = fcntl(fd, F_GETFL, 0)) < 0) printf(“Some error\n”);else { accmode = val & O_ACCMODE; if (accmode == O_RDONLY) printf(“read”); else if (accmode == O_WRONLY) printf(“write”); else if (accmode == O_RDWR) printf(“read write”); else printf(“access mode error”); if (val & O_APPEND) printf(“, append”); :
Using F_SETFL • Main use is to change O_APPEND: if (fcntl(fd, F_SETFL, O_APPEND) < 0) printf(“Setting append failed\n”);else ...
16. Duplicating File Descriptors • #include <unistd.h>int dup(int fd); • Make a new file descriptor (fd2) refer to the same file as fd: fd2 = dup(fd) • Useful for concurrent programming, where two processes refer to the same file.
Duplicating Diagram fd2 = dup(fd); file file fd fd 3 3 fd2 4
Another Approach • #include <unistd.h>int dup2(int fd, int fd2); • Make the existingfd2 descriptor refer to the same file as fd. • If fd2 is already open, close its file before redirecting it.
Main use is for redirecting stdio: dup2(fd, 0) • Same as the ‘hack’: close(0); dup(fd);
dup2(fd,0) Diagram Before After program program file file fd fd stdin 0