Technical University of Varna

advertisement
Inter-process communication
Trifon Ruskov
ruskov@tu-varna.acad.bg
Technical University of Varna - Bulgaria
Process Communication and Synchronization
The UNIX IPC (Inter-process communications) facilities provide
methods for multiple processes to communicate with one another:
ƒ Signals
ƒ Half-duplex UNIX pipes
ƒ Named pipes
ƒ Message queues
ƒ Shared memory segments
ƒ Semaphore sets
Trifon Ruskov
Technical University - Varna
2
Signals in Unix
9 Signals are software-generated interrupts that are sent to a process when an
event happens.
9 Signals can be posted to a process when the system detects a software
event.
9 Signals can also come directly from the kernel when a hardware event is
encountered.
Each signal has a default action:
ƒ The signal is discarded
ƒ The process terminate
Each signal falls into one of five classes:
ƒ Hardware condition
ƒ Software condition
ƒ Input/output notification
ƒ Process control
ƒ Resource control
Trifon Ruskov
Technical University - Varna
3
Signals in Unix (cont.)
Signal definitions are in <signal.h>
#define
#define
#define
#define
#define
#define
#define
SIGHUP
SIGINT
SIGQIUT
SIGILL
SIGTRAP
SIGKILL
SIGALRM
1
2
3
4
5
9
14
/*
/*
/*
/*
/*
/*
/*
hang up */
interrupt */
quit */
illegal instruction */
hardware interrupt */
hard kill */
alarm clock */
etc.
Trifon Ruskov
Technical University - Varna
4
Sending Signals
ƒ A system call that sens a signal (signal) to a process (pid)
int kill (int pid, int signal)
returns:
0 – successful call
-1 – otherwise (errorno is set)
ƒ From the Linux shell:
kill –9 37
Trifon Ruskov
Technical University - Varna
5
Signal Handling
ƒ
ƒ
An application program can specify a function, called a signal
handler, to be invoked when a specific signal is received.
When a signal handler is invoked on receipt of a signal, it is said to
catch the signal.
A process can deal with a signal in one of the following ways:
ƒ The process can let the default action happen.
ƒ The process can block the signal (some signals cannot be ignored).
ƒ The process can catch the signal with a handler.
Receiving signals is straightforward with the function:
int (* signal (int sig, void (*func)())) ()
The function signal() will call the function pointed to by func if the
process receives a signal sig.
Trifon Ruskov
Technical University - Varna
6
Signal Handling (cont.)
func can have three values:
ƒ SIG_DFL – a pointer to the system default function SIG_DFL ( )
ƒ SIG_IGN – a pointer to the system ignore function SIG_IGN ( )
ƒ function – a user specified function
Examples:
1. To ignore a Ctrl/C command from command line
signal(SIGINT, SIG_IGN);
2. To reset system to default action
signal(SIGINT, SIG_DFL);
Trifon Ruskov
Technical University - Varna
7
Signal Handling (cont.)
3. To trap a Ctrl/C but not quit on this signal
#include <stdio.h>
#include <signal.h>
void sigproc(void);
void quitproc(void);
int main(){
signal(SIGINT, sigproc);
signal(SIGQUIT, quitproc);
for(;;);
// infinite loop
}
void sigproc(){
signal(SIGINT, sigproc);
printf(”CTRL/C pressed\n”);
}
void quitproc(){
exit(0);
}
Trifon Ruskov
Technical University - Varna
8
Signal Handling (cont.)
4. To delete a temporary file after abnormal termination
#include <signal.h>
void cleanup(void);
int main () {
signal(SIGINT, cleanup);
...
creat(tempfile, 0);
...
unlink(tempfile);
exit(0);
}
void cleanup() {
unlink(tempfile);
exit(1);
}
Trifon Ruskov
Technical University - Varna
9
Signal Handling (cont.)
Note:
9 Signals are reset to default when accepted by signal
handler. Why?
System call pause:
pause();
Stop the process until some signal is received.
Trifon Ruskov
Technical University - Varna
10
Pipes in Unix
ƒ
ƒ
ƒ
Pipes provide one-way (half-duplex) communications between related
processes.
Pipes are implemented based on the kernel I/O buffers.
A pipe is a tool for connecting the standard output of one process to the
standard input of another process, typically used in UNIX shell pipelines.
Example:
Counting of files in current directory:
ls > tmpfile && wc –w tmpfile && rm tmpfile
The same example with a shell pipeline:
ls | wc –w
The pipeline reduces execution time by:
ƒ Parallel execution of the commands
ƒ Avoiding disk operations for temporary saving of intermediate data
ƒ No temporary files to be deleted
Trifon Ruskov
Technical University - Varna
11
Pipes in Unix (cont.)
Pipes are accessed in a similar way as ordinary files:
ƒ They have assigned inodes.
ƒ Reading and writing are through file descriptors.
fd1
stdout
P1
fd0
kernel I/O
buffers
stdin
P2
Differences between pipes and files:
ƒ The pipe inode resides in the kernel, not in the physical file system.
ƒ A pipe can exist only during the existence of its creator process.
ƒ A pipe has limited size.
Trifon Ruskov
Technical University - Varna
12
Creating Pipes
System call that creates a pipe:
int pipe(int fd[2]);
Parameters:
fd[] – array of file descriptors
returns:
0 – successful call
-1 – otherwise (errorno is set)
This call opens two file descriptors:
fd[0] – file descriptor for reading
fd[1] – file descriptor for writing
Example:
Redirecting the standard output to a pipe
int fd[2];
pipe(fd);
close(1);
dup2(1, fd[1]);
close(fd[1]);
Trifon Ruskov
//
//
//
//
Create a pipe
Close stdout
Duplicate stdout to the fd[1]
Close fd[1]
Technical University - Varna
13
Using Pipes
ƒ
ƒ
ƒ
Process, created a pipe, can use it to communicate with a child process.
A child process inherits any open file descriptors from the parent, including
pipe descriptors.
Communicating processes must close unused file descriptor of the pipe.
Example:
int main() {
int fd[2], pid;
}
pipe(fd);
if (pid=fork()) == -1 ) {
perror(“fork”);
exit(1);
}
if (pid == 0)
close(fd[0]); // Child process closes input side of pipe
else
close(fd[1]); // Parent process closes output side of pipe
Trifon Ruskov
Technical University - Varna
14
Example: Implementation of ls | wc –w
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
pipe
int main() {
stdout
int fd[2], dead, status;
if (fork()) wait(&status);
else {
ls
pipe(fd);
if (fork()) {
close(1);
dup2(1, fd[1]);
close(fd[1]);
close(fd[0]);
execl(“/bin/ls”, “ls”, 0);
} else {
close(0);
dup2(0, fd[0]);
close(fd[0);
close(fd[1]);
execl(“/usr/bin/wc”, “wc”, “-w”, 0);
}
}
}
Trifon Ruskov
Technical University - Varna
stdin
wc -w
15
Example: Read/Write through a Pipe
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(void){
int fd[2], nbytes, status;
pid_t childpid;
char string[] = "Hello!\n";
char readbuffer[20];
}
fd[0]
read
parent
fd[1]
pipe
write
child
pipe(fd);
if(!fork()){
close(fd[0]);
// Child process closes input side of pipe
//Send "string" through the pipe
write(fd[1], string, strlen(string));
exit(0);
} else {
close(fd[1]); // Parent process closes output side of pipe
// Read in a string from the pipe
nbytes = read(fd[0], readbuffer, sizeof(readbuffer));
printf("Received string: %s", readbuffer);
wait(&status);
}
return(0);
Trifon Ruskov
Technical University - Varna
16
Inter-process communication with pipes and signals
SIGFPE
tty1
P1
tty2
P2
ttyn
P0
tty0
main
program
Pn
slave
programs
Trifon Ruskov
pipe
SIGTERM
Technical University - Varna
17
Inter-process communication with pipes and signals (cont.)
Slave program:
#include <signal.h>
#include <sys/types.h>
#include <sys/tty.h>
int main(int argc, char *argv[]) {
int n;
char bufer[TTYHOG];
for (;;) {
n = read(0, buffer, TTYHOG];
if (n == 0) {
// Reached EOF
kill(atoi(argv[1]), SIGFPE);
close(1);
exit(NULL);
}
kill(atoi(argv[1])), SIGTERM);
write(1, buffer, n);
}
}
Trifon Ruskov
Technical University - Varna
18
Inter-process communication with pipes and signals (cont.)
Main program:
#include
#include
#include
#include
<signal.h>
<stdio.h>
<sys/types.h>
<sys/tty.h>
int fd[2];
char pids[TTYHOG];
int slave_count;
void service() {
int n;
char buffer[TTYHOG];
// SIGTERM processing
signal(SIGTERM, service);
n = read(fd[0], buffer, TTYHOG];
write(1, buffer, n);
}
void slavedrop() {
signal(SIGFPE, slavedrop);
If (--slave_count = 0)
exit(NULL);
}
Trifon Ruskov
// SIGFPE processing
Technical University - Varna
19
Inter-process communication with pipes and signals (cont.)
void slave(char *tty) {
if (fork() == 0) {
int tfd;
// Slave program initialization
// stdio redirection to a terminal
close(0);
tfd = open(tty, 0);
if (tfd != 0) {
fprintf(stderr, “bad tty %s\n”, tty);
exit(1);
}
// stdout redirection to a pipe
close(1);
dup(fd[1]);
close(fd[1]);
close(fd[0]);
// Execute the slave program
execl(“./slave”, “slave”, pids, 0);
fprintf(stderr, “Can’t exec slave on %s\n”, tty);
exit(1);
}
}
Trifon Ruskov
Technical University - Varna
20
Inter-process communication with pipes and signals (cont.)
// Main program. Started as <program_name> /dev/tty1 /dev/tty2
int main(int argc, char *argv[]) {
int i;
signal(SIGTERM, service);
signal(SIGFPE, slavedrop);
sprintf(pids, “%06d”, getpid());
pipe(fd);
slave_count = argc – 1;
for (i=1; i<argc; i++)
slave(argv[i]);
close(fd[1]);
for (;;)
pause();
}
Trifon Ruskov
Technical University - Varna
21
Named pipes
Named pipes are used for inter-process communications.
Features:
ƒ Exist as special files in the physical file system
ƒ Any unrelated processes can access a named pipe and share data through it
ƒ Access to named pipes is regulated by the usual file permissions
ƒ Pipe data is accessed in a FIFO style
ƒ Once created, a named pipe remains in the file system until explicitly deleted
Creation:
ƒ By UNIX shell commands. Example:
mknod <filename> p
mkfifo a=rw <filename>
ƒ By systems calls. Example:
mknod(char *pathname, mode_t mode, dev_t dev);
mknod(“/tmp/myfifo”, S_IFIFO | 0660, 0);
Trifon Ruskov
Technical University - Varna
22
Named pipes (cont.)
Same I/O operations style on named pipes and regular files – open(),
read() and write() calls.
Implementation of I/O operations:
ƒ By system calls.
ƒ By library functions.
Semantics of open() call:
ƒ Blocking. The process that opens the named pipe for reading, sleeps
until another process opens it for writing, and v.v.
ƒ Non-blocking. Flag O_NONBLOCK, used in open() call disables
default blocking.
Pipes have size limitations.
Trifon Ruskov
Technical University - Varna
23
Named pipes. Example
Client-server communication through a pipe
Server
#include <fcntl.h>
...
#define PIPE "fifo"
int main(){
int fd;
char readbuf[20];
server
pipe
client
mknod(PIPE, S_IFIFO | 0660, 0); // create pipe
fd = open(PIPE, O_RDONLY, 0);
// open pipe
for (;;) {
if (read(fd, &readbuf, sizeof(readbuf)) < 0){ //read from pipe
perror("Error reading pipe");
exit(1);
}
printf("Received string: %s\n", readbuf); // process data
}
exit(0);
}
Trifon Ruskov
Technical University - Varna
24
Named pipes. Example (cont.)
Client
#include <stdio.h>
...
#define PIPE “fifo”
int main(){
int fd;
char writebuf[20] = “Hello”; // open pipe
fd = open(PIPE, O_WRONLY, 0);
// write to pipe
write(fd, writebuf, sizeof(writebuf));
exit(0);
}
Trifon Ruskov
Technical University - Varna
25
Inter-Process Communication (IPC)
Unix System V IPC facilities provide a method for multiple
processes to communicate with one another. There are several
methods of IPC available to UNIX programmers:
ƒ Messages
ƒ Shared memory
ƒ Semaphores
Each method is based on a specific object. The objects of various
methods have some common features. They:
ƒ are described in a table
ƒ have unique kernel identifiers
ƒ have unique user keys
ƒ have individual access permissions
ƒ have status information collected
ƒ are dynamically created and deleted
Trifon Ruskov
Technical University - Varna
26
Inter-Process Communication (cont.)
To share an object, a process must:
ƒ be able to identify it (know its key)
ƒ have appropriate privileges
ƒ gain access by a “get” system call
The particular “get” system call for each method is used for:
ƒ Creating an object (if it doesn’t exist)
ƒ Checking process permissions
ƒ Associating an unique ID to the key
Unix shell commands for IPC:
ƒ ipcs – shows the status of all IPC objects
ƒ ipcrm msg/shm/sem <object_ID> - deletes a particular
object
Trifon Ruskov
Technical University - Varna
27
IPC Messages
Features of the IPC message method:
ƒ Two or more processes can exchange information through a
system message queue.
ƒ The sending process places a message into the queue.
ƒ The receiving process reads a message from the queue.
ƒ Messages have a type, used by the process to select
appropriate message.
ƒ Message have a fixed length.
ƒ Processes must share a common key to gain access to the
message queue.
Trifon Ruskov
Technical University - Varna
28
IPC Messages Mechanism
Internal data structures, maintained by the kernel:
queue headers
message headers
type size
type size
msgID
type size
data area
message
data
message
data
message
data
Trifon Ruskov
Technical University - Varna
29
IPC Messages Mechanism (cont.)
Message queue header contains:
ƒ The queue key
ƒ The queue permissions
ƒ Times of the last send and receive operations on queue and pid of their
initiators
ƒ Message data and current queue state
ƒ Pointers to queues of processes, blocked on send and receive operations
Message structure
ƒ Type (positive number), used for selecting a message from the queue
ƒ Data with an application-dependent format
Trifon Ruskov
Technical University - Varna
30
Getting Access to a Message Queue
int msgget(key_t key, int flag);
Parameters:
ƒ key – an user defined value, associated with a message queue
ƒ flag - control flags and settings for the queue permissions
returns:
ƒ msgID – message queue identifier on success
ƒ -1 – otherwise (errno is set)
This system call compares the given key to values that exist within the kernel
for other message queues. At that point, the open or access operation is
dependent upon the contents of the flag argument:
ƒ IPC_CREAT - create the queue if it doesn’t already exist in the kernel.
ƒ IPC_CREAT | IPC_EXCL – function fails if required queue already exists.
Trifon Ruskov
Technical University - Varna
31
Sending Messages
int msgsnd(int msqid, const void *msgp, size_t msgsz,
int msgflg);
Parameters:
ƒ msgid – a message queue identifier.
ƒ msgp – a pointer to a message buffer in the process address
space.
ƒ mgsz - size of the message text in bytes.
ƒ msgflg – set to 0 or IPC_NOWAIT.
returns:
ƒ 0 – on success
ƒ -1 – otherwise (errno is set)
The function attempts to put the message, pointed by the msgp, into the
existing queue with identifier msgid. The message must be previously
constructed with type and text (data).
Trifon Ruskov
Technical University - Varna
32
Sending Messages (cont.)
Example of message structure:
struct mymsg {
long mtype;
char mtext[MSGSZ];
}
// message type
// message data with MSGSZ length
The following action of msgsnd are possible:
ƒ The message is successfully put into the message queue.
ƒ The calling process is blocked – not enough free space in the queue.
ƒ Error code is returned.
Send operations may be blocking or non-blocking:
ƒ Blocking is the default action in case of full queue.
ƒ Blocking can be prevented if IPC_NOWAIT is set as msgflg argument
of the msgsnd(). The calling process will return immediately with an
error code.
Trifon Ruskov
Technical University - Varna
33
Receiving Messages
int msgrcv(int msqid, void *msgp, size_t msgsz, long
msgtyp, int msgflg);
Parameters:
ƒ msgid – a message queue identifier.
ƒ msgp – a pointer to a message buffer in the process address
space.
ƒ mgsz - size of the message text in bytes.
ƒ msgtyp - the received message type as specified by the sending
process.
ƒ msgflg – set to 0 or IPC_NOWAIT.
returns:
ƒ Number of bytes, copied into *msgp – on success
ƒ -1 – otherwise (errno is set)
The function takes a message from the queue with identifier msgid and
writes it in process address space, pointed to by msgp.
Trifon Ruskov
Technical University - Varna
34
Receiving Messages (cont.)
The appropriate message is selected according the value of msgtyp:
ƒ msgtyp = 0 – select the first message in the message queue.
ƒ msgtyp > 0 – select the first message with type = msgtyp.
ƒ msgtyp < 0 – select the first message with type <= |msgtyp|.
The following actions of msgrcv are possible:
ƒ The needed message is selected and extracted from the queue. One
waiting process is waken up and allowed to perform send operation.
ƒ The calling process is blocked – no message in the queue.
ƒ Error code is retuned.
Receive operations may be blocking or non-blocking:
ƒ Blocking is the default action in case of empty queue or requested
message is not found.
ƒ Blocking can be prevented if IPC_NOWAIT is set as msgflg argument of
the msgrcv(). The calling process will return immediately with an
error code.
If the size of the physical message data is greater than msgsz, and flag
MSG_ NOERROR is asserted, then the message is truncated, and only msgsz
bytes are returned.
Trifon Ruskov
Technical University - Varna
35
Controlling Message Queues
Direct manipulation of internal kernel message structures can be done by:
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
Parameters:
ƒ msgid – a message queue identifier.
ƒ cmd – one of:
ƒ IPC_STAT – gets information about the status of the queue and stores
it in the address pointed to by buf.
ƒ IPC_SET – set permissions and other information for the queue.
ƒ IPC_RMID – remove the message queue msgid.
ƒ buf – pointer to data buffer.
returns:
ƒ 0 – on success
ƒ -1 – otherwise (errno is set)
Trifon Ruskov
Technical University - Varna
36
IPC Messages. Example
// Message-receiver process
#include <sys/ipc.h>
#include <sys/msg.h>
...
#define KEY
75
#define MSGSIZE 70
#define MSGTYPE 5
typedef struct msgs {
long mtype;
char mtext[MSGSIZE];
} msg_str;
message queue
P1
P2
int main (){
int msgid;
msg_str msg;
msgid = msgget(KEY, IPC_CREAT | 0660); // Create message queue and get its ID
...
if (msgrcv(msgid, (struct msgbuf *)&msg, MSGSIZE, MSGTYPE, 0) < 0){
perror("msgrcv"); exit(1); }
// msg processing
...
}
Trifon Ruskov
Technical University - Varna
37
IPC Messages. Example (cont.)
// Message-sender process
#include <sys/ipc.h>
#include <sys/msg.h>
...
#define KEY
75
#define MSGSIZE 70
#define MSGTYPE 5
typedef struct msgs {
long mtype;
char mtext[MSGSIZE];
} msg_str;
int main (){
int msgid;
msg_str msg;
char str[6]="HELLO";
}
msgid = msgget(KEY, IPC_CREAT | 0660); // Create message queue and get its ID
msg.mtype = MSGTYPE;
strcpy(msg.mtext, str);
if (msgsnd(msgid,(struct msgbuf *)&msg, MSGSIZE, 0) < 0) {
perror("msgsnd"); exit(1); }
...
Trifon Ruskov
Technical University - Varna
38
IPC Shared Memory
Shared memory is a mapping of an area (segment) of memory into several
process address spaces, so it could be shared by them. This is the fastest form of
inter-process communication. Processes access this segment directly by pointers.
A segment can be created by one process, and subsequently used by any number
of processes.
A process creates a shared memory segment using shmget()
int shmget(key_t key, size_t size, int shmflg);
Parameters:
ƒ key - an user-defined value, associated with a shared memory segment
ƒ size - size in bytes of the requested shared memory
ƒ shmflg - specifies the creation control flags and initial access
permissions
returns:
ƒ shmID – shared memory segment identifier on success
ƒ -1 – otherwise (errno is set)
This call is also used to get the ID of an existing shared segment if calling
process has enough permissions.
Trifon Ruskov
Technical University - Varna
39
Attaching a Shared Memory Segment
Once a process has a valid IPC identifier for a given segment, its next step
is to attach or map the segment into its own address space:
void *shmat(int shmid, const void *shmaddr, int shmflg);
Parameters:
ƒ shmid - shared memory segment identifier
ƒ shmaddr – virtual address in process address space where the shared
memory is to be mapped. If the shmaddr argument is zero, the kernel
tries to find an unmapped region. This is the recommended method.
ƒ shmflg - specifies the shared memory access mode (SHM_RDONLY –
segment is read-only).
returns:
ƒ address at which segment was attached to the process – on success
ƒ -1 – otherwise (errno is set)
Trifon Ruskov
Technical University - Varna
40
Detaching a Shared Memory Segment
After a shared memory segment is no longer needed by a process, it
should be detached by calling shmdt().
int shmdt(const void *shmaddr );
Parameters:
ƒ shmaddr – shared memory segment address returned by shmat()
returns:
ƒ 0 – on success
ƒ -1 – otherwise (errno is set)
Trifon Ruskov
Technical University - Varna
41
Controlling a Shared Memory Segment
Direct manipulation of internal kernel shared memory structures may be
done by:
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
Parameters:
ƒ shmid – a shared memory segment identifier.
ƒ cmd – one of:
ƒ IPC_STAT – retrieves information for a segment, and stores it in
the address pointed to by buf.
ƒ IPC_SET – set the permissions and other information for the shared
memory segment.
ƒ IPC_RMID – marks the segment for removal. The actual removal
itself occurs when the last process currently attached to the
segment has properly detached it.
ƒ buf – pointer to data buffer.
returns:
ƒ 0 – on success
ƒ -1 – otherwise (errno is set)
Trifon Ruskov
Technical University - Varna
42
IPC Shared Memory. Example
// Shared memory clear process Pc
#include <sys/ipc.h>
#include <sys/shm.h>
...
#define SHMKEY 75
shared memory
Pc
integer variable
+1
int main (){
int shmid;
int *pint;
char *addr;
P1
-1
P2
// Create shared memory and get its ID
shmid = shmget(SHMKEY, sizeof(int), 0660 | IPC_CREAT);
addr = shmat(shmid, 0, 0);
// Attach to the shared memory
pint = (int *)addr;
*pint=0;
// Clear shared memory
}
Trifon Ruskov
Technical University - Varna
43
IPC Shared Memory. Example (cont.)
// Inter-process communication via shared memory
#include <sys/ipc.h>
#include <sys/shm.h>
...
#define SHMKEY 75
int main (){
int i, shmid, *pint, status;
char *addr;
// Create shared memory and get its ID
shmid = shmget(SHMKEY, sizeof(int), 0660 | IPC_CREAT);
addr = shmat(shmid, 0, 0);
pint = (int*)addr;
}
switch (fork()){
// Fork communicating processes
case 0: pint = (int *)addr;
for (i=0;i<5000000;i++) *pint = *pint + 1;
break;
default:
pint = (int *)addr;
for (i=0;i<5000000;i++) *pint = *pint - 1;
wait(&status);
printf(“Shared memory value is: %d\n”, *pint);
}
Trifon Ruskov
Technical University - Varna
44
IPC Semaphores
Semaphores can be operated as individual units or as elements of a set.
A semaphore is represented as:
ƒ A value.
ƒ A count of processes waiting for its value to increase.
ƒ A count of processes waiting for its value to become 0.
ƒ An identifier of the last process completed a semaphore operation.
Semaphore
sets table
Arrays of semaphores
0
1
2
3
0
1
2
3
4
5
6
0
Trifon Ruskov
Technical University - Varna
45
Getting Access to Semaphores
int semget(key_t key, int nsems, int semflag);
Parameters:
ƒ key – a user defined value, associated with a semaphore set.
ƒ nsems - specifies the number of semaphores that should be created
in a new set.
ƒ semflag - control flags and settings for the semaphore permissions.
returns:
ƒ semID – semaphore set identifier on success
ƒ -1 – otherwise (errno is set)
This system call compares the given key to values that exist within the
kernel for other semaphore sets. At that point, the open or access operation
is dependent upon the contents of the semflag argument:
ƒ IPC_CREAT - create the semaphore set if it doesn’t already exist in
the kernel.
ƒ IPC_CREAT | IPC_EXCL – function fails if the specified semaphore set
already exists.
Trifon Ruskov
Technical University - Varna
46
Semaphore Operations
Operations on a semaphore set can be performed by:
int semop(int semid,struct sembuf *sops,size_t nsops);
Parameters:
ƒ semid – semaphore set identifier.
ƒ sops - pointer to an array of structures, each containing the following
information about a semaphore operation:
ƒ the semaphore number.
ƒ the operation to be performed.
ƒ control flags, if any.
ƒ nsops - specifies the length of the array of operations.
returns:
ƒ 0 – on success (all operations performed)
ƒ -1 – otherwise (errno is set)
Trifon Ruskov
Technical University - Varna
47
Semaphore Operations (cont.)
The sembuf structure specifies a semaphore operation:
struct sembuf {
ushort_t sem_num;
short
sem_op;
short
sem_flg;
};
/* semaphore number */
/* semaphore operation */
/* operation flags */
Either all operations, specified in sops, are executed, or none (“all or
nothing”). If the execution of some sops operation is not possible, the kernel
restores the old values of the semaphores and suspends the process.
When the event, on which the process waits, takes place, the semop() system
call is restarted and starts its execution from the beginning (i.e. from the first
element of sops).
Prior to the execution of each operation, the validity of the specified
semaphore and its read/write privileges are checked.
Trifon Ruskov
Technical University - Varna
48
Semaphore Operations (cont.)
The semaphore value is changed depending on sem_op:
ƒ sem_op > 0 – Semaphore value is incremented by the sem_op value
(corresponds to releasing multiple resources). All processes, waiting on
semaphore increment are awaken.
ƒ sem_op = 0 – The kernel checks the current semaphore value
(sem_val). Two cases are distinguished:
ƒsem_val = 0 – The kernel continues with the execution of the remaining
operations from sops.
ƒsem_val ≠ 0 – Current process is suspended until sem_val becomes 0.
ƒ sem_op < 0 – (corresponds to obtaining multiple resources). Two
cases:
ƒIf |sem_op| <= sem_val, then sem_val := sem_val + sem_op. If sem_val =
0, the kernel wakes all processes, waiting for zero semaphore value.
ƒIf |sem_op| > sem_val, then kernel suspends the current process until the
semaphore is incremented.
Trifon Ruskov
Technical University - Varna
49
IPC Semaphores. Example
// Inter-process communication via shared memory
// synchronized with semaphores
#include <sys/ipc.h>
shared memory
#include <sys/shm.h>
#include <sys/sem.h>
...
#define SHMKEY 55
integer variable
#define SEMKEY 81
int main (){
int i, shmid, *pint, status;
char *addr;
short init[1];
struct opt {
short sem_num;
short sem_op;
short sem_flg;
};
struct sembuf p,v;
semaphore
+1
P1
-1
P2
// Create shared memory and get its ID
shmid = shmget(SHMKEY, sizeof(int), 0660 | IPC_CREAT);
addr = shmat(shmid, 0, 0); // Attach to the shared memory
pint = (int *)addr;
*pint=0;
// Clear shared memory
Trifon Ruskov
Technical University - Varna
50
IPC Semaphores. Example (cont.)
// Create and initialize semaphores
init[0] = 1;
semid = semget(SEMKEY, 1, IPC_CREAT | 0660);
p.sem_num = 0;
p.sem_op = -1;
p.sem_flg = 0;
v.sem_num = 0;
v.sem_op = 1;
v.sem_flg = 0;
semctl(semid, 1, SETALL, init);
switch (fork()) {
// Fork communicating processes
case 0: pint = (int *)addr;
for (i=0;i<500000;i++){
semop(semid, &p, 1);
*pint = *pint + 1;
semop(semid, &v, 1);
}
break;
default:
pint = (int *)addr;
for (i=0;i<500000;i++){
semop(semid, &p, 1);
*pint = *pint - 1;
semop(semid, &v, 1);
}
wait(&status);
printf(“Shared memory value is: %d\n”, *pint);
}
}
Trifon Ruskov
Technical University - Varna
51
Download