1 / 60

CS 620 Advanced Operating Systems

CS 620 Advanced Operating Systems. Lecture 3 – UNIX IPC Programming Review Professor Timothy Arndt BU 331. UNIX Programming Review. UNIX manages multiple concurrent processes. Each process is represented by an entry in the process table .

clarke
Download Presentation

CS 620 Advanced Operating Systems

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. CS 620 Advanced Operating Systems Lecture 3 – UNIX IPC Programming Review Professor Timothy Arndt BU 331

  2. UNIX Programming Review • UNIX manages multiple concurrent processes. • Each process is represented by an entry in the process table. • A process goes through a number of states before running to completion • Running • Asleep • Ready • Zombie

  3. Process Creation • The fork system call is used inside a C program to create another process. • After the fork call, both a parent and child process are running. • The child process has a unique PID, and PPID, but otherwise is equivalent to the parent (i.e. it is a copy of the same program). • The PID is used to distinguish the child from the parent process.

  4. Process Creation #include <stdio.h> main() { int child_id; child_id = fork(); /* process creation */ if ( child_id == 0 ) /* child code begin */ { printf("Child says my pid = %d and my parent pid = %d\n", getpid(), getppid()); _exit(0); /* child terminates (i) */ } /* child code end */ /* remaining parent code */ if ( child_id < 0 ) { fprintf(stderr, "fork failed\n"); exit(1); } printf("Parent says child pid = %d\n", child_id); }

  5. Program Execution: exec • The child process can also run a different program than the parent by overlaying a new executable file on itself. • This is done using one of the exec routines: execl, execv, execve, etc. • The execed file can either be an executable binary file or an executable text file (e.g. a shell script). • The various exec routines vary in the type and number of arguments they take.

  6. Exec Example #include <stdio.h> #include <string.h> #define MAXLINE 80 int main() { char cmd[MAXLINE]; void background(char *cmd); for (;;) { printf("mysh ready%%"); /* prompt */ gets(cmd); /* read command */ if ( strcmp(cmd,"exit") == 0 ) return(0); background(cmd); /* start background job */ } }

  7. Exec Example #define WHITE "\t \n" #define MAXARG 20 void background(char *cmd) { char *argv[MAXARG]; int id, i = 0; /* to fill in argv */ argv[i++] = strtok(cmd, WHITE); while ( i < MAXARG && (argv[i++] = strtok(NULL, WHITE)) != NULL ); if ( (id = fork()) == 0) /* child executes background job */ { execv(argv[0], argv); _exit(1); /* execv failed */ } else if ( id < 0 ) { fprintf(stderr, "fork failed\n"); perror("background:"); } }

  8. Synchronization of Parent and Child • After creating a child process using fork, the parent may run independently, or it may wait for the child process to terminate before proceeding further. • The wait call searches for a terminated child (in the zombie state) of the calling process. • If there are no child processes, wait returns with a value of -1 • If one or more child processes are in the zombie state, wait selects an arbitrary child, frees its process table slot, stores its exit status and returns it PID • Otherwise, wait blocks until one of the child processes terminates.

  9. Synchronization of Parent and Child #include <stdio.h> #include <sys/wait.h> int main() { int pid1, pid2, pid; union wait status; if ((pid1 = fork()) == 0) /* child one */ { printf("child pid=%d\n", getpid()); _exit(0); } printf("forking again\n"); if ((pid2 = fork()) == 0) /* child two */ { printf("child pid=%d\n", getpid()); _exit(1); }

  10. Synchronization of Parent and Child printf("first wait\n"); pid = wait(&status); printf("pid=%d, status=%d\n", pid, status); printf("2nd wait\n"); pid = wait(&status); printf("pid=%d, status=%d\n", pid, status); return(0); }

  11. Interrupts and Signals • Signals are used in UNIX to tell a running process that some event has occurred. • After receiving a signal, a process reacts to it in a well-defined manner. • There are a number of different signals that can be sent: SIGHUP 1; SIGINT 2; SIGQUIT 3; SIGILL 4; SIGTRAP 5; SIGFPE 8; SIGKILL 9; SIGBUS 10; SIGSEGV 11; SIGSYS 12; SIGVTALRM 26; SIGPROF 27.

  12. Interrupts and Signals • From the shell, a signal can be sent using the kill command with the signal to be sent along with the PID. • From a C program, the function raise sends a signal to the same process, while the function kill sends a signal to some other process. • If a signal is not currently blocked by a process, further occurrences of the signal are blocked during signal handling.

  13. Interrupts and Signals • The function is suspended and the handler function for the signal is called, and finally if the handler function returns, the signal is unblocked and normal execution of the process resumes from the point of interrupt. • After receiving a signal, a process normally either exits or stops. This default behavior can be changed using the signal function, specifying a handler function for a specific signal. When that signal is received, the handler function will be invoked.

  14. Signal Trapping /* this program demonstrates the use of signal to trap * interrupts from the terminal. To terminate the program * type ^\ */ #include <signal.h> main() { void cnt(int sig); signal(SIGINT, cnt); printf("Begin counting and INTERRUPTs\n"); for(;;); /* infinite loop */ } void cnt(int sig) { static int count=0; printf("Total of %d INTERRUPTS received\n", ++count); signal(SIGINT, cnt); }

  15. IPC and Network Communication • Two processes residing either on the same computer or on different computers may need to exchange data. This is known as Inter Process Communication (IPC). • If the two processes are related by a fork, IPC can be done through the pipe mechanism (the processes are on the same machine). • If the two processes are not related, IPC can be performed using the socket mechanism.

  16. Pipes • A pipe is a direct (in memory) I/O channel between processes. • It is used with the calls fork, exec, wait, and exit to make multiple processes cooperate. • To establish a pipe use the system call int pipe(int fildes[2]) • This establishes a buffer and two descriptors fildes[0] for reading the pipe and fildes[1] for writing the pipe. • The pipe is created before the fork, so both parent and child have copies of the pipe.

  17. Pipes • I/O is performed through a pipe using the read and write calls. • Read removes characters from the buffer and write deposits them. • The buffer size is usually 4096 characters. • If we write a full pipe buffer, the process blocks until more space is available in the pipe. • If we read an empty buffer, the reading process blocks unless the write end of the pipe has been closed - in this case 0 (end of file) is returned.

  18. Pipes #include <stdio.h> #include <string.h> #include <sys/wait.h> int main(int argc, char *argv[]) { int p[2]; int i, pid, status; char buffer[20]; pipe(p); /* setting up the pipe */ if ((pid = fork()) == 0) /* in child */ { close(p[1]); /* child closes p[1] */ while ((i = read(p[0], buffer, 6)) != 0) { buffer[i] = '\0'; /* string terminator */ printf("%d chars :%s: received by child\n", i, buffer); } _exit(0); /* child terminates */ }

  19. Pipes /* in parent */ close(p[0]); /* parent writes p[1] */ write(p[1], "Hello there,", sizeof("Hello there,")); write(p[1], " from me.", sizeof(" from me.")); close(p[1]); /* finished writing p[1] */ while (wait(&status)!=pid); /* waiting for pid */ if (status == 0) printf("child finished\n"); else printf("child failed\n"); return(0); }

  20. The dup2 Command • The following example shows the use of the dup2 command which duplicates an existing I/O descriptor.

  21. Pipes #include <stdio.h> #include <string.h> int main(int argc, char *argv[]) { int p[2]; int i,pid1,pid2, status; argv++; /* lose argv[0] */ for (i = 1; i <= argc ; i++) if (strcmp(argv[i],"%") == 0) { argv[i] = '\0'; /* break into two commands */ break; } pipe(p); /* setting up the pipe */ if ((pid1 = fork ()) == 0) /* child one */ { close(p[1]); dup2(p[0],0); /* 0 becomes a duplicate of p[0] */ close(p[0]); execv(argv[i+1], &argv[i+1]); /* this reads the pipe */

  22. Pipes _exit(1); /* bad error execl failed */ } if ((pid2 = fork ()) == 0) /* child two */ { close(p[0]); dup2(p[1],1); /* 1 becomes a duplicate of p[1] */ close(p[1]); execv(argv[0],argv); /* this writes the pipe */ _exit(1); /* bad error execv failed */ } /* parent does not use pipe */ close(p[0]); close(p[1]); while (wait(&status)!=pid2); /* waiting for pid2 */ if (status == 0) printf("child two terminated\n"); else printf("child two failed\n"); exit(0); }

  23. Two-Way Pipe Connections • In order to have two-way pipe connections between two processes, two pipes must be used. This is shown in the following example.

  24. Two-Way Pipe Connections #include <stdio.h> #include <string.h> int readl(int fd, char s[], int size) { char *tmp = s; while (0 < --size && read(fd, tmp, 1) != 0 && *tmp++ != '\n'); *tmp = '\0'; /* string terminator */ return(tmp - s); } int pipe_2way(char *cmd[], int piped[]) { int pid, wt[2], rd[2]; pipe(rd); /* incoming pipe: read by parent */ pipe(wt); /* outgoing pipe: write to child */ if ((pid=vfork()) == 0)

  25. Two-Way Pipe Connections { close(wt[1]); /* in child */ dup2(wt[0],0); /* 0 identified with wt[0] */ close(wt[0]); close(rd[0]); dup2(rd[1], 1); /* 1 identified with rd[1] */ close(rd[1]); execv(cmd[0],cmd); /* execute given command */ perror("execv failed"); /* normally not reached */ _exit(1); } close(wt[0]); /* in parent */ piped[1] = wt[1]; close(rd[1]); piped[0] = rd[0]; return(0); }

  26. Two-Way Pipe Connections #define SIZE 256 int main() { int pd[2]; char *str[2]; char test_string[] = "IPC WITH TWO-WAY PIPE.\n"; char buf[SIZE]; char *tmp = buf; str[0] = "./lowercase"; str[1] = NULL; pipe_2way(str, pd); /* write to lowercase process */ write(pd[1], test_string, strlen(test_string)); readl(pd[0], buf, SIZE); /* read from lowercase process */ printf("Received from lowercase process:\n%s", buf); return(0); }

  27. Sockets • Sockets are abstractions that serve as endpoints of communication within a networking domain. • The socket is the ipc mechanism’s interface to application programs. Each socket can exchange data with any other socket within the same domain (e.g. the Internet domain). • Each socket is assigned a type property. Different type sockets use different protocols. • Stream sockets support bidirectional, reliable, sequenced flow of data • Stream sockets in the Internet domain use TCP/IP.

  28. Sockets • Datagram sockets provide bidirectional flow of data packets called messages • The communication channel is not sequenced, reliable, or unduplicated • A datagram socket does not have to be connected to a peer • A message is sent to a datagram socket by specifying its address • Datagram sockets in the Internet domain use UDP/IP. • Raw sockets give access to the underlying communications protocol • They are not intended for the general user • In the Internet domain, they give direct access to the Internet Protocol (IP).

  29. Sockets • The socket system call creates a socket of a particular type in a particular domain • The type and domain are given by constants defined in sys/socket.h • Sockets must then be given an address so that other processes can refer to them • In Internet domain, socket address consists of combination of host IP address and port number • Standard network services are assigned the same port number on each host • A database file contains a list of services and port number.

  30. Sockets grail:/users/faculty/arndt> cd /etc grail:/etc> more services daytime 13/tcp # Daytime daytime 13/udp # qotd 17/tcp quote # Quote of the Day chargen 19/tcp ttytst source # Character Generator chargen 19/udp ttytst source # ftp-data 20/tcp # File Transfer Protocol (Data) ftp 21/tcp # File Transfer Protocol (Control) telnet 23/tcp # Virtual Terminal Protocol smtp 25/tcp # Simple Mail Transfer Protocol time 37/tcp timeserver # Time time 37/udp timeserver # rlp 39/udp resource # Resource Location Protocol whois 43/tcp nicname # Who Is domain 53/tcp nameserver # Domain Name Service

  31. Sockets domain 53/udp nameserver # bootps 67/udp # Bootstrap Protocol Server bootpc 68/udp # Bootstrap Protocol Client tftp 69/udp # Trivial File Transfer Protocol finger 79/tcp # Finge rwww 80/tcp http # WorldWideWeb HTTP www 80/udp # HyperText Transfer Protocol supdup 95/tcp # hostnames 101/tcp hostname # NIC Host Name Server poppassd 106/tcp pop 109/tcp postoffice # Post Office Protocol - Version 2 pop-3 110/tcp # Post Office version 3 portmap 111/tcp sunrpc # SUN Remote Procedure Call uucp-path 117/tcp # UUCP Path Service nntp 119/tcp readnews untp # Network News Transfer Protocol ntp 123/udp # Network Time Protocol

  32. Sockets • To bind a name to socket, use the system call bind(int soc, struct sockaddr *addr, int addrlen) • The following example illustrates the use of datagram sockets with a sender process and a receiver process.

  33. Datagram Sockets #include <stdio.h> #include <sys/types.h> #include <sys/file.h> #include <sys/socket.h> #include <sys/un.h> /* UNIX domain header */ int main() { int soc; char buf[] = "Hello there"; struct sockaddr_un peer={AF_UNIX, "receiver_soc"}; /* (1) */ int n; soc = socket(AF_UNIX, SOCK_DGRAM, 0); /* (2) */ if ( access(peer.sun_path, F_OK) > -1 ) /* (3) */ { n = sendto(soc, buf, strlen(buf), /* (4) */ 0, &peer, sizeof(peer)); if ( n < 0 ) { fprintf(stderr, "sendto failed\n"); exit(1); }

  34. Datagram Sockets printf("Sender: %d characters sent!\n", n); /* (5) */ close(soc); /* (6) */ } return(0); } /***** File : receiver.c *****/ /***** datagram socket example: receiver process *****/ #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> /* UNIX domain header */ void cleanup(int soc, char *file) { close(soc); unlink(file); }

  35. Datagram Sockets int main() { int soc; char buf[64]; struct sockaddr_un self={AF_UNIX, "receiver_soc"}; /* (A) */ struct sockaddr_un peer; int n, len; soc = socket(AF_UNIX, SOCK_DGRAM, 0); /* (B) */ n = bind(soc, &self, sizeof(self)); /* (C) */ if ( n < 0 ) { fprintf(stderr, "bind failed\n"); exit(1); } n = recvfrom(soc, buf, sizeof(buf), /* (D) */ 0, &peer, &len); if ( n < 0 ) { fprintf(stderr, "recvfrom failed\n"); cleanup(soc, self.sun_path); exit(1); }

  36. Datagram Sockets buf[n] = '\0'; /* (E) */ printf("Datagram received = %s\n", buf); /* (F) */ cleanup(soc, self.sun_path); return(0); }

  37. Datagram Sockets • Note that we have two separate processes (we have two main functions). Both must be compiled. • In order to run the example, both processes must be launched: receiver & sender • The sendto and recvfrom system calls are normally used with datagram sockets.

  38. Stream Sockets • A stream socket is connected with its peer to form a two-way pipe between a client and a server. • The server process listens for connection requests and accepts connections. • The server process binds a known address to a socket. • The client process uses its socket to initiate a connection to a socket of a server process. • The client process finds the correct address of the server. • Then initiates a connection to the server process. • After connection, data communication takes place using the read and write I/O system calls.

  39. Stream Sockets • The connect system call - connect(int soc, struct sockaddr *name, int namelen) - associates the client socket given by the descriptor soc to a peer socket in a server specified by *name. • The connect call is often used by client programs to connect to stream sockets of known services on the Internet. • A pair of sockets forms a virtual circuit between the client and server process.

  40. Stream Sockets • A server process with a stream socket needs to take the following steps to get ready to accept a connection: • Create a socket in the appropriate domains of type SOCK_STREAM • Construct the correct address and bind it to the socket • Indicate a willingness to accept connection requests by executing the listen system call. • Use the accept call to wait for a connection request and establish a connection.

  41. Stream Sockets • int listen(int soc, int n) initializes the socket for incoming requests and sets the maximum number of pending connections. • int accept(int soc, struct sockaddr *addr, int *addrlen) accepts connections on the socket on which a listen has been executed • If there are pending connections, the first is chosen and a new socket is created with the same properties as soc • This socket is connected and a descriptor of the socket is returned.

  42. Stream Sockets • The listening socket remains ready to receive connection requests. • If no connections are pending and the socket is not marked as nonblocking (using system call fcntl), accept blocks until a connection request arrives. • If the connection is marked as nonblocking and no requests are pending, accept returns an error. • The accepted socket communicates with its peer and may not accept additional connections.

  43. Stream Sockets • For connected sockets, the basic read and write system calls send and receive data: • write(soc, buffer, sizeof(buffer)); • read(soc, buffer, sizeof(buffer)); • Each process reads and writes its own socket, resulting in a bidirectional data flow between the connected peers. The socket I/O calls • send(soc, buffer, sizeof(buffer), opt); • recv(soc, buffer, sizeof(buffer), opt); • Can be used by stream sockets to send out-of-band data by setting opt to MSG_OOB.

  44. Stream Socket Example /***** stream socket example: server.c *****/ #include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> /* UNIX domain header */ int main() { int soc, ns, k; char buf[256]; struct sockaddr_un self = {AF_UNIX, "serversoc"}; struct sockaddr_un peer = {AF_UNIX}; int peer_len = sizeof(peer); /* set up listening socket soc */ soc = socket(AF_UNIX, SOCK_STREAM, 0); /* (1) */ if (soc < 0) { perror("server:socket"); exit(1); }

  45. Stream Socket Example if (bind(soc, &self, sizeof(self)) == -1)/* (2) */ { perror("server:bind"); close(soc); exit(1); } listen(soc, 1); /* (3) */ /* accept connection request */ ns = accept(soc, &peer, &peer_len); /* (4) */ if (ns < 0) { perror("server:accept"); close(soc); unlink(self.sun_path); exit(1); } /* data transfer on connected socket ns */ k = read(ns, buf, sizeof(buf)); /* (5) */ printf("SERVER RECEIVED: %s\n", buf); write(ns, buf, k); /* (6) */ close(ns); close(soc); unlink(self.sun_path); return(0); }

  46. Stream Socket Example /***** File : client.c *****/ /***** socket example: receiver process *****/ #include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> /* UNIX domain header */ int main() { int soc; char buf[256]; struct sockaddr_un self={AF_UNIX, ”clientsoc"}; struct sockaddr_un peer={AF_UNIX, “serversoc”}; soc = socket(AF_UNIX, SOCK_STREAM, 0); bind(soc, &self, sizeof(self)); /* request connection to serversoc */ if (connect(soc, &peer, sizeof(peer)) == -1) { perror(“client:connect”); close(soc);

  47. Stream Socket Example unlink(self.sun_path); exit(1); } write(soc, “hello from client”, 18); read(soc, buf, sizeof(buf)); printf(“SERVER ECHOED: %s\n”, buf); close(soc); unlink(self.sun_path); return(0); } /*** end of client.c ***/

  48. Stream Socket Example • Note that it is not strictly necessary to bind an explicit address for a client process • The close system call closes both halves of a socket • To close the read and write halves independently, the shutdown system call can be used

  49. Network Databases and Address Mapping • The previous example programs involved sockets with UNIX domain addresses - a simple name • For Internet domain sockets, the socket address involves both the numeric IP address of a host as well as a port number for a particular server • The DNS, a set of database files, and a collection of library functions combine to help construct required Internet socket addresses in application programs

  50. Network Databases and Address Mapping • Some of the important files for name resolution are: • /etc/resolv.conf - the configuration file for the DNS resolver. Lists name servers for the local domain. • /etc/named.boot - the DNS name server boot file. Needed only on a host that runs a name server. Gives the locations of root name servers, e-mail exchange information, etc. • /etc/services - contains information regarding well known Internet services. • /etc/protocols - contains information regarding known Internet protocols.

More Related