File I/O

File I/O operations are basically open files, read files, write files, and close files. Most operations only use open, read, write, lseek, and close files.

The open function

#include <fcntl.h>
int open(const char*path, int oflag, ...);
int openat(int fd, const char *path, int oflag, ...);
// Returns the file descriptor on success, -1 on failure
Copy the code

Path specifies the file name. Oflag can specify the behavior of a function

Creat a function

The creat function creates a new file

#include <fcntl.h>
int create(const char *path, mode_t mode);
// Returns the file descriptor on success, -1 on failure
/ / equivalent to the open (path, O_WRONLY | O_CREAT | O_TRUNC, mode).
Copy the code

The disadvantage of create is that it creates a file in write-only mode

The close

Close an open file

#include <fcntl.h>
int close(int fd);
Return 0 on success, -1 on failure
Copy the code

Closing a file releases all record locks that the process has placed on the file. When a process terminates, the kernel automatically closes all open files

Lseek function

Each open file has a current file offset associated with it. It is usually a non-negative integer that measures the number of bytes counted from the beginning of the file. Typically, read and write operations start at the current file offset and increase the offset by the number of bytes read and written. By default, when opening a file, the offset is set to 0 unless the O_APPEND option is specified. Lseek can explicitly set an offset for an open file.

off_t lseek(int fd, off_t offset, int whence);
// Returns the new file offset on success, -1 on failure
Copy the code

whence parameter affects the interpretation of offset

Test the examples in the book

  #include "apue.h"
 
  int main(void)
  {
      if (lseek(STDIN_FILENO, 0, SEEK_CUR) == - 1)
          printf("cannot seek\n");
      else
          printf("seek OK\n");
      exit(0);
  }
Copy the code

Let’s compile and test it

$ ./a.out < /etc/passwd
seek OK
$ ./a.out < /etc/passwd| ./a.out
cannot seek
Copy the code

Note that some offsets may be negative, so do not test lseek’s return value for less than 0, but for -1


An example from the test book

#include "apue.h"
#include <fcntl.h>

char	buf1[] = "abcdefghij";
char	buf2[] = "ABCDEFGHIJ";

int
main(void)
{
	int		fd;

	if ((fd = creat("file.hole", FILE_MODE)) < 0)
		err_sys("creat error");

	if (write(fd, buf1, 10) != 10)
		err_sys("buf1 write error");
	/* offset now = 10 */

	if (lseek(fd, 16384, SEEK_SET) == - 1)
		err_sys("lseek error");
	/* offset now = 16384 */

	if (write(fd, buf2, 10) != 10)
		err_sys("buf2 write error");
	/* offset now = 16394 */

	exit(0);
}
Copy the code

Let’s test it out

$ gcc hole.c -lapue $ ./a.out $ ls -l file.hole -rw-r--r-- 1 yuanzhihong yuanzhihong 16394 Jun 6 22:03 file.hole $ od -c  file.hole 0000000 a b c d e f g h i j \0 \0 \0 \0 \0 \0 0000020 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 * 0040000 A B C D E F G H I J 0040012Copy the code

The functions read and write

#include<unistd.h>
ssize_t read(int fd, void *buf, size_t nbytes);
ssize_t write(int fd, void *buf, size_t nbytes);
// Returns the number of bytes read or written. Return -1 on error
Copy the code

Sometimes you don’t read that many bytes. The buffer in the network, for example, has reached the end of the file.

Process shared file entry (I node)

Atomic operation

#include <unistd.h>
ssize_t pread(int fd, void *buf, size_t nbytes, off_t offset);
ssize_t pwrite(int fd, const void *buf, size_t nbytes, off_t offset);
// Returns the number of bytes read/written, -1 on error
Copy the code

Dup and dup2

Used to copy a file descriptor

#include<unistd.h> int dup(int fd); int dup2(int fd, int fd2); // Returns the new file descriptor. Return -1 on errorCopy the code

Dup is equivalent to copying an original file descriptor. Dup2 is equivalent to specifying fd2 as a copy fd. If fd2 is already on, fd2 is turned off first.

The functions sync, fsync, fdatasync

All three functions are used to clear buffers

#include<unistd.h>
int fsync(int fd);
int fdatasync(int fd);
// Return 0 for success, -1 for error
void sync(void);
Copy the code

An FCNTL function

#include<fcntl.h> int fcntl(int fd, int cmd, ... /* int arg */); // Return -1 on errorCopy the code

The FCNTL function has five functions

  • 1, copy an existing descriptor, CMD =F_DUPFDOr CMD =F_DUPFD_CLOEXEC
  • 2, get/set file descriptor, CMD =F_GETFDOr CMD =F_SETFD
  • 3, get/set the file descriptor state, CMD =F_GETFLOr CMD =F_SETFL
  • Get/set asynchronous I/O ownership, CMD =F_GETOWNOr CMD =F_SETOWN
  • Get/set log lock, CMD =F_GETLKOr CMD =F_SETLKOr CMD =F_SETLKW

For an example from the book, the first parameter specifies the file descriptor and prints the selected file flag description for that descriptor

#include "apue.h"
#include <fcntl.h>

int
main(int argc, char *argv[])
{
	int		val;

	if(argc ! =2)
		err_quit("usage: a.out <descriptor#>");

	if ((val = fcntl(atoi(argv[1]), F_GETFL, 0))"0)
		err_sys("fcntl error for fd %d", atoi(argv[1]));

	switch (val & O_ACCMODE) {
	case O_RDONLY:
		printf("read only");
		break;

	case O_WRONLY:
		printf("write only");
		break;

	case O_RDWR:
		printf("read write");
		break;

	default:
		err_dump("unknown access mode");
	}

	if (val & O_APPEND)
		printf(", append");
	if (val & O_NONBLOCK)
		printf(", nonblocking");
	if (val & O_SYNC)
		printf(", synchronous writes");

#if! defined(_POSIX_C_SOURCE) && defined(O_FSYNC) && (O_FSYNC ! = O_SYNC)
	if (val & O_FSYNC)
		printf(", synchronous writes");
#endif

	putchar('\n');
	exit(0);
}
Copy the code

Let’s compile and run

$gcc fileflags.c -lapue
$./a.out 0 < /dev/tty               #0 represents standard input
read only
$ ./a.out 1 > temp.foo              #1 represents standard output
$ cat temp.foo                      
write only
$ ./a.out 2  2>>temp.foo 
write only, append
$ ./a.out 5 5<>temp.foo            # 5<> opens the file temp. Foo on file descriptor 5 for reading and writing
read write
Copy the code

Let’s look at another function from the book. A function that sets one or more file status flags for a file descriptor

#include "apue.h"
#include <fcntl.h>

void
set_fl(int fd, int flags) /* flags are file status flags to turn on */
{
	int		val;

	if ((val = fcntl(fd, F_GETFL, 0))"0)
		err_sys("fcntl F_GETFL error");

	val |= flags;		/* turn on flags */

	if (fcntl(fd, F_SETFL, val) < 0)
		err_sys("fcntl F_SETFL error");
}
Copy the code

Get the state of the file descriptor, then do or with the flag to set, and then set back to the original file descriptor