This commit is contained in:
Joshua Powers
2022-09-14 10:34:38 -05:00
parent 5c5b03b6e0
commit b0caf2463e
14 changed files with 696 additions and 0 deletions

Binary file not shown.

13
Examples/ipc/Makefile Normal file
View File

@@ -0,0 +1,13 @@
CC=gcc
CFLAGS=-Wall -Wextra -g
LFLAGS=-lrt
BINS=$(patsubst %.c,%,$(wildcard *.c))
all: $(BINS)
clean:
rm -f $(BINS) MYFILE
%: %.c
$(CC) $(CFLAGS) -o $@ $< $(LFLAGS)

35
Examples/ipc/README.md Normal file
View File

@@ -0,0 +1,35 @@
# Inter-process communication (IPC) examples
CS3841 examples using IPC mechanisms to communicate between processes
* ipcfs1.c - Using the file system for inter-process communication
Shows a problem with the fact that the file position pointer is shared between parent and childUsing fork to create two processes
* ipcfs2.c - Using the file system for inter-process communication
Fixes the file position pointer problem in ipcfs1.c by using lseek to reset the pointer to the beginning
Shows the frustration with using the file system for IPC by needing to move the file position pointer around
* ipcpipe1.c - Using a pipe to communicate data from a child process to a parent process
* ipcpipe2.c - Pipes are unidirectional and require two pipes to send data in two directions
* ipcmsg1.c - Using a message queue to send data from a parent process to a child process
NOTE: message queues are persistent
* ipcmsg2.c - ses message queues to send multiple messages between two processes
Shows what can happen with blocking queues when the queue gets full
NOTE: message queues are persistent
* signal1.c - Shows how to set up a signal handler for the interrupt (CTRL+C) signal
* signal2.c - Shows how to set up a signal handler for the segmentation fault signal
Unfortunately does not fix the segmentation fault and essentially loops forever
* ipcsignal.c - Shows how to use the kill system call to send a signal from one process to another
* ipcshm1.c - Uses shared memory to send data from a child process to a parent process
NOTE: named shared memory segments are persistent
* ipcshm2.c - Uses shared memory to send data from a child process to a parent process
Uses shm\_unlink to remove the shared memory when done
NOTE: named shared memory segments are persistent

54
Examples/ipc/ipcfs1.c Normal file
View File

@@ -0,0 +1,54 @@
/*
* ipcsfs1 - Using the file system for inter-process communication
* Shows a problem with the fact that the file position
* pointer is shared between parent and child
*/
#include <fcntl.h> // needed for open system call
#include <stdio.h> // needed for printf
#include <stdlib.h> // needed for exit
#include <string.h> // nneded for strlen
#include <sys/types.h> // needed for pid_t
#include <sys/wait.h> // needed for wait system call
#include <unistd.h> // needed for fork, read, write, close system calls
int main()
{
// Open a file for reading and writing
// Create it if it doesn't exist
int myfile = open("MYFILE", O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
pid_t pid = fork(); // fork into 2 processes
if (pid < 0) // error
{
printf("ERROR: COULD NOT FORK\n");
exit(EXIT_FAILURE);
}
else if (pid == 0) // child
{
/* Write to parent */
char *data = "HELLO";
write(myfile, data, strlen(data));
printf("Child wrote %s\n", data);
/* Close file */
close(myfile);
return 0; // Return success
}
else // parent
{
/* Wait for child */
wait(0);
/* Read data */
char data[32];
read(myfile, data, sizeof(data));
printf("Parent received %s from child\n", data);
/* Close file */
close(myfile);
}
return 0;
}

59
Examples/ipc/ipcfs2.c Normal file
View File

@@ -0,0 +1,59 @@
/*
* ipcsfs2 - Using the file system for inter-process communication
* Fixes the file position pointer problem in ipcfs1.c
* by using lseek to reset the pointer to the beginning
* Shows the frustration with using the file system for
* IPC by needing to move the file position pointer around
*/
#include <fcntl.h> // needed for open system call
#include <stdio.h> // needed for printf
#include <stdlib.h> // needed for exit
#include <string.h> // nneded for strlen
#include <sys/types.h> // needed for pid_t
#include <sys/wait.h> // needed for wait system call
#include <unistd.h> // needed for fork, read, write, close system calls
int main()
{
// Open a file for reading and writing
// Create it if it doesn't exist
int myfile = open("MYFILE", O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
pid_t pid = fork(); // fork into 2 processes
if (pid < 0) // error
{
printf("ERROR: COULD NOT FORK\n");
exit(EXIT_FAILURE);
}
else if (pid == 0) // child
{
/* Write to parent */
char *data = "HELLO";
write(myfile, data, strlen(data));
printf("Child wrote %s\n", data);
/* Close file */
close(myfile);
return 0; // Return success
}
else // parent
{
/* Wait for child */
wait(0);
/* Reset file position pointer */
lseek(myfile, 0, SEEK_SET);
/* Read data */
char data[32];
read(myfile, data, sizeof(data));
printf("Parent received %s from child\n", data);
/* Close file */
close(myfile);
}
return 0; // Return success
}

71
Examples/ipc/ipcmsg1.c Normal file
View File

@@ -0,0 +1,71 @@
/*
* ipcmsg1.c - Using a message queue to send data from
* a parent process to a child process
* NOTE: message queues are persistent
*/
#include <sys/types.h> // needed for pid_t
#include <sys/wait.h> // needed for wait system call
#include <unistd.h> // needed for fork system call
#include <stdlib.h> // needed for exit
#include <stdio.h> // needed for printf, perror
#include <mqueue.h> // needed for mq system calls
#include <string.h> // needed for strcpy
/* Struct for the queue message */
typedef struct message {
int message_id;
char string[10];
} message;
int main()
{
// Create attributes for new queue
struct mq_attr queue_attr;
queue_attr.mq_flags = 0; // Ignored by kernel
queue_attr.mq_maxmsg = 10; // Max messages the queue supports
queue_attr.mq_msgsize = sizeof(message);
queue_attr.mq_curmsgs = 0; // Not used
// Create and open a queue
mqd_t mqdes = mq_open("/CS3841QUEUE", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR, &queue_attr);
if(mqdes == -1) {
printf("COULD NOT OPEN QUEUE\n");
exit(EXIT_FAILURE);
}
pid_t pid = fork(); // fork into 2 processes
if(pid < 0) // error
{
perror("fork");
exit(EXIT_FAILURE);
}
else if(pid == 0) // child
{
// Child receives message from parent
message from_parent;
mq_receive(mqdes, (char*)&from_parent, sizeof(message), NULL);
printf("Child got %d: %s from parent\n", from_parent.message_id, from_parent.string);
// Close the queue
mq_close(mqdes);
return 0; // Return success
}
else // parent
{
// Parent sends message to child
message to_child;
to_child.message_id = 10;
strcpy(to_child.string, "HELLO");
mq_send(mqdes, (char*)&to_child, sizeof(message), 0);
// Wait for child
wait(0);
// Close the queue
mq_close(mqdes);
}
return 0; // Return success
}

74
Examples/ipc/ipcmsg2.c Normal file
View File

@@ -0,0 +1,74 @@
/*
* ipcmsg2.c - Uses message queues to send multiple messages
* between two processes. Shows what can happen
* with blocking queues when the queue gets full
* NOTE: message queues are persistent
*/
#include <sys/types.h> // needed for pid_t
#include <sys/wait.h> // needed for wait system call
#include <unistd.h> // needed for fork system call
#include <stdlib.h> // needed for exit
#include <stdio.h> // needed for printf, perror
#include <mqueue.h> // needed for mq system calls
#include <string.h> // needed for strcpy
/* Struct for the queue message */
typedef struct message {
int message_id;
char string[10];
} message;
int main()
{
// Create attributes for new queue
struct mq_attr queue_attr;
queue_attr.mq_flags = 0; // Ignored by kernel
queue_attr.mq_maxmsg = 10; // Max messages the queue supports
queue_attr.mq_msgsize = sizeof(message);
queue_attr.mq_curmsgs = 0; // Not used
// Create and open a queue
mqd_t mqdes = mq_open("/CS3841QUEUE", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR, &queue_attr);
if(mqdes == -1) {
printf("COULD NOT OPEN QUEUE\n");
exit(EXIT_FAILURE);
}
pid_t pid = fork(); // fork into 2 processes
if(pid < 0) // error
{
perror("fork");
exit(EXIT_FAILURE);
}
else if(pid == 0) // child
{
// Child receives message from parent
message from_parent;
mq_receive(mqdes, (char*)&from_parent, sizeof(message), NULL);
printf("Child got %d: %s from parent\n", from_parent.message_id, from_parent.string);
// Close the queue
mq_close(mqdes);
return 0; // Return success
}
else // parent
{
// Parent sends messages to child
message to_child;
for(int i = 0; i < 10; i++) {
to_child.message_id = i;
strcpy(to_child.string, "HELLO");
mq_send(mqdes, (char*)&to_child, sizeof(message), 0);
}
// Wait for child
wait(0);
// Close the queue
mq_close(mqdes);
}
return 0; // Return success
}

58
Examples/ipc/ipcpipe1.c Normal file
View File

@@ -0,0 +1,58 @@
/*
* ipcpipe1.c - Using a pipe to communicate data from
* a child process to a parent process
*/
#include <sys/types.h> // needed for pid_t
#include <sys/wait.h> // needed for wait system call
#include <unistd.h> // needed for fork, read, write, close system calls
#include <stdlib.h> // needed for exit
#include <stdio.h> // needed for printf, perror
int main()
{
/* Create a pipe */
int pipefd[2];
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
pid_t pid = fork(); // fork into 2 processes
if (pid == -1) // error
{
perror("fork");
exit(EXIT_FAILURE);
}
else if (pid == 0) // child
{
/* Close unused write end */
close(pipefd[1]);
/* Read from parent */
char data[32];
read(pipefd[0], data, sizeof(data));
printf("Child received %s from parent\n", data);
/* Close pipe */
close(pipefd[0]);
return 0; // Return success
}
else // parent
{
/* Close unused read end */
close(pipefd[0]);
/* Write to child */
write(pipefd[1], "HELLO", 5);
/* Close pipe */
close(pipefd[1]);
/* Wait or child */
wait(0);
}
return 0; // Return success
}

75
Examples/ipc/ipcpipe2.c Normal file
View File

@@ -0,0 +1,75 @@
/*
* ipcpipe2.c - Pipes are unidirectional and require
* two pipes to send data in two directions
*/
#include <sys/types.h> // needed for pid_t
#include <sys/wait.h> // needed for wait system call
#include <unistd.h> // needed for fork, read, write, close system calls
#include <stdlib.h> // needed for exit
#include <stdio.h> // needed for printf, perror
int main()
{
/* Create pipes */
int pipe_to_child[2];
if (pipe(pipe_to_child) == -1) {
printf("PIPE FAILURE\n");
exit(EXIT_FAILURE);
}
int pipe_from_child[2];
if (pipe(pipe_from_child) == -1) {
printf("PIPE FAILURE\n");
exit(EXIT_FAILURE);
}
pid_t pid = fork(); // fork into 2 processes
if (pid == -1) // error
{
perror("fork");
exit(EXIT_FAILURE);
}
else if (pid == 0) //
{
/* Close unused pipe ends */
close(pipe_to_child[1]);
close(pipe_from_child[0]);
/* Write to parent */
write(pipe_from_child[1], "CHELLO", 6);
close(pipe_from_child[1]);
/* Read from parent */
char data[32];
read(pipe_to_child[0], data, sizeof(data));
printf("Child received %s from parent\n", data);
/* Close pipe */
close(pipe_to_child[0]);
return 0; // Return success
}
else // parent
{
/* Close unused pipe ends */
close(pipe_to_child[0]);
close(pipe_from_child[1]);
/* Write to child */
write(pipe_to_child[1], "PHELLO", 6);
close(pipe_to_child[1]);
/* Read from child */
char data[32];
read(pipe_from_child[0], data, sizeof(data));
printf("Parent recieved %s from child\n", data);
/* Close pipe */
close(pipe_from_child[0]);
/* Wait for child */
wait(0);
}
return 0; // Return success
}

74
Examples/ipc/ipcshm1.c Normal file
View File

@@ -0,0 +1,74 @@
/*
* ipcshm1.c - Uses shared memory to send data from
* a child process to a parent process
* NOTE: named shared memory segments are persistent
*/
#include <sys/types.h> // needed for pid_t
#include <sys/wait.h> // needed for wait system call
#include <fcntl.h> // needed for parameter values for shm_open
#include <unistd.h> // needed for fork, getpid, getppid, kill system calls
#include <stdlib.h> // needed for exit
#include <signal.h> // needed for signal system call
#include <stdio.h> // needed for printf, perror
#include <sys/mman.h> // needed for mmap, munmap, shm system calls
#include <string.h> // needed for strcpy
#define MAPPED_SIZE 128
int main()
{
// Create and open a shared memory segment
int shmfd = shm_open("/CS3841MEMORY", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if(shmfd == -1) {
printf("COULD NOT OPEN SHARED MEMORY SEGMENT\n");
exit(EXIT_FAILURE);
}
// Set the size of the shared memory segment
ftruncate(shmfd, MAPPED_SIZE);
// Map the segment into the processes address space
// NOTE: protection is set to allow reading and writing with a shared mapping
void* mapped_space = mmap(NULL, MAPPED_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shmfd, 0);
if(mapped_space == MAP_FAILED) {
printf("COULD NOT MMAP\n");
exit(EXIT_FAILURE);
}
pid_t pid = fork(); // fork into 2 processes
if(pid < 0) // error
{
perror("fork");
exit(EXIT_FAILURE);
}
else if(pid == 0) // child
{
// Child writes to shared memory segment
strcpy(mapped_space, "HELLO");
// Unmap the shared memory
munmap(mapped_space, MAPPED_SIZE);
// Close the shared memory segment
close(shmfd);
return 0; // Return success
}
else // parent
{
// Wait for child to finish
wait(0);
// Parent reads from shared memory segment
printf("Parent reads %s from shared mapped segment\n", (char*)mapped_space);
// Unmap the shared memory
munmap(mapped_space, MAPPED_SIZE);
// Close the shared memory segment
close(shmfd);
}
return 0; // Return success
}

79
Examples/ipc/ipcshm2.c Normal file
View File

@@ -0,0 +1,79 @@
/*
* ipcshm2.c - Uses shared memory to send data from
* a child process to a parent process
* Uses shm_unlink to remove the shared memory when done
* NOTE: named shared memory segments are persistent
*/
#include <sys/types.h> // needed for pid_t
#include <sys/wait.h> // needed for wait system call
#include <fcntl.h> // needed for parameter values for shm_open
#include <unistd.h> // needed for fork, getpid, getppid, kill system calls
#include <stdlib.h> // needed for exit
#include <signal.h> // needed for signal system call
#include <stdio.h> // needed for printf, perror
#include <sys/mman.h> // needed for mmap, munmap, shm system calls
#include <string.h> // needed for strcpy
#define MAPPED_SIZE 128
int main()
{
// Create and open a shared memory segment
int shmfd = shm_open("/CS3841MEMORY", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if(shmfd == -1) {
printf("COULD NOT OPEN SHARED MEMORY SEGMENT\n");
exit(EXIT_FAILURE);
}
// Set the size of the shared memory segment
ftruncate(shmfd, MAPPED_SIZE);
// Map the segment into the processes address space
// NOTE: protection is set to allow reading and writing with a shared mapping
void* mapped_space = mmap(NULL, MAPPED_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shmfd, 0);
if(mapped_space == MAP_FAILED) {
printf("COULD NOT MMAP\n");
exit(EXIT_FAILURE);
}
pid_t pid = fork(); // fork into 2 processes
if(pid < 0) // error
{
perror("fork");
exit(EXIT_FAILURE);
}
else if(pid == 0) // child
{
// Child writes to shared memory segment
strcpy(mapped_space, "HELLO");
// Unmap the shared memory
munmap(mapped_space, MAPPED_SIZE);
// Close the shared memory segment
close(shmfd);
return 0; // Return success
}
else // parent
{
// Wait for child to finish
wait(0);
// Parent reads from shared memory segment
printf("Parent reads %s from shared mapped segment\n", (char*)mapped_space);
// Unmap the shared memory
munmap(mapped_space, MAPPED_SIZE);
// Close the shared memory segment
close(shmfd);
// Unlink the shared memory
shm_unlink("/CS3841MEMORY");
}
return 0; // Return success
}

46
Examples/ipc/ipcsignal.c Normal file
View File

@@ -0,0 +1,46 @@
/*
* ipcsignal.c - Shows how to use the kill system call
* to send a signal from one process to another
*/
#include <sys/types.h> // needed for pid_t
#include <sys/wait.h> // needed for wait system call
#include <unistd.h> // needed for fork, getpid, getppid, kill system calls
#include <stdlib.h> // needed for exit
#include <signal.h> // needed for signal system call
#include <stdio.h> // needed for printf, perror
void signal_handler(int sig)
{
pid_t pid = getpid();
printf("Process %d received signal %d\n", pid, sig);
}
int main()
{
// Set a signal handler for a user defined signal number 1
signal(SIGUSR1, signal_handler);
pid_t pid = fork(); // fork into 2 processes
if(pid < 0) // error
{
perror("fork");
exit(EXIT_FAILURE);
}
else if(pid == 0) // child
{
// Send a signal to the parent by its PID
pid_t parent_pid = getppid();
printf("Sending SIGUSR1(%d) to %d\n", SIGUSR1, parent_pid);
kill(parent_pid, SIGUSR1);
return 0; // Return success
}
else // parent
{
// Wait for child
wait(0);
}
return 0; // Return success
}

29
Examples/ipc/signal1.c Normal file
View File

@@ -0,0 +1,29 @@
/*
* signal1.c - Shows how to set up a signal handler
* for the interrupt (CTRL+C) signal
*/
#include <sys/types.h> // needed for pid_t
#include <unistd.h> // needed for getpid, sleep system calls
#include <signal.h> // needed for signal system call
#include <stdio.h> // needed for printf
// Signal handler
// Prints the PID of the process and the received signal
void signal_handler(int sig)
{
pid_t pid = getpid();
printf("Process %d received signal %d\n", pid, sig);
}
int main()
{
// Set a signal handler for the interrupt signal
signal(SIGINT, signal_handler);
// Loop forever and sleep
while(1) {
printf("sleeping...\n");
sleep(1);
}
}

29
Examples/ipc/signal2.c Normal file
View File

@@ -0,0 +1,29 @@
/*
* signal2.c - Shows how to set up a signal handler
* for the segmentation fault signal
* Unfortunately does not fix the segmentation
* fault and essentially loops forever
*/
#include <sys/types.h> // needed for pid_t
#include <unistd.h> // needed for getpid, sleep system calls
#include <signal.h> // needed for signal system call
#include <stdio.h> // needed for printf
// Signal handler
// Prints the PID of the process and the received signal
void signal_handler(int sig)
{
pid_t pid = getpid();
printf("Process %d received signal %d\n", pid, sig);
}
int main()
{
// Set up a signal handler for the segmentation fault signal
signal(SIGSEGV, signal_handler);
// Cause a segmentation fault
int* i = NULL;
return *i + 10;
}