Signal Example 1 #include <stdio.h> #include <signal.h> void ctrl_c_handler(int tmp) { printf("you typed CTL-C, but I don't want to die!\n"); int main(int argc, char* argv[]) { long i; signal(sigint, ctrl_c_handler); for(i = 0; i < 5000000000; i++); [fgsong@pegasus signal]$./signal_1 *The function pointer can be a constant: SIGIGN: process ignores the signal Note: SIGKILL cannot be ignored! SIGDFL: process switches to the default signal handler. typedef void (*sighandler_t)(int); 19 Signal Example 2 #include <stdio.h> #include <stdlib.h> #include <signal.h> long i; void ctrl_c_handler(int tmp) { printf("you typed CTL-C, i = %ld\n", i); void ctrl_slash_handler(int tmp) { printf("you typed CTL-\\, i = %ld\n", i); [fgsong@pegasus signal]$./signal_2 ^CYou typed CTL-C, i = 879682164 ^\You typed CTL-\, i = 2290681483 ^CYou typed CTL-C, i = 2926963168 ^\You typed CTL-\, i = 3529939362 ^CYou typed CTL-C, i = 4408585330 ^\You typed CTL-\, i = 4807404731 int main(int argc, char* argv[]) { signal(sigint, ctrl_c_handler); signal(sigquit, ctrl_slash_handler); for(i = 0; i < 10000000000; i++); exit(0); 20 1
alarm unsigned int alarm (unsigned int seconds); It arranges a SIGALRM signal to be delivered to the calling process in seconds seconds. If = 0 second? No new alarm is scheduled. Suppose you can provide an alarm signal handler, you can catch the signal, and do whatever you want to do. Q: If you don t provide an alarm signal handler, what would happen? 21 Example of using alarm() #include <stdio.h> #include <signal.h> #include <unistd.h> int seconds; void alarm_handler(int dummy) { ++seconds; printf("%d seconds has passed\n", seconds); alarm(1); int main(int argc, char* argv[]) { int i; signal(sigalrm, alarm_handler); alarm(1); for(i = 0; i < 9000000000; i++); return 0; [fgsong@pegasus signal]$./alarm 1 seconds has passed 2 seconds has passed 3 seconds has passed 4 seconds has passed 5 seconds has passed 6 seconds has passed 7 seconds has passed 8 seconds has passed 9 seconds has passed Q1: what would happen if removing alarm()? Q2: add while(1) after alarm(1), what would happen? Why? Q3: add your own signal handler to SIGINT, can CTRL-C be handled? 22 2
Discussions on Signal (a lot of info) On some systems, when you are inside a signal handler, you cannot process that same signal again until the handler returns. On other systems, maybe you can. On Linux, we cannot process the same signal again. e.g., Alarm handler, CTRL-C handler. But, you can handle signals B, C, and D if you are still in A s signal handler. Signals are generated from difference places: From keyboard stroke, e.g., CTRL-C From hardware conditions, e.g., div by 0 From processes. e.g., the kill function (one process sends a signal to another process if privileged) int kill(pid_t pid, int sig) From software conditions like SIGPIPE and SIGALARM Execvp() will reset signal handlers to the default behavior When a signal arrives: the running process is interrupted, the current registers are saved, the signal handler is invoked. When a child dies, it sends a SIGCHLD signal to its parent. Parent can use wait() to check its status. signal (SIGCHLD, SIG_IGN); // ignore the signal (already by default) 23 Shared Memory (shm) Shared Memory is the fastest IPC method We talked about the message passing model Pro: After a shared memory region is set up, there is no kernel overhead. no system call overhead! no context switch (user<->kernel<->user)! Processes will communicate by reading or writing data from/to the shared memory region. Users must make sure they do not write to the same address at the same time. Nevertheless, 1st thing to do is to establish the shared memory. 24 3
How Shared Memory Works? Each process has its own address space. It is a virtual space, separated from other processes The virtual space will be mapped to the physical memory by the OS. Process 1 address space: 0 x 1M Bytes Block Process 2 AS: 0 n-1 0 2 20-1 Physical Memory y n-1 1M Bytes Block 25 System V Shared Memory int shmget(key_t key, size_t size, int shmflg); //see next slide Get or create a new shared memory, with size equal to size round up to a multiple of PAGE_SIZE. Return value: ID of the shared memory associated with key. void* shmat(int shmid, const void* shmaddr, int shmflg); Attach the shared memory segment (i.e., shmid) to the address space of the calling process. If shmaddr == NULL, the OS will choose an unused address to attach the memory segment (preferred, portable) You may specify a suitable address yourself (not portable!). Return value: Address of the attached memory on success; OR -1 on errors. void* shmdt(const void* shmaddr); //detach shm from the calling process int shmctl(int shmid, int cmd, struct shmid_ds *buf) //mark to delete! To delete a shared memory segment (cmd = IPC_RMID). Will wait until the last process detaches from the shm. Warning: the shared memory segment may be attached at distinct addresses in different processes. 26 4
int shmget(key_t key, size_t size, int shmflg); shmget() returns the ID of the shared memory segment associated with the value of the argument key. A new shared memory segment, with size equal to the value of size rounded up to a multiple of PAGE_SIZE, is created Using the flag of IPC_CREAT: To create a new segment. If this flag is not used, then shmget() will find the segment associated with key and check to see if the user has permission to access the segment. 27 Shared Memory Example Look at the code: shm.c Parent writes a message to a common area Child reads the message from the common area 28 5
POSIX Shared Memory Typical operations: Process A first creates a shm object shm_fd = shm_open(name, O_CREAT O_RDWR, 0666); Also can open an existing segment //just like open a file Set the size of the shm object ftruncate(shm_fd, 4096); Memory map the shm object into the address space of the current process ptr = mmap(null, 4096, PROT_WRITE, MAP_SHARED, shm_fd, 0); Now the process could write to the shared memory sprintf(ptr, "Writing to shared memory"); Note: Take a look at the example in the textbook (Chapter 3). 6
POSIX Memory Mapping #include <sys/mman.h> void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t off); Memory map a file Establish a mapping between the address space of the process to the memory object represented by the file descriptor off len File fd Memory addr 3 1 Unix Domain Sockets A socket is a two-way communication pipe. It has two end points. We use Unix Domain Sockets to do IPC for processes on the same system. You should have used Internet sockets Socket = IP address + port# Internally it is implemented as a special file in the file system. How to use it? Create a server and a client Server: Listens to incoming connections from all clients 32 7
How to Become a Server 1) Call socket(): int s = socket(af_unix, SOCK_STREAM, 0); //create a new endpoint intended for communication 2) Call bind(): struct sockaddr_un local_addr; local_addr.sun_family = AF_UNIX; strcpy(local_addr.sun_path, /home/fgsong/tmp_socket ); unlink(local_addr.sun_path); bind(s, (struct sockaddr*) &local_addr, sizeof(local_addr); //bind a name to the socket 3) Call listen(): listen(s, 8); //server socket. 4) Call accept() struct sockaddr_unremote_addr; int length = sizeof(remote_addr); Extracts the first connection request on the queue of pending connections for the listening socket, s, creates a new connected socket, and returns a new file descriptor referring to that socket. The newly created socket is not in the listening state. The original socket s is unaffected by this call. int s2 = accept(s, &remote_addr, &length); Note: s2 is a new socket and is ready to send and receive. 5) A while loop to call recv() and send() via new socket s2 while (!done) { recv(s2, &buf, len, 0); send(s2, &buf, len, 0); 33 1) Call socket() How to Become a Client int s = socket(af_unix, SOCK_STREAM, 0); 2) Call connect() sockaddr_un server_addr; server_addr.sun_family = AF_UNIX; strcpy(server_addr.sun_path, /home/fgsong/tmp_socket ); connect(s, (struct sockaddr*) &server_addr, sizeof(server_addr)); 3) A loop to call send() and receive() while (!done) { recv(s, &buf, len, 0); send(s, &buf, len, 0); 34 8
Revisit: Pipes (1/2) Pipe acts as a channel that allows two processes to communicate You have been using it in Lab 2 Questions: Is communication unidirectional or bidirectional? Is it half- or full-duplex? Does it require a relationship between the communicating processes? Can the pipes be used over a network? Revisit: Pipes (2/2) Allow communication in standard producer-consumer style. Producer writes to one end (i.e., the write-end of the pipe) Consumer reads from the other end (i.e., the read-end of the pipe) Require parent-child relationship between communicating processes It is also called unnamed pipes. Reading end Writing end 9
Also known as FIFO. Named Pipes More powerful than ordinary pipes Communication can be bidirectional No parent-child relationship is necessary between the communicating processes Multiple processes can use the named pipe for communication Provided on both UNIX and Windows systems e.g., $mkfifo named_pipe $ls -l >> named_pipe $cat < named_pipe //run the command in another terminal Remote Procedure Call (RPC) RPC provides a way to offer procedure-call mechanism between processes on a network A client invokes a procedure on a server, just like it invokes a local procedure. Supported by stubs The Client Stub locates the server, marshalls the parameters, send them to server, wait, unmarshall the replies. (auto generated code) The Server Stub receives this message, unmarshalls parameters, and performs the procedure on the server. (auto generated code) Remote communication may fail Local procedure calls never fails. RPC can be retried multiple times! OS typically provides a matchmaker service to connect client and server 38 10
39 Summary Two IPC Models Five IPC mechanisms: Signal, Shm, Unix Domain Socket, Pipe, and RPC Run the code yourself Lab 3 will need Unix Socket and Shm Next lecture: Start to investigate Threads 41 11