Friday, November 15, 2019

Linux process and IPC :

Process:
  fork()
  exec()
  PCB?
 
IPC:
1. Pipes

              Pipe Always unidirectional, used between parent and child communication.
Data written to the write end of pipe can be read from read end of pipe.

int fd[2];
Pipe created  two file descriptors using pipe system call. i.e pipe(fd); returns -1 on failure.
fd[0] - read, fd[1] - write its similar for file descriptors regular read()/write() api's used for read and write.
 - Data written to the write end of the pipe is Buffered by the Kernel until it is read from the read end of the pipe.
- If process attempt to read from empty pipe then read will block until data available.
- If process attempt to write on a non empty(full data in pipe) write api will block until sufficient data  has been read from pipe to allow the write to complete.
- Non blocking I/O possible by setting the system flag O_NONBLOCK using fcntl system api.
- The communication channel provided by pipes is a byte stream, there is no concept of message   boundaries.
- If write end fd[1] closed and read end try to read it will see EOF and read returns 0.
- If read end fd[0] closed and write will cause the SIGPIPE signal will generate to calling process, if process ignore the signel write will fail with the EPIPE error.
 -  close api should be used to close the file descriptors close(fd[0]/fd[1]).
- the capacity of the pipe is same as the system page size. 
Disadvantage: its works only related process i.e fd file descriptors should be within a process.
---------
how the kernel implements pipes (at a high level).
  • Linux has a VFS (virtual file system) module called pipefs, that gets mounted in kernel space during boot
  • pipefs is mounted alongside the root file system (/), not in it (pipe’s root is pipe:)
  • pipefs cannot be directly examined by the user unlike most file systems
  • The entry point to pipefs is the pipe(2) syscall
  • The pipe(2) syscall is used by shells and other programs to implement piping, and just creates a new file in pipefs, returning two file descriptors (one for the read end, opening using O_RDONLY, and one for the write end, opened using O_WRONLY)
  • pipefs is stored using an in-memory file system

---------

2. Fifo - named pipes:
     The disadvantage of pipe is within a process fd will be used for read write, to over come that named pipes i.e to communicate with unrelated process by providing the name to the pipe.

    Command line api's to create the mkfifo, mknod.

Ex1: mkfifo file_name will create the file of type pipe.
Ex2: mknod filename p create the file with type pipe, here p denotes the pipe type file. as mknode used for other types like char file creation etc.
Main features: FIFO is the name within a file system, it can be open just like normal files, as same as read/write for reading writing.
- Blocking on fifo is open for read but it will block until write writes to it and vice versa for write.
 - cab be unblock using O_NONBLOCKING flag in open system call.
Disadvantage: pipes follows strict FIFO behaviour.

3. System V message queues(MQ):
      Message Queue can behave similar to pipes but FIFO order can be changed.
      MQ having system wide limit which can be seen with ipcs -l command '
       ------ Messages Limits --------
max queues system wide = 32000
max size of message (bytes) = 8192
default max size of queue (bytes) = 16384

      MQ is a sequence of messages each which has two parts:
      1. the payload, which is an array bytes.
      2. A type, positive number, will be helpful to flexible retrieval(based on need, not like FIFO order to read).
    To create the message queue, we need ipc key, key can be created using ftok() api.
    #include <sys/types.h>
#include <sys/ipc.h>

key_t ftok (const char *pathname, int proj_id);


      pathname must be existing accessible file, content of file is immaterial.
      proj_id  lowest 8 bits of project id will be used and it should be not zero.

     msgget: system call gets the message queue identifier for the given key.
     #include <sys/msg.h>
        int msgget (key_t key, int msg_flags);

         key : is received from ftok. some times special key IPC_PRIVATE used for private mq.details
         msg_flags: IPC_CREATE for new key, if it order with IPC_EXCL for already existing q with permissions with octal values.
     msget returns integer identifier, will be used for next send receive and control message api's.

    msgctl: with this can do control operations on message queue.
      int msgctl (int msqid, int cmd, struct msqid_ds *buf);
      msqid: qid returned from msgget.
      cmd: cammand can be IPC_RMID, IPC_STAT and IPC_SET. will use IPC_RMID to remove message queue once its usage finishes.more on msgq buffer info

    msgsnd : sending messages to message queue.
int msgsnd (int msqid, const void *msgp, size_t msgsz, int msgflg);
       msgp: message buffer  
       msgsz : message size
       msgflg: it can be 0, or IPC_NOWAIT.
msgrcv: receiving message from message queue.
ssize_t msgrcv (int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
    
       msgp: message buffer    
       msgsz : message size
        msgtype : type of message to be retrieved.
       msgflg: it can be 0, or IPC_NOWAIT.
Example code :
//----keep below data in queue.h----------
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <string.h>


#define projid 123
#define PathName  "/tmp/myqueue" /* any existing, accessible file would do */
#define MsgLen    4
#define MsgCount  6


typedef struct {
  long type;                 /* must be of type long */
  char payload[MsgLen + 1];  /* bytes in the message */
} queuedMessage;
void report_and_exit(const char* msg) {
  perror(msg);
  exit(-1); /* EXIT_FAILURE */
}

//------------------------------------------
// Inserver server.cpp:
#include "queue.h"

int main() {
  key_t key = ftok(PathName, ProjectId);
  if (key < 0) report_and_exit("couldn't get key...");
  int qid = msgget(key, 0666 | IPC_CREAT);
  if (qid < 0) report_and_exit("couldn't get queue id...");
  char* payloads[] = {"msg1", "msg2", "msg3", "msg4", "msg5", "msg6"};
  int types[] = {1, 1, 2, 2, 3, 3}; /* each must be > 0 */
  int i;
  for (i = 0; i < MsgCount; i++) {
    /* build the message */
    queuedMessage msg;
    msg.type = types[i];
    strcpy(msg.payload, payloads[i]);
    /* send the message */
    msgsnd(qid, &msg, sizeof(msg), IPC_NOWAIT); /* don't block */
    printf("%s sent as type %i\n", msg.payload, (int) msg.type);
  }
  return 0;
}

//client.cpp:
#include "queue.h"
int main() {
  key_t key= ftok(PathName, ProjectId); /* key to identify the queue */
  if (key < 0) report_and_exit("key not gotten...");
  int qid = msgget(key, 0666 | IPC_CREAT); /* access if created already */
  if (qid < 0) report_and_exit("no access to queue...");
  int types[] = {3, 1, 2, 1, 3, 2}; /* different than in sender */
  int i;
  for (i = 0; i < MsgCount; i++) {
    queuedMessage msg; /* defined in queue.h */
    if (msgrcv(qid, &msg, sizeof(msg), types[i], MSG_NOERROR | IPC_NOWAIT) < 0)
      puts("msgrcv trouble...");
    printf("%s received as type %i\n", msg.payload, (int) msg.type);
  }
  /** remove the queue **/
  if (msgctl(qid, IPC_RMID, NULL) < 0)  /* NULL = 'no flags' */
    report_and_exit("trouble removing queue...");
  return 0;
}


Problem with the pipes, fifos and message queues, the work involved in sending data from one process to another is like this. Process P1 makes a system call to send data to Process P2. The message is copied from the address space of the first process to the kernel space during the system call for sending the message. Then, the second process makes a system call to receive the message. The message is copied from the kernel space to the address space of the second process. 

The shared memory mechanism does away with this copying overhead.


4. shared memory
Fasted IPC is shared memory.
 Shared memory segment created by the kernel and mapped to the data segment of the address space of the requested process.
Shared Memory

To use system V IPC like above message queues, need system v ipc key, which can get with ftok api.
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok (const char *pathname, int proj_id);


shmget: it gets the shared memory segment associated with key.

#include<sys/shm.h>
int shmid = shmget(key_t key, size_t size, int shmflgs); 
key - obtained with ftok or it can be IPC_PRIVATE.
size - size of the shared memory segment to be created, its a multiples of PAGE_SIZE.
shmflgs - IPC_CREATE | 0660 permissions.

shmat: With this calling process can attach the shared memory  segment with shmid got from shmget.
void *shmat (int shmid, const void *shmaddr, int shmflg);
 shmid - from shmget return value.
shmaddr - it can be null or process can specify the address at which the shared meory segment should be attached with shmaddr.
shmflg - SHM_RDONLY there are more flags..
 On error returns 1, on success returns shared memory address.

shmdt: with this detach the shared memory segment from calling process  address space.
int shmdt (const void *shmaddr);
shmaddr - shared memory address space returned at shmat
 on return -1 for error, 0 on success.

shmctl: to control the share memory segment.
int shmctl (int shmid, int cmd, struct shmid_ds *buf);
shmid - from shmget.
cmd - IPC_STAT, IPC_RMID etc.
shmid_ds is a structure to control shared meory segment params.

return -1 on error


5. Shared File
      
6. semaphore
7. pthreads
8. mutex
9. conditional variables

10. unix socket for local IPC 
     https://www.softprayog.in/programming/interprocess-communication-using-unix-domain-sockets

system calls:

Kernel and its internals:

Driver programming:


No comments:

Post a Comment

Port forwarding issues and port collusion by more then one process

Port forwarding script to identify collusions  Identifying local and remote port forwarding processes, as well as checking for potential col...