Pipe n n n Pipe is a halfduplex
- Slides: 38
Pipe n n n Pipe is a half-duplex communication channel between processes that have parent-child relation. Pipe() opens a pipe for reading at one end, and writing at the other. After creating a pipe a process usually creates another copy by fork, so that they share the pipe. Usually the parent and the child will decide whether to read from or write to the pipe, and will close the other end that they do not need. 2
The 5 basic Syscalls for I/O 5. int open(char *path, int flags [ , int mode ] ); (check man –s 2 open) int close(int fd); int read(int fd, char *buf, int size); int write(int fd, char *buf, int size); off_t lseek(int fd, off_t offset, int whence); n fd: file descriptor, a ressource handler for a file, represented as an int 1. 2. 3. 4. n 3
How it works ? foo. exe mozilla 0 x. FFFF stack a fd is an index in an OS table forbidden kernel … File descriptor tables heap fd = open(“/tmp/foo. txt“); 0 x 0000 push 0 x 010 F mov ax, 3 int 0 x 80 mov bx, ax sys_close(){…} sys_open(){…} interrupt table code process can not forge fds will check if foo. exe can access foo. txt 4
Standard Input, Output and Error n Now, every process in Unix starts out with three file descriptors predefined: q q q n You can read from standard input, using read(0, . . . ), and write to standard output using write(1, . . . ) or using two library calls q q n File descriptor 0 is standard input. File descriptor 1 is standard output. File descriptor 2 is standard error. printf Scanf So int fd = open(“/tmp/foo. txt”, O_RDONLY); should return 3 Use ls /proc/self/fd to see the devices/files they point to 5
Low-level File Access n n Open To create a new file descriptor we need to used the open system call #include <fcntl. h> #include <sys/types. h> #include <sys/stat. h> int open(const char *path, int oflags); int open(const char *path, int oflags, mode_t mode); 6
n ofalgs Mode Description O-RDONLY Open for read-only O_WRONLY Open for write-only O_RDWR Open for reading and writing The call may also include a combination of the following optional modes in the oflags parameter q q q O_APPEND Place written data at the end of the file O_TRUNC Set the length of the file to zero, discarding exiting contents O_CREAT Creates the file, if necessary, with permissions given in mode 7
#include <fcntl. h> #include <stdio. h> #include <sys/stat. h> #include <sys/types. h> #include <unistd. h> int main (int argc, char* argv[]) { const char* const filename = argv[1]; int fd = open (filename, O_RDONLY); printf (“in process %d, file descriptor %d is open to %sn”, (int) getpid (), (int) fd, filename); while (1); return 0; } 8
Low-level File Access n Read #include <unistd. h> size_t read(int fildes, void *buf, size_t nbytes); n It returns the number of data bytes actually read, which my be less than the number requested n If a read call returns 0, it had nothing to read; it reached the end of the file n If a read call returns -1, an error occurs 9
Low-level File Access n write #include <unistd. h> size_t write(int fildes, const void *buf, size_t nbytes); n n n The write system call arranges for the first nbytes from buf to be written to the file associated with the file descriptor fildes. It returns the number of bytes actually written This may be less than nbytes if there has been an error in the file descriptor, of if the underlying device driver is sensitive to block size If the function return 0, it means no data was written, if -1, there has been an error in the write call and the error will be specified in the errno global variable 10
Low-level File Access n close #include <unistd. h> int close(int fildes); n n We use to terminate the association between a file descriptor, fildes, and its file The file descriptor becomes available for reuse It returns 0 if successful and -1 on error It is important to check the return result from close since some file systems, particularly networked ones, may not report an error writing to a file until the file is closed 11
#include <stdio. h> #include <stdlib. h> #include <errno. h> #include <unistd. h> int main() { int pfds[2]; char buf[30]; if (pipe(pfds) == -1) { perror("pipe"); exit(1); } printf("writing to file descriptor #%dn", pfds[1]); write(pfds[1], "test", 5); printf("reading from file descriptor #%dn", pfds[0]); read(pfds[0], buf, 5); printf("read "%s"n", buf); } 12
#include <stdio. h> #include <stdlib. h> #include <sys/types. h> #include <unistd. h> int main() { int pfds[2]; char buf[30]; pipe(pfds); if (!fork()) { printf(" CHILD: writing to the pipen"); write(pfds[1], "test", 5); printf(" CHILD: exitingn"); exit(0); } else { printf("PARENT: reading from pipen"); read(pfds[0], buf, 5); printf("PARENT: read "%s"n", buf); wait(NULL); } } 13
// The 'consumer' program, pipe 4. c, that reads the data is much simpler. // The program works with next slide’s codes #include <unistd. h> #include <stdlib. h> #include <stdio. h> #include <string. h> int main(int argc, char *argv[]) { int data_processed; char buffer[BUFSIZ + 1]; int file_descriptor; memset(buffer, '