760 likes | 815 Views
Course code: 10CS62. Interprocess Communication. Engineered for Tomorrow. Prepared by : M.Chandana Department of CSE. Contents. Shared Memory, Client-Server Properties Stream Pipes Passing File Descriptors An Open Server-Version 1 Client-Server Connection Functions.
E N D
Course code: 10CS62 Interprocess Communication Engineered for Tomorrow Prepared by : M.Chandana Department of CSE
Contents • Shared Memory, • Client-Server Properties • Stream Pipes • Passing File Descriptors • An Open Server-Version 1 • Client-Server Connection Functions. • Out – of – band Data • Socket Addressing
Shared memory • Shared memory allows two or more processes to share a given region of memory. • This is the fastest form of IPC, because the data does not need to be copied between the client and the server. • The only trick in using shared memory is synchronizing access to a given region among multiple processes. • If the server is placing data into a shared memory region, the client shouldn’t try to access the data until the server is done.
The XSI shared memory segments are anonymous segments of memory. • The kernel maintains a structure with at least the following members for each shared memory segment: struct shmid_ds { struct ipc_perm shm_perm; size_t shm_segsz; /* size of segment in bytes */ pid_t shm_lpid; /* pid of last shmop() */ pid_t shm_cpid; /* pid of creator */ shmatt_t shm_nattch; /* number of current attaches */ time_t shm_atime; /* last-attach time */ time_t shm_dtime; /* last-detach time */ time_t shm_ctime; /* last-change time */ ... };
The first function called is usually shmget, to obtain a shared memory identifier. • #include <sys/shm.h> • int shmget(key_t key, size_t size, int flag); • Returns: shared memory ID if OK, −1 on error When a new segment is created, the following members of the shmid_ds structure are initialized. • The ipc_perm structure is initialized. The mode member of this structure is set to the corresponding permission bits of flag. • shm_lpid, shm_nattch, shm_atime, and shm_dtime are all set to 0.
• shm_ctime is set to the current time. • shm_segsz is set to the size requested. The size parameter is the size of the shared memory segment in bytes. The shmctl function is the catchall for various shared memory operations. • #include <sys/shm.h> • int shmctl(int shmid, int cmd, struct shmid_ds *buf ); • Returns: 0 if OK, −1 on error
The cmd argument specifies one of the following five commands to be performed, on the segment specified by shmid.
Once a shared memory segment has been created, a process attaches it to its address space by calling shmat. #include <sys/shm.h> void *shmat(intshmid, const void *addr, intflag); Returns: pointer to shared memory segment if OK, −1 on error The address in the calling process at which the segment is attached depends on the addrargument and whether the SHM_RND bit is specified in flag.
The identifier remains in existence until some process (often a server) specifically removes it by calling shmctl with a command of IPC_RMID. #include <sys/shm.h> int shmdt(const void *addr); Returns: 0 if OK, −1 on error The addr argument is the value that was returned by a previous call to shmat. If successful, shmdt will decrement the shm_nattch counter in the associated shmid_ds structure.
Client–Server Properties • Open – Server Arrangement • It opens files for the client instead of the client calling the open function. • 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. • In this example, since the server is a child of the parent, all the server can do is pass back the contents of the file to the parent.
Multiple possibilities exist with message queues, when they are used for communicatio • 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. Before sending the first request to a server, each client creates its own message queue with a key of IPC_PRIVATE. The server also has its own queue, with a key or identifier known to all clients. The client sends its first request to the server’s well-known queue, and this request must contain the message queue ID of the client’s queue. The server sends its first response to the client’s queue, and all future requests and responses are exchanged on this queue.
If FIFOs are used for the communication, • The client must create its own FIFO and set the file access permissions of the FIFO so that only user-read and user-write are on. • We assume that the server has superuser privileges, so the server can still read and write to this FIFO. • When the server receives the client’s first request on the server’s well-known FIFO (which must contain the identity of the client-specific FIFO), the server calls either stat or fstat on the client-specific FIFO. • The server assumes that the effective user ID of the client is the owner of the FIFO (the st_uid field of the stat structure).
The server verifies that only the user-read and user-write permissions are enabled. • As another check, the server should look at the three times associated with the FIFO (the st_atime, st_mtime, and st_ctime fields of the stat structure) to verify that they are recent (no older than 15 or 30 seconds, for example). • If a malicious client can create a FIFO with someone else as the owner and set the file’s permission bits to user-read and user-write only, then the system has other fundamental security problems.
Passing File Descriptors • Passing an open file descriptor between processes is a powerful technique. It can lead to different ways of designing client–server applications. • It allows one process (typically a server) to do everything that is required to open a file (involving such details as translating a network name to a network address, dialing a modem, and negotiating locks for the file) and simply pass back to the calling process a descriptor that can be used with all the I/O functions.
We define the following three functions that we use to send and receive file descriptors #include "apue.h" int send_fd(int fd, int fd_to_send); int send_err(int fd, int status, const char *errmsg); Both return: 0 if OK, −1 on error int recv_fd(int fd, ssize_t (*userfunc)(int, const void *, size_t)); Returns: file descriptor if OK, negative value on error
The send_fd function sends the descriptor fd_to_send across using the UNIX domain socket represented by fd. • The send_err function sends the errmsg using fd, followed by the status byte. The value of status must be in the range −1 through −255. • Clients call recv_fd to receive a descriptor. If all is OK (the sender called send_fd), the non-negative descriptor is returned as the value of the function. Otherwise, the value returned is the status that was sent by send_err (a negative value in the range −1 through −255).
Fig: Passing an open file from the top process to the bottom process
Three macros are used to access the control data, and one macro is used to help calculate the value to be used for cmsg_len. #include <sys/socket.h> unsigned char *CMSG_DATA(struct cmsghdr *cp); Returns: pointer to data associated with cmsghdr structure struct cmsghdr *CMSG_FIRSTHDR(struct msghdr *mp); Returns: pointer to first cmsghdr structure associated with the msghdr structure, or NULL if none exists
struct cmsghdr *CMSG_NXTHDR(struct msghdr *mp,struct cmsghdr *cp); Returns: pointer to next cmsghdr structure associated with the msghdr structure given the current cmsghdr structure, or NULL if we’re at the last one unsigned int CMSG_LEN(unsigned int nbytes); Returns: size to allocate for data object nbytes large • The CMSG_LEN macro returns the number of bytes needed to store a data object of size nbytes, after adding the size of the cmsghdr structure, adjusting for any alignment constraints required by the processor architecture, and rounding up.
An Open Server - Version 1 • Using file descriptor passing, we now develop an open server—a program that is executed by a process to open one or more files. • Instead of sending the contents of the file back to the calling process, however, this server sends back an open file descriptor. • As a result, the open server can work with any type of file (such as a device or a socket) and not simply regular files. • The client and server exchange a minimum amount of information using IPC
The server can easily be contacted by any client, similar to the client calling a library function. • If we need to change the server, only a single program is affected. • The server can be a set-user-ID program, providing it with additional permissions that the client does not have.
We define the following application protocol between the client and the server. • 1. The client sends a request of the form ‘‘open <pathname> <openmode>\0’’ across the fd-pipe to the server. The <openmode> is the numeric value, in ASCII decimal, of the second argument to the open function. This request string is terminated by a null byte. • 2. The server sends back an open descriptor or an error by calling either send_fd or send_err.
We first have the header, open.h, which includes the standard headers and defines the function prototypes. • #include "apue.h" • #include <errno.h> • #define CL_OPEN "open" /* client’s request for server */ • int csopen(char *, int); • The main function is a loop that reads a pathname from standard input and copies the file to standard output. The function calls csopen to contact the open server and return an open descriptor. • client main function, version 1
The function csopen does the fork and exec of the server, after creating the fd-pipe. • Click here for “The csopen function, version 1” • The child closes one end of the fd-pipe, and the parent closes the other. For the server that it executes, the child also duplicates its end of the fd-pipe onto its standard input and standard output. • The parent sends to the server the request containing the pathname and open mode. Finally, the parent calls recv_fd to return either the descriptor or an error. If an error is returned by the server, write is called to output the message to standard error.
The opend.h header will now includes the standard headers and declares the global variables and function prototypes. • The main function reads the requests from the client on the fd-pipe (its standard input) and calls the function handle_request. • Click here for “ The server main function, version 1 “
The function handle_request does all the work. It calls the function buf_args to break up the client’s request into a standard argv-style argument list and calls the function cli_args to process the client’s arguments. • If all is OK, open is called to open the file, and then send_fd sends the descriptor back to the client across the fd-pipe (its standard output). • If an error is encountered, send_err is called to send back an error message, using the client–server protocol. • Click here for “ The handle_request function, version 1”
The client’s request is a null-terminated string of white-space-separated arguments. • The function buf_args breaks this string into a standard argv-style argument list and calls a user function to process the arguments. • Click here for “ The buf_args function” • The server’s function that is called by buf_args is cli_args .It verifies that the client sent the right number of arguments and stores the pathname and open mode in global variables. • Click here for “ The cli_args function “ • This completes the open server that is invoked by a fork and exec from the client
Client – Server Connection Functions • Sockets : • 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); • Returns: file (socket) descriptor if OK, −1 on error The domainargument determines the nature of the communication, including the address format.
The typeargument determines the type of the socket, which further determines the communication characteristics
The protocol argument is usually zero, to select the default protocol for the given domain and socket type. • The default protocol for a SOCK_STREAM socket in the AF_INET communication domain is TCP (Transmission Control Protocol) and default protocol for a SOCK_DGRAM socket in the AF_INET communication domain is UDP (User Datagram Protocol).
Communication on a socket is bidirectional. We can disable I/O on a socket with the shutdown function. #include <sys/socket.h> int shutdown(int sockfd, int how); Returns: 0 if OK, −1 on error • If how is SHUT_RD, then reading from the socket is disabled. If how is SHUT_WR, then we can’t use the socket for transmitting data. We can use SHUT_RDWR to disable both data transmission and reception.
There are several reasons to shutdown a socket instead of closing it. • First, close will deallocate the network endpoint only when the last active reference is closed. If we duplicate the socket the socket won’t be deallocated until we close the last file descriptor referring to it. The shutdown function allows us to deactivate a socket independently of the number of active file descriptors referencing it. • Second, it is sometimes convenient to shut a socket down in one direction only. For example, we can shut a socket down for writing if we want the process we are communicating with to be able to tell when we are done transmitting data, while still allowing us to use the socket to receive data sent to us by the process.
Connection establishment • If we’re dealing with a connection-oriented network service (SOCK_STREAM or SOCK_SEQPACKET), then before we can exchange data, we need to create a connection between the socket of the process requesting the service (the client) and the process providing the service (the server). We use the connect function to create a connection. #include <sys/socket.h> int connect(int sockfd, const struct sockaddr *addr, socklen_t len); Returns: 0 if OK, −1 on error • The address we specify with connect is the address of the server with which we wish to communicate.
A server announces that it is willing to accept connect requests by calling the listen function. #include <sys/socket.h> int listen(int sockfd, int backlog); Returns: 0 if OK, −1 on error • The backlog argument provides a hint to the system regarding the number of outstanding connect requests that it should enqueue on behalf of the process. • The actual value is determined by the system, but the upper limit is specified as SOMAXCONN in <sys/socket.h>.
Once a server has called listen, the socket used can receive connect requests. We use the accept function to retrieve a connect request and convert it into a connection. #include <sys/socket.h> int accept(int sockfd, struct sockaddr *restrict addr,socklen_t *restrict len); Returns: file (socket) descriptor if OK, −1 on error The file descriptor returned by accept is a socket descriptor that is connected to the client that called connect. This new socket descriptor has the same socket type and address family as the original socket (sockfd).
Data Transfer • Three functions are available for sending data, and three are available for receiving data. First, we’ll look at the ones used to send data. • #include <sys/socket.h> • ssize_t send(int sockfd, const void *buf, size_t nbytes, int flags); • Returns: number of bytes sent if OK, −1 on error • Send allows us to specify flags to change how the data we want to transmit is treated. • The buf and nbytes arguments have the same meaning as they do with write.
The sendto function is similar to send. The difference is that sendto allows us to specify a destination address to be used with connectionless sockets. #include <sys/socket.h> ssize_t sendto(int sockfd, const void *buf, size_t nbytes, int flags, const struct sockaddr *destaddr, socklen_t destlen); Returns: number of bytes sent if OK, −1 on error • With a connection-oriented socket, the destination address is ignored, as the destination is implied by the connection.
With a connectionless socket, we can’t use send unless the destination address is first set by calling connect, so sendto gives us an alternate way to send a message. • We can call sendmsg with a msghdr structure to specify multiple buffers from which to transmit data, similar to the writev function . #include <sys/socket.h> ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags); Returns: number of bytes sent if OK, −1 on error
POSIX.1 defines the msghdr structure to have at least the following members: struct msghdr { void *msg_name; /* optional address */ socklen_t msg_namelen; /* address size in bytes */ struct iovec *msg_iov; /* array of I/O buffers */ int msg_iovlen; /* number of elements in array */ void *msg_control; /* ancillary data */ socklen_t msg_controllen; /* number of ancillary bytes */ int msg_flags; /* flags for received message */ ... };
The recv function is similar to read, but allows us to specify some options to control how we receive the data. #include <sys/socket.h> ssize_t recv(int sockfd, void *buf, size_t nbytes, int flags); Returns: length of message in bytes, 0 if no messages are available and peer has done an orderly shutdown, or −1 on error
If we are interested in the identity of the sender, we can use recvfrom to obtain the source address from which the data was sent. #include <sys/socket.h> ssize_t recvfrom(int sockfd, void *restrict buf, size_t len, int flags, struct sockaddr *restrict addr, socklen_t *restrict addrlen); Returns: length of message in bytes,0 if no messages are available and peer has done an orderly shutdown, or −1 on error If addr is non-null, it will contain the address of the socket endpoint from which the data was sent.
To receive data into multiple buffers, similar to readv or if we want to receive ancillary data , we can use recvmsg. • We can set the flags argument to change the default behavior of recvmsg. On return, the msg_flags field of the msghdr structure is set to indicate various characteristics of the data received. #include <sys/socket.h> ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags); Returns: length of message in bytes,0 if no messages are available and peer has done an orderly shutdown,or −1 on error
The possible values on return from recvmsg are summarized as follows:
OUT_OF BAND DATA • Out-of-band data is an optional feature supported by some communication protocols, allowing higher-priority delivery of data than normal. • Out-of-band data is sent ahead of any data that is already queued for transmission. • The socket interface to out-of-band data is heavily influenced by TCP’s implementation of out-of-band data.TCP refers to out-of-band data as ‘‘urgent’’ data. • To generate urgent data, we specify the MSG_OOB flag to any of the three send functions
When urgent data is received, we are sent the SIGURG signal if we have arranged for signal generation by the socket. • We can use the F_SETOWN command to fcntl to set the ownership of a socket. • The F_GETOWN command can be used to retrieve the current socket ownership • Thus, the call owner = fcntl(sockfd, F_GETOWN, 0);will return with owner equal to the ID of the process configured to receive signals from the socket if owner is positive and with the absolute value of owner equal to the ID of the process group configured to receive signals from the socket if owner is negative.
TCP supports the notion of an urgent mark : the point in the normal data stream where the urgent data would go. • We can choose to receive the urgent data inline with the normal data if we use the SO_OOBINLINE socket option. • To help us identify when we have reached the urgent mark, we can use stockatmark function. #include <sys/socket.h> int sockatmark(int sockfd); Returns: 1 if at mark, 0 if not at mark, −1 on error
When the next byte to be read is at the urgent mark, sockatmark will return 1. • When out-of-band data is present in a socket’s read queue, the select function will return the file descriptor as having an exception condition pending. • We can choose to receive the urgent data inline with the normal data, or we can use the MSG_OOB flag with one of the recv functions to receive the urgent data ahead of any other queue data. • TCP queues only one byte of urgent data. If another urgent byte arrives before we receive the current one, the existing one is discarded.
Socket Addressing 1. Byte Ordering: • The byte order is a characteristic of the processor architecture, dictating how bytes are ordered within larger data types, such as integers. Figure shows how the bytes within a 32-bit integer are numbered.