📄 chapter8.html
字号:
<html><head><title>Chapter 8 - The UNIX System Interface</title></head><body><hr><p align="center"><a href="chapter7.html">Back to Chapter 7</a> -- <a href="kandr.html">Index</a> -- <a href="appa.html">Appendix A</a><p><hr><h1>Chapter 8 - The UNIX System Interface</h1>The UNIX operating system provides its services through a set of <em>systemcalls</em>, which are in effect functions within the operating system that may becalled by user programs. This chapter describes how to use some of the mostimportant system calls from C programs. If you use UNIX, this should bedirectly helpful, for it is sometimes necessary to employ system calls formaximum efficiency, or to access some facility that is not in the library.Even if you use C on a different operating system, however, you should beable to glean insight into C programming from studying these examples;although details vary, similar code will be found on any system. Since theANSI C library is in many cases modeled on UNIX facilities, this code mayhelp your understanding of the library as well.<p>This chapter is divided into three major parts: input/output, file system,and storage allocation. The first two parts assume a modest familiarity withthe external characteristics of UNIX systems.<p><a href="chapter7.html">Chapter 7</a> was concerned with an input/outputinterface that is uniform across operating systems. On any particular systemthe routines of the standard library have to be written in terms of thefacilities provided by the host system. In the next few sections we willdescribe the UNIX system calls for input and output, and show how parts ofthe standard library can be implemented with them.<h2><a name="s8.1">8.1 File Descriptors</a></h2>In the UNIX operating system, all input and output is done by reading orwriting files, because all peripheral devices, even keyboard and screen, arefiles in the file system. This means that a single homogeneous interfacehandles all communication between a program and peripheral devices.<p>In the most general case, before you read and write a file, you must informthe system of your intent to do so, a process called <em>opening</em> the file.If you are going to write on a file it may also be necessary to create it orto discard its previous contents. The system checks your right to do so (Doesthe 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 <em>filedescriptor</em>. Whenever input or output is to be done on the file, the filedescriptor is used instead of the name to identify the file. (A filedescriptor 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 ismaintained by the system; the user program refers to the file only by thefile descriptor.<p>Since input and output involving keyboard and screen is so common, specialarrangements exist to make this convenient. When the command interpreter (the``shell'') runs a program, three files are open, with file descriptors 0, 1,and 2, called the standard input, the standard output, and the standarderror. If a program reads 0 and writes 1 and 2, it can do input and outputwithout worrying about opening files.<p>The user of a program can redirect I/O to and from files with < and >:<pre> prog <infile >outfile</pre>In this case, the shell changes the default assignments for the filedescriptors 0 and 1 to the named files. Normally file descriptor 2 remainsattached to the screen, so error messages can go there. Similar observationshold for input or output associated with a pipe. In all cases, the fileassignments are changed by the shell, not by the program. The program doesnot know where its input comes from nor where its output goes, so long as ituses file 0 for input and 1 and 2 for output.<h2><a name="s8.2">8.2 Low Level I/O - Read and Write</a></h2>Input and output uses the <tt>read</tt> and <tt>write</tt> system calls,which are accessed from C programs through two functions called <tt>read</tt>and <tt>write</tt>. For both, the first argument is a file descriptor. Thesecond argument is a character array in your program where the data is to goto or to come from. The third argument is the number is the number of bytesto be transferred.<pre> int n_read = read(int fd, char *buf, int n); int n_written = write(int fd, char *buf, int n);</pre>Each call returns a count of the number of bytes transferred. On reading, thenumber of bytes returned may be less than the number requested. A returnvalue of zero bytes implies end of file, and <tt>-1</tt> indicates an error ofsome sort. For writing, the return value is the number of bytes written; anerror has occurred if this isn't equal to the number requested.<p>Any number of bytes can be read or written in one call. The most commonvalues are 1, which means one character at a time (``unbuffered''), and anumber like 1024 or 4096 that corresponds to a physical block size on aperipheral device. Larger sizes will be more efficient because fewer systemcalls will be made.<p>Putting these facts together, we can write a simple program to copy its inputto its output, the equivalent of the file copying program written for<a href="chapter1.html">Chapter 1</a>. This program will copy anything toanything, since the input and output can be redirected to any file or device.<pre> #include "syscalls.h" main() /* copy input to output */ { char buf[BUFSIZ]; int n; while ((n = read(0, buf, BUFSIZ)) > 0) write(1, buf, n); return 0; }</pre>We have collected function prototypes for the system calls into a file called<tt>syscalls.h</tt> so we can include it in the programs of this chapter.This name is not standard, however.<p>The parameter <tt>BUFSIZ</tt> is also defined in <tt>syscalls.h</tt>; itsvalue is a good size for the local system. If the file size is not a multipleof <tt>BUFSIZ</tt>, some <tt>read</tt> will return a smaller number of bytesto be written by <tt>write</tt>; the next call to <tt>read</tt> after thatwill return zero.<p>It is instructive to see how <tt>read</tt> and <tt>write</tt> can be used toconstruct higher-level routines like <tt>getchar</tt>, <tt>putchar</tt>, etc.For example, here is a version of <tt>getchar</tt> that does unbuffered input,by reading the standard input one character at a time.<pre> #include "syscalls.h" /* getchar: unbuffered single character input */ int getchar(void) { char c; return (read(0, &c, 1) == 1) ? (unsigned char) c : EOF; }</pre><tt>c</tt> must be a <tt>char</tt>, because <tt>read</tt> needs a characterpointer. Casting <tt>c</tt> to <tt>unsigned char</tt> in the return statementeliminates any problem of sign extension.<p>The second version of <tt>getchar</tt> does input in big chunks, and hands outthe characters one at a time.<pre> #include "syscalls.h" /* getchar: simple buffered version */ int getchar(void) { static char buf[BUFSIZ]; static char *bufp = buf; static int n = 0; if (n == 0) { /* buffer is empty */ n = read(0, buf, sizeof buf); bufp = buf; } return (--n >= 0) ? (unsigned char) *bufp++ : EOF; }</pre>If these versions of <tt>getchar</tt> were to be compiled with <tt><stdio.h></tt>included, it would be necessary to <tt>#undef</tt> the name <tt>getchar</tt> incase it is implemented as a macro.<h2><a name="s8.3">8.3 Open, Creat, Close, Unlink</a></h2>Other than the default standard input, output and error, you must explicitlyopen files in order to read or write them. There are two system calls forthis, <tt>open</tt> and <tt>creat</tt> [sic].<p><tt>open</tt> is rather like the <tt>fopen</tt> discussed in<a href="chapter7.html">Chapter 7</a>, except that instead of returning afile pointer, it returns a file descriptor, which is just an <tt>int</tt>.<tt>open</tt> returns <tt>-1</tt> if any error occurs.<pre> #include <fcntl.h> int fd; int open(char *name, int flags, int perms); fd = open(name, flags, perms);</pre>As with <tt>fopen</tt>, the <tt>name</tt> argument is a character stringcontaining the filename. The second argument, <tt>flags</tt>, is an <tt>int</tt>that specifies how the file is to be opened; the main values are<p><table align="center" border=0><td><tt>O_RDONLY</tt></td><td>open for reading only</td><tr><td><tt>O_WRONLY</tt></td><td>open for writing only</td><tr><td><tt>O_RDWR </tt></td><td>open for both reading and writing</td></table><p>These constants are defined in <tt><fcntl.h></tt> on System V UNIXsystems, and in <tt><sys/file.h></tt> on Berkeley (BSD) versions.<p>To open an existing file for reading,<pre> fd = open(name, O_RDONLY,0);</pre>The <tt>perms</tt> argument is always zero for the uses of <tt>open</tt> thatwe will discuss.<p>It is an error to try to open a file that does not exist. The system call<tt>creat</tt> is provided to create new files, or to re-write old ones.<pre> int creat(char *name, int perms); fd = creat(name, perms);</pre>returns a file descriptor if it was able to create the file, and <tt>-1</tt> ifnot. If the file already exists, <tt>creat</tt> will truncate it to zero length,thereby discarding its previous contents; it is not an error to <tt>creat</tt> afile that already exists.<p>If the file does not already exist, <tt>creat</tt> creates it with thepermissions specified by the <tt>perms</tt> argument. In the UNIX file system,there are nine bits of permission information associated with a file thatcontrol read, write and execute access for the owner of the file, for theowner's group, and for all others. Thus a three-digit octal number isconvenient for specifying the permissions. For example, <tt>0775</tt> specifiesread, write and execute permission for the owner, and read and executepermission for the group and everyone else.<p>To illustrate, here is a simplified version of the UNIX program <tt>cp</tt>,which copies one file to another. Our version copies only one file, it doesnot permit the second argument to be a directory, and it invents permissionsinstead of copying them.<pre> #include <stdio.h> #include <fcntl.h> #include "syscalls.h" #define PERMS 0666 /* RW for owner, group, others */ void error(char *, ...); /* cp: copy f1 to f2 */ main(int argc, char *argv[]) { int f1, f2, n; char buf[BUFSIZ]; if (argc != 3) error("Usage: cp from to"); if ((f1 = open(argv[1], O_RDONLY, 0)) == -1) error("cp: can't open %s", argv[1]); if ((f2 = creat(argv[2], PERMS)) == -1) error("cp: can't create %s, mode %03o", argv[2], PERMS); while ((n = read(f1, buf, BUFSIZ)) > 0) if (write(f2, buf, n) != n) error("cp: write error on file %s", argv[2]); return 0; }</pre>This program creates the output file with fixed permissions of <tt>0666</tt>.With the <tt>stat</tt> system call, described in <a href="#s8.6">Section 8.6</a>,we can determine the mode of an existing file and thus give the same mode tothe copy.<p>Notice that the function <tt>error</tt> is called with variable argumentlists much like <tt>printf</tt>. The implementation of error illustrates how
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -