There are many models of IPC, with different advantages and problems.
This can be a very fast method of information transfer because RAM can be used. Synchronisation is a major problem - if the first process keeps writing data, how can it ensure that the second one reads it?
Pipes can be un-named. This is the norm in Unix where a process creates a pipe and then forks, so that the two processes share the pipe between them.
If the processes do not come from a common ancestor then they can only share a pipe if they can both name it (otherwise they could not find it). Named pipes usually appear as though they were files in the file system.
#define SIZE 1024 char buf[SIZE]; src = open(infile, O_RDONLY); dst = creat(outfile, 0777); while ((nread = read(src, buf, SIZE)) > 0) write(dst, buf, nread);
int pfd[2]; pipe(pfd);
write(pfd[1], buf, size); read(pfd[0], buf, SIZE);
Before fork
After fork
This gives two read ends and two write ends. The read end of the pipe will
not be closed until both of the read ends are closed, and the write end will
not be closed until both the write ends are closed.
Either process can write into the pipe, and either can read from it. Which process will get what is not known.
For predictable behaviour, one of the processes must close its read end, and
the other must close its write end. Then it will become a simple pipeline again.
Suppose the parent wants to write down a pipeline to a child.
The parent closes its read end, and writes into the other end. The child
closes its write end and reads from the other end.
When the processes have ceased communication, the parent closes its write end. This means that the child gets eof on its next read, and it can close its read end.
#include <stdio.h> #define SIZE 1024 int main(int argc, char **argv) { int pfd[2]; int nread; int pid; char buf[SIZE]; if (pipe(pfd) == -1) { perror("pipe failed"); exit(1); } if ((pid = fork()) < 0) { perror("fork failed"); exit(2); } if (pid == 0) { /* child */ close(pfd[1]); while ((nread = read(pfd[0], buf, SIZE)) != 0) printf("child read %s\n", buf); close(pfd[0]); } else { /* parent */ close(pfd[0]); strcpy(buf, "hello..."); /* include null terminator in write */ write(pfd[1], buf, strlen(buf)+1); close(pfd[1]); } exit(0); }
Suppose one of the processes replaces itself by an ``exec''. The new process will have files for descriptors 0, 1, 2, 3 and 4 open. How will it know which are the ones belonging to the pipe? It can't.
After dup2
After close
Without any error checks, the program to do this is
int main(void) { int pfd[2]; pipe(pfd); if (fork() == 0) { close(pfd[1]); dup2(pfd[0], 0); close(pfd[0]); execlp("wc", "wc", (char *) 0); } else { close(pfd[0]); dup2(pfd[1], 1); close(pfd[1]); execlp("ls", "ls", (char *) 0); } }With checks, it is
#include <stdio.h> int main(void) { int pfd[2]; int pid; if (pipe(pfd) == -1) { perror("pipe failed"); exit(1); } if ((pid = fork()) < 0) { perror("fork failed"); exit(2); } if (pid == 0) { close(pfd[1]); dup2(pfd[0], 0); close(pfd[0]); execlp("wc", "wc", (char *) 0); perror("wc failed"); exit(3); } else { close(pfd[0]); dup2(pfd[1], 1); close(pfd[1]); execlp("ls", "ls", (char *) 0); perror("ls failed"); exit(4); } exit(0); }
ps | sed 1d | wc -l
). In this case there are lots of choices
The Linux shared memory calls come from Unix System V and are not yet standardised under POSIX. They are likely to be similar to these:
#include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> int shmget(key_t key, int size, int shmflg); char *shmat(int shmid, char *shmaddr, int shmflg); int shmdt(char *shmaddr);The purpose of shmget is to create or to locate a piece of shared memory. The pieces of shared memory are identified by a key, which is an integer agreed upon by all processes that are going to use this piece of memory. The call also specifies the size of the memory and the permission flags (eg -rwx------).
The call shmat takes the shared memory identifier and performs a mapping of that into the address space of the process, returning a pointer to it. From then on it can be used just like any pointer to memory.
The call shmdt detaches it from the address space of the process.
So shmget and shmat are the shared memory equivalent of malloc, and shmdt is the equivalent of free.
#include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> int main(void) { int shmid; char *shmPtr; int n; if (fork() == 0) { shmid = shmget(2041, 32, 0666 | IPC_CREAT); if (shmid == -1) exit(1); shmPtr = shmat(shmid, 0, 0); if (shmPtr == (char *) -1) exit(2); for (n = 0; n < 26; n++) shmPtr[n] = 'a' + n; for (n = 0; n < 26; n++) putchar(shmPtr[n]); putchar('\n'); } else { /* wait for the child to terminate so that we know it has written to shared memory */ wait(NULL); shmid = shmget(2041, 32, 0); if (shmid == -1) exit(1); shmPtr = shmat(shmid, 0, 0); if (shmPtr == (char *) -1) exit(2); for (n = 0; n < 26; n++) putchar(shmPtr[n]); putchar('\n'); shmdt(shmid); } exit(0); }