You are on page 1of 41

Operating Systems: IPC

Inter-Process Communication : Message Passing


• Processes can communicate through shared areas of memory
– the Mutual Exclusion problem and Critical Sections
• Semaphores - a synchronisation abstraction
• Monitors - a higher level abstraction
• Inter-Process Message Passing much more useful for information transfer
– can also be used just for synchronisation
– can co-exist with shared memory communication
• Two basic operations : send(message) and receive(message)
– message contents can be anything mutually comprehensible
» data, remote procedure calls, executable code etc.
– usually contains standard fields
» destination process ID, sending process ID for any reply
» message length
» data type, data etc.
1
Operating Systems: IPC

• Fixed-length messages:
– simple to implement - can have pool of standard-sized buffers
» low overheads and efficient for small lengths
- copying overheads if fixed length too long
– can be inconvenient for user processes with variable amount of data to pass
» may need a sequence of messages to pass all the data
» long messages may be better passed another way e.g. FTP
- copying probably involved, sometimes multiple copying into kernel and out

• Variable-length messages:
– more difficult to implement - may need a heap with garbage collection
» more overheads and less efficient, memory fragmentation
– more convenient for user processes

2
Operating Systems: IPC

• Communication links between processes


– not concerned with physical implementation e.g. shared memory, processor
bus, network etc.
– rather with issues of its logical implementation
• Issues:
– how are links established?
– can a link be associated with more than two processes?
– how many links can there be between every pair of processes?
– what is the capacity of a link?
» i.e. buffer space and how much
– fixed v. variable length messages
– unidirectional v. bidirectional ?
» can messages flow in one or both directions between two linked processes
» unidirectional if each linked process can either send orreceive but not both
and each link has at least one receiver process connected to it

3
Operating Systems: IPC

• Naming of links - direct and indirect communications


• Direct:
– each process wanting to communicate must explicitly name the recipient or
sender of the communication
– send and receive primitives defined:
send ( P, message ) : send a message to process P
receive ( Q, message ) : receive a message from process Q
– a link established automatically between every pair of processes that want
to communicate
» processes only need to know each other’s identity
– link is associated with exactly two processes
– link is usually bidirectional but can be unidirectional
– Process A Process B
while (TRUE) { while (TRUE) {
produce an item receive ( A, item )
send ( B, item ) consume item
} }
4
Operating Systems: IPC

• Asymmetric addressing:
– only the sender names the recipient
– recipient not required to name the sender - need not know the sender
send ( P, message ) : send message to process P
receive ( id, message ) : receive from any process, id set to sender
• Disadvantage of direct communications :
– limited modularity - changing the name of a process means changing every
sender and receiver process to match
– need to know process names

• Indirect communications :
– messages sent to and received from mailboxes (or ports)
» mailboxes can be viewed as objects into which messages placed by
processes and from which messages can be removed by other processes
– each mailbox has a unique ID
– two processes can communicate only if they have a shared mailbox
5
Operating Systems: IPC

send ( A, message ) : send a message to mailbox A


receive ( A, message ) : receive a message from mailbox A
– a communications link is only established between a pair of processes if they
have a shared mailbox
– a pair of processes can communicate via several different mailboxes if desired
– a link can be either unidirectional or bidirectional
– a link may be associated with more than two processes
» allows one-to-many, many-to-one, many-to-many communications
– one-to-many : any of several processes may receive from the mailbox
» e.g. a broadcast of some sort
» which of the receivers gets the message?
- arbitrary choice of the scheduling system if many waiting?
- only allow one process at a time to wait on a receive
– many-to-one : many processes sending to one receiving process
» e.g. a server providing service to a collection of processes
» file server, network server, mail server etc.
» receiver can identify the sender from the message header contents
6
Operating Systems: IPC

• many-to-many :
– e.g. multiple senders requesting service and a pool of receiving servers
offering service - a server farm

• Mailbox Ownership
– process mailbox ownership :
» only the process may receive messages from the mailbox
» other processes may send to the mailbox
» mailbox can be created with the process and destroyed when the process dies
- process sending to a dead process’s mailbox will need to be signalled
» or through separate create_mailbox and destroy_mailbox calls
- possibly declare variables of type ‘mailbox’
– system mailbox ownership :
» mailboxes have their own independent existence, not attached to any process
» dynamic connection to a mailbox by processes
- for send and/or receive

7
Operating Systems: IPC

• Buffering - the number of messages that can reside in a link temporarily


– Zero capacity - queue length 0
» sender must wait until receiver ready to take the message
– Bounded capacity - finite length queue
» messages can be queued as long as queue not full
» otherwise sender will have to wait
– Unbounded capacity
» any number of messages can be queued - in virtual space?
» sender never delayed
• Copying
– need to minimise message copying for efficiency
– copy from sending process into kernel message queue space and then into
receiving process?
» probably inevitable in a distributed system
» advantage that communicating processes are kept separate
- malfunctions localised to each process

8
Operating Systems: IPC

– direct copy from one process to the other?


» from virtual space to virtual space?
» message queues keep indirect pointers to message in process virtual space
» both processes need to be memory resident i.e. not swapped out to disc, at
time of message transfer
– shared virtual memory
» message mapped into virtual space of both sender and receiver processes
» one physical copy of message in memory ever
» no copying involved beyond normal paging mechanisms
» used in MACH operating system
Aside : Mach’s Copy-on-Write mechanism (also used in Linux forks) :
» single copy of shared material mapped into both processes virtual space
» both processes can read the same copy in physical memory
» if either process tries to write, an exception to the kernel occurs
» kernel makes a copy of the material and remaps virtual space of writing
process onto it
» writing process modifies new copy and leaves old copy intact for other process
9
Operating Systems: IPC

• Synchronised versus Asynchronous Communications


• Synchronised:
– send and receive operations blocking
» sender is suspended until receiving process does a corresponding read
» receiver suspended until a message is sent for it to receive
– properties :
» processes tightly synchronised - the rendezvous of Ada
» effective confirmation of receipt for sender
» at most one message can be outstanding for any process pair
- no buffer space problems
» easy to implement, with low overhead
– disadvantages :
» sending process might want to continue after its send operation without
waiting for confirmation of receipt
» receiving process might want to do something else if no message is waiting
to be received

10
Operating Systems: IPC

• Asynchronous :
– send and receive operations non-blocking
» sender continues when no corresponding receive outstanding
» receiver continues when no message has been sent
– properties :
» messages need to be buffered until they are received
- amount of buffer space to allocate can be problematic
- a process running amok could clog the system with messages if not careful
» often very convenient rather than be forced to wait
- particularly for senders
» can increase concurrency
» some awkward kernel decisions avoided
- e.g. whether to swap a waiting process out to disc or not
– receivers can poll for messages
» i.e. do a test-receive every so often to see if any messages waiting
» interrupt and signal programming more difficult
» preferable alternative perhaps to have a blocking receive in a separate thread
11
Operating Systems: IPC

• Other combinations :
– non-blocking send + blocking receive
» probably the most useful combination
» sending process can send off several successive messages to one or more
processes if need be without being held up
» receivers wait until something to do i.e. take some action on message receipt
- e.g. a server process
- might wait on a read until a service request arrived,
- then transfer the execution of the request to a separate thread
- then go back and wait on the read for the next request
– blocking send + non-blocking receive
» conceivable but probably not a useful combination
– in practice, sending and receiving processes will each choose independently
• Linux file access normally blocking :
– to set a device to non-blocking (already opened with a descriptor fd) :
fcntl ( fd, F_SETFL, fcntl ( fd, F_GETFL) | O_NDELAY )

12
Operating Systems: IPC

• Missing messages ?
– message sent but never received
» receiver crashed?
» receiver no longer trying to read messages?
– waiting receiver never receives a message
» sender crashed?
» no longer sending messages?

• Crashing processes :
– kernel knows when processes crash
– can notify waiting process
» by synthesised message
» by signal
» terminate process

13
Operating Systems: IPC

• Time-outs
– add a time-limit argument to receive
receive ( mailbox, message, time-limit )
– if no message received by time-limit after issuing the receive:
» kernel can generate an artificial synthesised message saying time-out
» could signal receiver process with a program-error
- allows receiver to escape from current program context
– sending process can also be protected using a handshake protocol :
– sender :
send (mailbox, message)
receive (ackmail, ackmess, time-limit)
– receiver :
receive (mailbox, message, time-limit)
if ( message received in time ) send (ackmail, ackmess)

14
Operating Systems: IPC

• Lost messages
– particularly relevant in distributed systems
– OS’s need to be resilient i.e. not crash either the kernel or user processes
– options:
» kernel responsible for detecting message loss and resending
- need to buffer message until receipt confirmed
- can assume unreliability and demand receipt confirmation within a time limit
» sending process responsible for detecting loss
- receipt confirmation within time-limit and optional retransmit
» kernel responsible for detecting loss but just notifies sender
- sender can retransmit if desired
– long transmission delays can cause problems
» something delayed a message beyond its time limit but still in transit
- if message retransmitted, multiple messages flying about
- need to identify messages e.g. serial numbers, and discard any duplicates
» scrambled message - checksums etc.
» see Communications module!
15
Operating Systems: IPC

• Message Queuing order


– first-come-first served
» the obvious, simple approach
» may not be adequate for some situations
- e.g. a message to a print server from device driver saying printer gone off-line
- could use signals instead
– a priority system
» some types of message go to head of the queue
- e.g. exception messages
– user selection
» allow receiving process to select which message to receive next
- e.g. from a particular process, to complete some comms protocol
» to select a message type
- e.g. a request for a particular kind of service (different mailbox better?)
» to postpone a message type
- an inhibit request (implemented in EMAS)
• Authentication
– of sending and receiving processes - signatures, encryption etc.
16
Operating Systems: IPC

Process Synchronisation using Messages


• A mailbox can correspond to a semaphore:
non-blocking send + blocking receive
– equivalent to :
signal (V) by sender + wait (P) by receiver
• Mutual Exclusion :
– initialise :
» create_mailbox (mutex)
send (mutex, null-message)
– for each process :
» while (TRUE) {
receive (mutex, null-message);
critical section
send (mutex, null-messge);
}
– mutual exclusion just depends on whether mailbox is empty or not
» message is just a token, possesion of which gives right to enter C.S.
17
Operating Systems: IPC

• Producer / Consumer problem using messages :


– Binary semaphores : one message token
– General (counting) semaphores : more than one message token
– message blocks used to buffer data items
– scheme uses two mailboxes
» mayproduce and mayconsume

– producer :
» get a message block from mayproduce
» put data item in block
» send message to mayconsume
– consumer :
» get a message from mayconsume
» consume data in block
» return empty message block to mayproduce mailbox

18
Operating Systems: IPC

– parent process creates message slots


» buffering capacity depends on number of slots created
» slot = empty message
capacity = buffering capacity
create_mailbox ( mayproduce );
create_mailbox ( mayconsume );
for (i=0; i<capacity; i++) send (mayproduce, slot);
start producer and consumer processes
– producer :
» while (TRUE) {
receive (mayproduce, slot);
slot = new data item
send (mayconsume, slot);
}
– consumer :
» while (TRUE) {
receive (mayconsume, slot);
consume data item in slot
send (mayproduce, slot);
}
19
Operating Systems: IPC

– properties :
» no shared global data accessed
» all variables local to each process
» works on a distributed network in principle
» producers and consumers can be heterogeneous
- written in different programming languages - just need library support
- run on different machine architectures - ditto

• Examples of Operating Systems with message passing :

• EMAS - Edinburgh Multi-Access System


– all interprocess comms done with messages
» even kernel processes
– system calls used messages
– signals to processes used messages

20
Operating Systems: IPC

• QNX Real-Time Op. Sys.


– tightly synchronised send and receive
» after send, sending process goes into send-blocked state
» when receiving process issues a receive :
» message block shared between the processes
- no extra copying or buffering needed
» sending process changed to reply-blocked state
» had the receiver issued a receive before sender issued a send, receiver
would have gone into receive-blocked state
» when receiver has processed the data, it issues a reply, which unblocks the
sender and allows it to continue
- which process runs first is left to the scheduler
– in error situations, blocked processes can be signalled
» process is unblocked and allowed to deal with the signal
» may need an extra monitoring process to keep track of which messages
actually got through

21
Operating Systems: IPC

• MACH Op. Sys.


– two mailboxes (or ports) created with each process
» kernel mailbox : for system calls
» notify mailbox : for signals
– message calls : msg_send, msg_receive and msg_rpc
» msg_rpc (remote procedure call) is a combination of send and receive -
sends a message and then waits for exactly one return message
– port_allocate : creates a new mailbox
» default buffering of eight messages
– mailbox creator is its owner and is given receive permission to it
» can only have one owner at a time but ownership can be transferred
– messages copied into queue as they are sent
» all with same priority, first-come-first-served
– messages have : fixed length header, variable length data portion
» header contains sender and receiver Ids
» variable part a list of typed data items
- ownership & receive rights, task states, memory segments etc.
22
Operating Systems: IPC

– when message queue full, sender can opt to :


» wait indefinitely until there is space
» wait at most n milleseconds
» do not wait at all, but return immediately
» temporarily cache the message
- kept in the kernel, pending
- one space for one such message
- meant for server tasks e.g. to send a reply to a requester, even though the
requestor’s queue may be full of other messages
– messages can be received from either one mailbox or from one of a set of
mailboxes
– port_status call returns number of messages in a queue
– message transfer done via shared virtual memory if possible to avoid
copying
» only works for one system, not for a distributed system

23
Operating Systems: IPC

• Linux Op. Sys. IPC


– shared files, pipes, sockets etc.
• Shared files :
– one process writes / appends to a file
– other process reads from it
– properties:
» any pair of process with access rights to the file can communicate
» large amounts of data can be transferred
» synchronisation necessary
• Pipes :
– kernel buffers for info transfer, typically 4096 bytes long, used cyclically
– e.g. used by command shells to pipe output from one command to input of
another : $ ls | wc -w
» command shell forks a process to execute each command
» normally waits until sub-process terminates before continuing
- but continues executing if ‘&’ appended to command line

24
Operating Systems: IPC

#include <unistd.h>
#include <stdio.h>

main() {
int fda[2]; // file descriptors [0] : read end, [1] write end
char buf[1]; // data buffer

if ( pipe(fda) < 0 ) error (“create pipe failed\n”);

switch ( fork() ) { // fork an identical sub-process


case -1 : error (“fork failed\n”);
case 0: // child process is pipe reader
close ( fda[1] ); // close write of pipe
read ( fda[0], buf, 1 ); // read a character
printf (“%c\n”, buf[0] );
break;
default: // parent process is pipe writer
close ( fda[0] ); // close read end of pipe
write (fda[1], “a”, 1); // write a character
break;
}
}
– fork returns 0 to child process, sub-process ID to parent
– by default : write blocks when buffer full, read blocks when buffer empty
25
Operating Systems: IPC

• Aside : I/O Redirection


– used by a command shell
– dup() system call duplicates a file descriptor using first available table entry
– example : dup() file descriptor number 2 :

file descriptors open file descriptors file descriptors open file descriptors

0 0
1 1
2 2
3 3

– example on next slide : $ ls | wc -w


» ls executed by child process, wc executed by parent process
» file descriptors manipulated with close() and dup() so that pipe descriptors
become standard output to ls and standard input to wc
» execlp replaces current process with the specified command

26
Operating Systems: IPC

#include <unistd.h>
#include <stdio.h>

main() {
int fda[2]; // file descriptors

if ( pipe(fda) < 0 ) error (“create pipe failed\n”);

switch ( fork() ) { // fork an identical sub-process


case -1 : error (“fork failed\n”);
case 0: // run ls in child process
close (1); // close standard output
dup ( fda[1] ); // duplicate pipe write end
close ( fda[1] ); // close pipe write end
close ( fda[0] ); // close pipe read end
execlp (“ls”, “ls”, 0); // execute ls command
error (“failed to exec ls\n”); // should not get here
break;
default:// run wc in parent
close (0); // close standard input
dup (fda[0] ); // duplicate pipe read end
close ( fda[0] ); // close read end of pipe
close (fda[1] ); // close write end of pipe
execlp (“wc”, “wc”, “-w”, 0); // execute wc command
error (“failed to execute4 wc\n”);
break;
}
}

27
Operating Systems: IPC

– redirection to file similarly


» closing standard input and output descriptors followed by opening the
redirection files to re-use the standard I/O descriptor numbers
– example : $ cat <file1 >file2

#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#define WRFLAGS (O_WRONLY | O_CREAT | O_TRUNC)
#define MODE600 (S_IRUSR | S_IWUSR)

main() {
close (0); // close standard input
if (open(“file1”, O_RDONLY) == -1) error(“open input file failed\n”);

close (1); // close standard output


if (open(“file2”, WRFLAGS, MODE600) == -1) error(“open output failed\n”);

execlp (“cat”, “cat”, 0); // execute cat command

error(“failed to execute ‘cat’\n”);


}
28
Operating Systems: IPC

• FIFOs - Named Pipes


– can be used by any set of processes, not just forked from a common
ancestor which created the anonymous pipe
– FIFO files have names and directory links
– created with a mknod command or system call :
#define MYNODE (S_IFIFO | S_IRUSR | S_IWUSR)
mknod (“myfifo”, MYMODE, 0);
– can be opened and used by any process with access permission
– same functionality as anonymous pipes with blocking by default
– concurrent writers are permitted
– writes of up to 4096 bytes are guaranteed to be atomic
• System V Unix Compatibility
– shared memory - shmget() creates a sharable memory segment identified by
an ID which can be used by any other process by attaching it with a shmat()
system call
– semaphores (extremely messy!) and messages also available
» see : Advanced Programming in the UNIX Environment by W.R. Stevens
29
Operating Systems: IPC

• Sockets
– intended for communications across a distributed network
– uses the connectionless Internet Protocol and IP addresses at the lowest
level e.g. 129.215.58.7
– datagram packets transmitted to destination IP host
– User Datagram Protocol (UDP) or Transmission Control Protocol (TCP) at
the application level
– TCP is a connection based protocol, with order of packet arrival guaranteed
– a socket is a communication end-point
– once a TCP-socket connection between two processes is made, end-points
made to act like ordinary files, using read() and write() system calls.

30
Operating Systems: IPC

– a Client-Server system :
» creating a socket :
- sd = socket ( family, type, protocol );
» binding to a local address :
- bind ( sd, IP address, addrlen ); // address includes port number connection
by client process :
- connect ( sd, IP address, addrlen ); // servers IP address
» server listens for client connection requests :
- listen ( sd, queuelen ); // number of requests that can be queued
» and accepts the request :
- newsd = accept ( sd, IP address, addrlen );
– accept() normally blocks if no client process waiting to establish a connection
– can be made non-blocking for server to enquire whether any clients waiting
– connectionless communication with UDP datagram sockets also possible
using sendto() and recvfrom() system calls

31
Operating Systems: IPC

// Server-side socket demo progam

#include <fcntl.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <errno.h>

void close_socket(int sd) {


int cs;
if ((cs = close(sd)) < 0) {
printf(“close socket failed: %s\n”, strerror(errno));
exit(1);
}
}

#define SERVER (129<<24 | 215<<16 | 58<<8 | 7)


#define MESSAGELEN 1024
#define SERVER_PORT 5000

void main() {
int ssd, csd;
struct sockaddr_in server, client;
int sockaddrlen, clientlen, ca;
char message[MESSAGELEN];
int messagelen;

sockaddrlen = sizeof(struct sockaddr_in);

32
Operating Systems: IPC

// create socket
if ((ssd = socket (AF_NET, SOCK_STREAM, 0)) < 0) {
printf(“socket create failed: %s\n”, strerror(errno));
exit(1):
} else printf(server socket created, ssd = %d\n”, ssd);

// bind socket to me
server.sin_family = AF_INET;
server.sin_port = htons(SERVER_PORT); // big/little-endian conversion
server.sin_addr.s_addr = htonl(SERVER);
bzero(&server.sin_zero, 8);
if (bind(ssd, (struct sockaddr *) &server, sockaddrlen) < 0) {
printf(“server bind failed: %s\n”, strerror(errno));
exit(1):
}

// listen on my socket for clients


if (listen(ssd, 1) < 0) {
printf(“listen failed: %s\n”, strerror(errno));
close_socket(ssd);
exit(1);
}

// make socket non-blocking


fcntl(ssd, F_SETFL, fcntl(ssd, F_GETFL) | O_NDELAY);

33
Operating Systems: IPC

// accept a client (non-blocking)


clientlen = sockaddrlen;
while ((csd = accept(ssd, &client, &clientlen)) < 0) {
if (errno == EAGAIN) {
printf(“no client yet\n”);
sleep(1); // wait a sec
} else {
printf(“accept failed: %s\n”, strerror(errno));
close_socker(ssd);
exit(1);
}

ca = ntohl(client.sin_addr.s_addr);
printf(“client accepted, csd = %d, IP = %d.%d.%d.%d\n”,
csd, (ca>>24)&255, (ca>>16)&255, (ca>>8)&255, ca&255);

// send message to client


sprintf(message, “Server calling client : hi!\n”);
messagelen - strlen(message)+1;
if (write(csd, message, messagelen) != messagelen) {
printf(write failed\n”);
close_socket(ssd);
exit(1);
} else printf(“message sent to client\n”);

// receive message from client


if (read(csd, message, MESSAGELEN) < 0) {
if (errno == EAGAIN) {

34
Operating Systems: IPC

printf(“no client message yet\n”);


sleep(1);
} else {
printf(“read failed: %s\n”, strerror(errno));
close_socket(ssd);
exit(1);
}

printf(“client message was:\n%s”, message);

close_socket(ssd);

35
Operating Systems: IPC

// Client-side socket demo program


#include <fcntl.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <errno.h>

void close_socket(int sd) {


int cs;
if ((cs = close(sd)) < 0) {
printf(“close socket failed: %s\n”, strerror(errno));
exit(1);
}
}

#define SERVER (129<<24 | 215<<16 | 58<<8 | 7)


#define MESSAGELEN 1024
#define SERVER_PORT 5000

void main() {
int ssd, csd;
struct sockaddr_in server, client;
int sockaddrlen, clientlen, ca;
char message[MESSAGELEN];
int messagelen;

sockaddrlen = sizeof(struct sockaddr_in);

36
Operating Systems: IPC

// server address
server.sin_family = AF_INET;
server.sin_port = htons(SERVER_PORT);
server.sin_addr.s_addr = htonl(SERVER);

for (;;) {

//create socket
if ((csd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
printf(“client socket create failed: %s\n”, strerror(errno));
exit(1);
} else prinf(“client socket create, csd = %d\n”, csd);

// try to connect to server


if (connect(csd, (struct sockaddr *) &server, sockaddrlen) < 0) {
printf(“connect failed: %s\n”, strerror(errno));
// need to destroy socket before trying to connect again
close_socket(csd);
sleep(1);
} else break;
}
printf(“connected to server\n”);

// make socket non-blocking


fcntl(csd, F_SETFL, fcntl(csd, F_GETFL) | O_NDELAY);

37
Operating Systems: IPC

// receive a message from server


while (read(csd, message, MESSAGELEN) < 0) {
if (errno == EAGAIN) {
printf(“no server message yet\n”);
sleep(1);
} else {
printf(“read failed: %s\n”, strerror(errno));
close_socket(csd);
exit(1);
}
}
printf(“server message was:\n%s”, message);

// send a message to server


sprintf(message, “Client calling server : ho!\n”);
messagelen = strlen(message)+1;
if (write(csd, message, messagelen) != messagelen) {
printf(“write failed\n”);
close_socket(csd);
exit(1);
} else printf(“message sent to server\n”);

close_socket(csd);

38
Operating Systems: IPC

• Signals
– the mechanism whereby processes are made aware of events occurring
– asynchronous - can be received by a process at any time in its execution
– examples of Linux signal types:
» SIGINT : interrupt from keyboard
» SIGFPE : floating point exception
» SIGKILL : terminate receiving process
» SIGCHLD : child process stopped or terminated
» SIGSEGV : segment access violation
– default action is usually for kernel to terminate the receiving process
– process can request some other action
» ignore the signal - process will not know it happened
- SIGKILL and SIGSTOP cannot be ignored
» restore signal’s default action
» execute a pre-arranged signal-handling function
- process can register a function to be called
- like an interrupt service routine

39
Operating Systems: IPC

- when the handler returns, control is passed back to the main process code and
normal execution continues

» to set up a signal handler:


#include <signal.h>
#include <unistd.h>

void (*signal(int signum, void (*handler)(int)))(int);

» signal is a call which takes two parameters


- signum : the signal number
- handler : a pointer to a function which takes a single integer parameter and
returns nothing (void)
» return value is itself a pointer to a function which:
- takes a single integer parameter and returns nothing
– example:
» gets characters until a newline typed, then goes into an infinite loop
» uses signals to count ctrl-c’s typed at keyboard until newline typed

40
Operating Systems: IPC

#include <stdio.h>
#include <signal.h>
#include <unistd.h>

int ctrl_C_count = 0;
void (* old_handler)(int);
void ctrl_c(int);

main () {
int c;
old_handler = signal (SIGINT, ctrl_c );

while ((c = getchar()) != ‘\n’);

printf(“ctrl_c count = %d\n”, ctrl_c_count);

(void) signal (SIGINT, old_handler);

for (;;);
}

void ctrl_c(int signum) {


(void) signal (SIGINT, ctrl_c); // signals are automatically reset
++ctrl_c_count;
}

• see also the POSIX sigaction() call - more complex but better
41

You might also like