Documents
Resources
Learning Center
Upload
Plans & pricing Sign in
Sign Out

Shared Memory

VIEWS: 8 PAGES: 49

									Shared Memory
Sharing data

   on a Unix system, each process has its own virtual
    address space, and the system makes sure no
    process would access the memory area of another
    process
   With shared memory, we declare a given section in
    the memory as one that will be used simultaneously
    by several processes
    –   This means that the data found in this memory section (or
        memory segment) will be seen by several processes
Virtual Memory Management Under
Unix

   the system divides memory into small pages each of
    the same size
   When the process is scheduled for running, its
    memory table is loaded by the operating system, and
    each memory access causes a mapping (by the
    CPU) to a physical memory page
   If the virtual memory page is not found in memory, it
    is looked up in swap space, and loaded from there
   When a process is being forked off from
    another process, the memory page table of
    the parent process is being copied to the
    child process
   If the child process will try to update any of
    these pages, then this page specifically will
    be copied, and then only the copy of the child
    process will be modified.
Allocating A Shared Memory Segment

   A shared memory segment first needs to be
    allocated (created), using the shmget()
    system call
   This call gets a key for the segment (like the
    keys used in msgget() and semget()), the
    desired segment size, and flags to denote
    access permissions
/* this variable is used to hold the returned segment identifier. */
int shm_id;
/* allocate a shared memory segment with size of 2048 bytes, */
/* accessible only to the current user. */
shm_id = shmget(100, 2048, IPC_CREAT | IPC_EXCL | 0600);
if (shm_id == -1)
           { perror("shmget: ");
           exit(1); }


 If several processes try to allocate a segment using the same ID,
 they will all get an identifier for the same page,
Attaching And Detaching A Shared
Memory Segment

   After we allocated a memory page, we need
    to add it to the memory page table of the
    process
   This is done using the shmat() (shared-
    memory attach) system call. Assuming
    'shm_id' contains an identifier returned by a
    call to shmget()
/* these variables are used to specify where the page is attached. */
char* shm_addr;
char* shm_addr_ro;
/* attach the given shared memory segment, at some free position */
/* that will be allocated by the system. */
shm_addr = shmat(shm_id, NULL, 0);
if (!shm_addr)
           { /* operation failed. */ perror("shmat: "); exit(1); }
/* attach the same shared memory segment again, this time in */
/* read-only mode. Any write operation to this page using this */
/* address will cause a segmentation violation (SIGSEGV) signal. */
shm_addr_ro = shmat(shm_id, NULL, SHM_RDONLY);
if (!shm_addr_ro) { /* operation failed. */ perror("shmat: "); exit(1); }
Placing Data In Shared Memory

   Any kind of data may be placed in a shared
    segment, except for pointers
   Since the same segment might be attached
    in a different virtual address in each process,
    a pointer referring to one memory area in
    one process might refer to a different
    memory area in another process
/* define a structure to be used in the given shared memory segment. */
struct country {
          char name[30];
          char capital_city[30];
          char currency[30];
          int population; };
/* define a countries array variable. */
int* countries_num;
struct country* countries;
/* create a countries index on the shared memory segment. */
countries_num = (int*) shm_addr;
*countries_num = 0;
countries = (struct country*) ((void*)shm_addr+sizeof(int));
strcpy(countries[0].capital_city, "U.S.A");
strcpy(countries[0].capital_city, "Washington");
strcpy(countries[0].currency, "U.S. Dollar");
countries[0].population = 250000000;
(*countries_num)++;

strcpy(countries[1].capital_city, "Israel");
strcpy(countries[1].capital_city, "Jerusalem");
strcpy(countries[1].currency, "New Israeli Shekel");
countries[1].population = 6000000;
(*countries_num)++;
strcpy(countries[1].capital_city, "France");
strcpy(countries[1].capital_city, "Paris");
strcpy(countries[1].currency, "Frank");
countries[1].population = 60000000;
(*countries_num)++;
/* now, print out the countries data. */
for (i=0; i < (*countries_num); i++)
{ printf("Country %d:\n", i+1);
printf(" name: %s:\n", countries[i].name);
printf(" capital city: %s:\n", countries[i].capital_city);
printf(" currency: %s:\n", countries[i].currency);
printf(" population: %d:\n", countries[i].population); }
Destroying A Shared Memory Segment



  /* this structure is used by the shmctl() system call. */
  struct shmid_ds shm_desc;
  /* destroy the shared memory segment. */
  if (shmctl(shm_id, IPC_RMID, &shm_desc) == -1)
             { perror("main: shmctl: "); }
Socket Programming
User and System
Programs




Kernel Support



Hardware
Sockets

   A socket is a communication mechanism
   A socket is normally identified by a small integer
    which may be called the socket descriptor
   Formally a socket is defined by a group of four
    numbers
    –   The remote host identification number or address
    –   The remote host port number
    –   The local host identification number or address
    –   The local host port number
   To the application programmer the sockets
    mechanism is accessed via a number of
    functions
    –   socket() create a socket
    –   bind() associate a socket with a network address
    –   connect() connect a socket to a remote network
        address
    –   listen() wait for incoming connection attempts
    –   accept() accept incoming connection attempts
   Data can be written to a socket using any of
    the functions
    –   write(),
    –   writev(),
    –   send(),
    –   sendto(),
    –   sendmsg().
   Data can be read from a socket using any of
    the functions
    –   read(),
    –   readv(),
    –   recv(),
    –   recvfrom(),
    –   recvmsg()
Daemon Processes

   The sockets mechanism is usually used to
    implement client-server applications
   The client process is directly or indirectly
    user driven whereas the server process sits
    on a host waiting for incoming connections.
   A server process will run unattended and
    continuously. In the Unix environment such
    processes are called daemon processes.
Creating a socket

 A socket is created using the function
  socket().
 The prototype is
  #include <sys/types.h>
  #include <sys/socket.h>

    int socket(int domain, int type, int protocol)
   domain is either AF_UNIX or AF_INET
    –   This parameter specifies whether the socket is to
        be used for communicating between Unix file
        system like objects or Internet objects
   type specifies the communications
    semantics
    –   SOCK_STREAM stream based full-duplex
        communication
    –   SOCK_DGRAM datagram based communication
   protocol is normally set to zero
   The return value from socket() is a small
    integer that may be used to refer to a socket
    in subsequent calls.
    –   It may be called the socket descriptor or handle
        and is analogous to a file descriptor.
Socket Options

   The behaviour of a socket can be modified
    using the functions setsockopt()
   The prototype of setsockopt() is
    #include <sys/types.h>
    #include <sys/socket.h>
    int setsockopt(int s, int level, int optname,
    char *optval, int *optlen)
   s is the socket number
   level is set to SOL_SOCKET
   optname indicates which option to modify.
    There are several options, see the manual
    for details.
   optval/optname are used to provide new
    values for the various options
The connect() function


   The connect() function is used by a client
    program to establish communication with a
    remote entity
    #include <sys/types.h>
    #include <sys/socket.h>
     int connect(int s, struct sockaddr *name, int
    namelen)
   s specifies the socket
   name points to an area of memory
    containing the address information
   namelen give the length of the address
    information block
   connect() is normally only used for
    SOCK_STREAM sockets.
IP Addresses for connect()

   If the AF_INET domain is being used this will
    be the address of an object of type struct
    sockaddr_in
     struct sockaddr_in {
            short sin_family;
            u_short sin_port;
            struct in_addr sin_addr;
            char sin_zero; }
struct in_addr {
    union { struct { u_char s_b1, s_b2, s_b3,
s_b4; };
          struct { u_short s_w1, s_w2; };
          u_long S_addr; } S_un; }
   The usual way of opening a connection is by
    using the library call gethostbyname() to
    determine the associated Internet address.

    #include <sys/socket.h>
    #include <netdb.h>
    struct hostent *gethostbyname(char *)
struct in_addr internet_address;
struct hostnet *hp; hp =
gethostbyname(remote address);
memcpy(&internet_address,
*(hp->h_addr_list),sizeof(struct in_addr);
Incoming connections bind()

   To accept incoming connection requests a
    server process must first create a socket
    using socket() and then use bind() to
    associate a port number with the socket
    #include <sys/types.h>
    #include <sys/socket.h>
    int bind(int s, struct sockaddr *name, int
    namelen)
Incoming Connections listen()


   Once an address has been bound to a
    socket it is then necessary to indicate the
    socket is to be listened to for incoming
    connection requests. This is done using the
    listen() function.

    int listen(int s, int backlog)
Incoming connections accept()


 Once the listen() call has returned the accept() call
  should be issued, this will block until a connection
  request is received from a remote host.
 #include<sys/types.h>
 #include<sys/socket.h>
  int accept(int s, struct sockaddr *addr, int *addrlen)
The return value of the call is the number of a new
  socket descriptor that should be used for all
  subsequent communication with the remote host.
Receiving Data

    read() may be used in the exactly the same way as for reading
     from files. There are some complications with non-blocking
     reads. This call may only be used with SOCK_STREAM type
     sockets.
    readv() may be used in the same way as read() to read into
     several separate buffers. This is called a scatter read. This call
     may only be used with SOCK_STREAM type sockets.
    recv() may be used in the same way as read(). The prototype is
     #include <sys/types.h>
    #include <sys/socket.h>
    int recv(int s, char *buff, int len, int flags) buff is the address of a
     buffer area.
   len is the size of the buffer.
   flags is formed by ORing MSG_OOB and
    MSG_PEEK allowing receipt of out of band
    data and allowing peeking at the incoming
    data. This call may only be used with
    SOCK_STREAM type sockets.
Sending Data

  write() may be used in exactly the same way as it is used to
   write to files. This call may only be used with SOCK_STREAM
   tpye sockets.
 writev() is similar to write() except that it writes from a set of
   buffers. This is called a gather write. This call may only be used
   with SOCK_STREAM type sockets.
 send() may be used in the same way as write(). The prototype
   is
 #include<sys/types.h>
 #include<sys/socket.h>
 int send(int s, char *msg, int len, int flags)
   s is the socket number.
   msg and len specify the buffer holding the
    text of the messagee.
Multiple Server Sessions


 listen(sd,2); /* at most 2 pending connections */
 do {
           nsd = accept(sd,&(work.s),&addrlen);
           pid = fork();
           if(pid == 0) {
                      /* Child process handles the dialogue
                      using descriptor 'nsd' */
                      close(nsd);
                      exit(0); /* end of child process */ }
           else close(nsd); /* parent won't be using 'nsd' */
 } while(1);
Summary

   The steps involved in establishing a socket
    on the client side are as follows:
    –   Create a socket with the socket() system call
    –   Connect the socket to the address of the server
        using the connect() system call
    –   Send and receive data. There are a number of
        ways to do this, but the simplest is to use the
        read() and write() system calls.
   The steps involved in establishing a socket on the
    server side are as follows:
    –   Create a socket with the socket() system call
    –   Bind the socket to an address using the bind() system call.
        For a server socket on the Internet, an address consists of a
        port number on the host machine.
    –   Listen for connections with the listen() system call
    –   Accept a connection with the accept() system call. This call
        typically blocks until a client connects with the server.
    –   Send and receive data
TCP Sequence
UDP Sequence
Useful UNIX Commands
   netstat –i prints information about the interfaces
   netstat –ni prints this information using numeric addresses
   loop back interface is called lo and the ethernet interface is called eth0
    or le0 depending on the machine
   netstat –r prints the routing table
   netstat | grep PORT_NO shows the state of the client socket
   ifconfig eth0 – Given the interface name ifconfig gives the details for
    each interface --- Ethernet Addr , inet_addr , Bcast , Mask , MTU
   ping IP_addr -- Sends a packet to the host specified by IP_addr and
    prints out the roundtrip time ( Uses ICMP messages)
   traceroute IP_addr -- Shows the path from this host to the destination
    printing out the roundtrip time for a packet to each hop in between
   Tcpdump communicates directly with Data Link layer UDP Packet fail
System Calls
   socket() – returns a socket descriptor
   int socket(int domain, int type, int protocol);
   bind() – What port I am on / what port to attach to
   int bind(int sockfd, struct sockaddr *my_addr, int addrlen);
   connect() – Connect to a remote host
   int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
   listen() – Waiting for someone to connect to my port
   int listen(int sockfd, int backlog);
   accept() – Get a file descriptor for a incomming connection
   int accept(int sockfd, void *addr, int *addrlen);
   send() and recv() – Send and receive data over a connection
   int send(int sockfd, const void *msg, int len, int flags);
   int recv(int sockfd, void *buf, int len, unsigned int flags);
   sendto() and recvfrom() – Send and receive data without connection
   int sendto(int sockfd, const void *msg, int len, unsigned int flags, const
    struct sockaddr *to, int tolen);
   int recvfrom(int sockfd, void *buf, int len, unsigned int flags, struct
    sockaddr *from, int *fromlen);
   close() and shutdown() – Close a connection Two way / One way
   getpeername() – Obtain the peer name given the socket file descriptor
   gethostname() – My computer name
   int sock_get_port(const struct sockaddr *sockaddr,socklen_t addrlen);
SELECT()

   One traditional way to write network servers is to
    have the main server block on accept(), waiting for a
    connection.
   Once a connection comes in, the server fork()s, the
    child process handles the connection and the main
    server is able to service new incoming requests.
   With select(), instead of having a process for each
    request, there is usually only one process that "multi-
    plexes" all requests, servicing each request as much
    as it can.
   select() works by blocking until something
    happens on a file descriptor (aka a socket).
   What's 'something'?
    –   Data coming in or being able to write to a file
        descriptor -- you tell select() what you want to be
        woken up by.
   How do you tell it?
    –   You fill up a fd_set structure with some macros.
   Most select()-based servers look pretty much the
    same:
    –     Fill up a fd_set structure with the file descriptors you want
        to know when data comes in on.
    –     Fill up a fd_set structure with the file descriptors you want
        to know when you can write on.
    –     Call select() and block until something happens.
    –     Once select() returns, check to see if any of your file
        descriptors was the reason you woke up. If so, 'service' that
        file descriptor in whatever particular way your server needs
        to (i.e. read in a request for a Web page).
    –     Repeat this process forever.

								
To top