File and I/O system calls int open(const char* path, int flags, mode_t modes) int creat(const char *path, mode_t mode) ssize_t read(int fd, void *buf, size_t nbyte) ssize_t write(int fd, const void *buf, size_t nbyte) int close(int fd) off_t lseek(int fd, off_t offset, int reference) int stat(const char *file_name, struct stat *buf)
Each process records all the files opened in its context, the file descriptor table in the process table entry. Process context process descriptor file descriptor table File info file descriptor table contains all the files opened by the process
open/creat: find the first empty slot in the file descriptor table, use the slot to store a pointer to the file information. –Linear search close(fd) : among other things, makes the entry fd in the file descriptor table available. read/write: get the file information (such as current position) through the file descriptor table.
//example1.cpp #include using namespace std; main() { int f1, f2, f3, f4, f5, f6; f1 = open(“open.cpp”, O_RDONLY); cout << “f1 = “ << f1 << “\n”; f2 = open(“open.cpp”, O_RDONLY); cout << “f2 = “ << f2 << “\n”; f3 = open(“open.cpp”, O_RDONLY); cout << “f3 = “ << f3 << “\n”; close(f1); f4 = open(“open.cpp”, O_RDONLY); cout << “f4 = “ << f4 << “\n”; } Why f1 = 3, but not 1?
First three slots in the file descriptor table are occupied for standard I/O operation when the process is created. –Slot 0 ==> for standard input. –Slot 1 ==> for standard output. –Slot 2 ==> for standard error. Every time UNIX searches for an available slot in the open file table, it does a linear search starting from slot 0. The return of the first open == ?
Slots 0, 1, and 2 in the file descriptor table –used for standard I/O – They can be used just like regular files. Main() { main() { cout write(1, “hello, world!!\n”, } strlen(“hello, world!!\n”); } // example2.cpp –no need to open and close files 0, 1, 2 -- taken care of by the OS when it creates the process.
Let us redo the following program with read calls: main() { int I; cin >> I; cout << I; } See example2a.cpp
What if we close the standard I/O files? See example2b.cpp. Lost I/O capability!!
What if we close the standard I/O files and open files again? What is return value of the open call? See example4.cpp Now what happens when you do cout in example4.cpp? –This is called I/O redirection, instead of read from keyboard and print to the screen, cin will read from a file and cout will print to a file!!
#include main() { int f1, f2, f3, f4, f5, f6; f1 = open(“open.cpp”, O_RDONLY); cout << “f1 = “ << f1 << “\n”; f2 = open(“open.cpp”, O_RDONLY); cout << “f2 = “ << f2 << “\n”; close(1); f3 = open(“aaa”, O_RDONLY | O_WRONLY | O_CREAT, 0666); cout << “f3 = “ << f3 << “\n”; cerr << “f3 = “ << f3 << “\n”; } // example4.cpp, Output redirection What happens in This program?
Another way of redirect a file into standard I/O Duplicate an file descriptor: dup() #include int dup(int fd); return a new file descriptor having the common open file, file pointer and access mode with fd. Note: UNIX does a linear search in the file descriptor table for the available slot.
Difference between dup and open/create (Why dup instead of open) Using dup, the file position of the two duplicated open files is shared. Using open one file two times, there are two file positions for the two open files (open file != file) See extra1.cpp and extra2.cpp.
Using dup() to do standard I/O redirection. Implementing I/O redirection #include main() { char buf[100]; int fd; fd = open(“tmp111”, O_CREAT|O_WRONLY, 00777); close(1); dup(fd); close(fd); cout << “what is going on?\n”; cerr << “ \n”; } // example5.cpp
execv system call revisit: format: int execv(const char *path, char * argv[]) Wipe out the original process and load the new executable path into memory and execute path. When the system call execv is successful, every thing after the execv call in the original program will NOT be executed. Wipe out everything??? NOT precise!!!!! The open file descriptor table remains unchanged -->implication? If you do something to the file descriptor table before you call execv, you can redirect the standard I/O in the command to be executed!!!
void runcommand(char *cmd, char*argv[], char* readfile, char* writefile) { int status; if (fork() == 0) { if (readfile != NULL) { int fd = open(readfile, O_RDONLY); if (fd == -1) {cerr << “error\n”, exit(0);} close(0); dup(fd); close(fd); } if (writefile != NULL) { int fd = open(writefile, O_CREAT | O_WRONLY, 00777); if (fd == -1) {cerr << “error\n”, exit(0);} close(1); dup(fd); close(fd); } int stat = execv(cmd, argv); if (stat == (-1)) {cerr << “error\n”, exit(10);} } int stat = wait(&status); if (stat == -1) {cerr << “error return from wait\n”;} } // example6.cpp Running cmd with I/O redirection