Assuming we were to create our own lock that could be locked by one thread and unlocked by another, our lock structure might look like
struct slock {
sem_t *semp;
char name[_POSIX_NAME_MAX];
};
#include "slock.h"
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
struct slock * s_alloc()
{
struct slock *sp;
static int count;
if((sp=malloc(sizeof(struct slock)))==NULL)
return NULL;
do
{
snprintf(sp->name, sizeof(sp->name), "/%ld.%d",(long)getpid(), count++);
sp->semp=sem_open(sp->name, O_CREAT|O_EXCL, SIRWXU, 1);
}while((sp->semp==SEM_FAILED)&&(errno==EEXIST))
if(sp->semp==SEM_FAILED)
{
free(sp);
return NULL;
}
sem_unlink(sp->name);
return 0;
}
void s_free(struct slock *sp)
{
sem_close(sp->semp);
free(sp);
}
int s_lock(struct slock *sp)
{
return sem_wait(sp->semp);
}
int s_trylock(struct slock *sp)
{
return sem_trywait(sp->semp);
}
int s_unlock(struct slock *sp)
{
return sem_post(sp->semp);
}
Mutual exclusion using a POSIX semaphore
Client–Server Proper ties
Let’s detail some of the properties of clients and servers that are affected by the various types of IPC used between them. The simplest type of relationship is to have the client fork and exec the desired server. Two half-duplex pipes can be created before the fork to allow data to be transferred in both directions. Figure 15.16 is an example of this arrangement. The server that is executed can be a set-user-ID program, giving it special privileges. Also, the server can determine the real identity of the client by looking at its real user ID. (Recall from Section 8.10 that the real user ID and real group ID don’t change across an exec.)
With this arrangement, we can build an open server. (We show an implementation of this client–server mechanism in Section 17.5.) It opens files for the client instead of the client calling the open function. This way, additional permission checking can be added, above and beyond the normal UNIX system user/group/other permissions. We assume that the server is a set-user-ID program, giving it additional permissions (root permission, perhaps). The server uses the real user ID of the client to determine whether to give it access to the requested file. This way, we can build a server that allows certain users permissions that they don’t normally have.
Multiple possibilities exist with message queues.
1.A single queue can be used between the server and all the clients, using the type field of each message to indicate the message recipient.
2.Alternatively, an individual message queue can be used for each client.
Network IPC: Sockets
Introduction
In this chapter, we look at the mechanisms that allow processes running on different computers (connected to a common network) to communicate with one another—network IPC.
In this chapter, we describe the socket network IPC interface, which can be used by processes to communicate with other processes, regardless of where they are running — on the same machine or on different machines. Indeed, this was one of the design goals of the socket interface. The same interfaces can be used for both intermachine communication and intramachine communication. Although the socket interface can be used to communicate using many different network protocols, we will restrict our discussion to the TCP/IP protocol suite in this chapter, since it is the de facto standard for communicating over the Internet.
The socket API as specified by POSIX.1 is based on the 4.4BSD socket interface. Although minor changes have been made over the years, the current socket interface closely resembles the interface when it was originally introduced in 4.2BSD in the early 1980s.
Socket Descriptors
A socket is an abstraction of a communication endpoint. Just as they would use file descriptors to access files, applications use socket descriptors to access sockets. Socket descriptors are implemented as file descriptors in the UNIX System. Indeed, many of the functions that deal with file descriptors, such as read and write, will work with a socket descriptor.
To create a socket, we call the socket function.
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
Most systems define the AF_LOCAL domain also, which is an alias for AF_UNIX. The AF_UNSPEC domain is a wildcard that represents ‘‘any’’ domain. Historically, some platforms provide support for additional network protocols, such as AF_IPX for the NetWare protocol family, but domain constants for these protocols are not defined by the POSIX.1 standard.
A datagram, therefore, provides a connectionless service. A byte stream (SOCK_STREAM), in contrast, requires that, before you can exchange data, you set up a logical connection between your socket and the socket belonging to the peer with which you wish to communicate.
A datagram is a self-contained message. Sending a datagram is analogous to mailing someone a letter. You can mail many letters, but you can’t guarantee the order of delivery, and some might get lost along the way. Each letter contains the address of the recipient, making the letter independent from all the others. Each letter can even go to different recipients.
A SOCK_STREAM socket provides a byte-stream service; applications are unaware of message boundaries. This means that when we read data from a SOCK_STREAM socket, it might not return the same number of bytes written by the sender. We will eventually get everything sent to us, but it might take several function calls.
A SOCK_SEQPACKET socket is just like a SOCK_STREAM socket except that we get a message-based service instead of a byte-stream service. This means that the amount of data received from a SOCK_SEQPACKET socket is the same amount as was written. The Stream Control Transmission Protocol (SCTP) provides a sequential packet service in the Internet domain.
A SOCK_RAW socket provides a datagram interface directly to the underlying network layer (which means IP in the Internet domain). Applications are responsible for building their own protocol headers when using this interface, because the transport protocols (TCP and UDP, for example) are bypassed. Superuser privileges are required to create a raw socket to prevent malicious applications from creating packets that might bypass established security mechanisms.