diff --git a/Examples/a.out b/Examples/a.out deleted file mode 100755 index 42b3d69..0000000 Binary files a/Examples/a.out and /dev/null differ diff --git a/Examples/ipc/Makefile b/Examples/ipc/Makefile new file mode 100644 index 0000000..c061232 --- /dev/null +++ b/Examples/ipc/Makefile @@ -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) diff --git a/Examples/ipc/README.md b/Examples/ipc/README.md new file mode 100644 index 0000000..773ddcd --- /dev/null +++ b/Examples/ipc/README.md @@ -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 diff --git a/Examples/ipc/ipcfs1.c b/Examples/ipc/ipcfs1.c new file mode 100644 index 0000000..bc53321 --- /dev/null +++ b/Examples/ipc/ipcfs1.c @@ -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 // needed for open system call +#include // needed for printf +#include // needed for exit +#include // nneded for strlen +#include // needed for pid_t +#include // needed for wait system call +#include // 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; +} diff --git a/Examples/ipc/ipcfs2.c b/Examples/ipc/ipcfs2.c new file mode 100644 index 0000000..534f9ce --- /dev/null +++ b/Examples/ipc/ipcfs2.c @@ -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 // needed for open system call +#include // needed for printf +#include // needed for exit +#include // nneded for strlen +#include // needed for pid_t +#include // needed for wait system call +#include // 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 +} diff --git a/Examples/ipc/ipcmsg1.c b/Examples/ipc/ipcmsg1.c new file mode 100644 index 0000000..b3c918d --- /dev/null +++ b/Examples/ipc/ipcmsg1.c @@ -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 // needed for pid_t +#include // needed for wait system call +#include // needed for fork system call +#include // needed for exit +#include // needed for printf, perror +#include // needed for mq system calls +#include // 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 +} diff --git a/Examples/ipc/ipcmsg2.c b/Examples/ipc/ipcmsg2.c new file mode 100644 index 0000000..786793c --- /dev/null +++ b/Examples/ipc/ipcmsg2.c @@ -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 // needed for pid_t +#include // needed for wait system call +#include // needed for fork system call +#include // needed for exit +#include // needed for printf, perror +#include // needed for mq system calls +#include // 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 +} diff --git a/Examples/ipc/ipcpipe1.c b/Examples/ipc/ipcpipe1.c new file mode 100644 index 0000000..ae3c814 --- /dev/null +++ b/Examples/ipc/ipcpipe1.c @@ -0,0 +1,58 @@ +/* + * ipcpipe1.c - Using a pipe to communicate data from + * a child process to a parent process + */ + +#include // needed for pid_t +#include // needed for wait system call +#include // needed for fork, read, write, close system calls +#include // needed for exit +#include // 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 +} diff --git a/Examples/ipc/ipcpipe2.c b/Examples/ipc/ipcpipe2.c new file mode 100644 index 0000000..a67c588 --- /dev/null +++ b/Examples/ipc/ipcpipe2.c @@ -0,0 +1,75 @@ +/* + * ipcpipe2.c - Pipes are unidirectional and require + * two pipes to send data in two directions + */ + +#include // needed for pid_t +#include // needed for wait system call +#include // needed for fork, read, write, close system calls +#include // needed for exit +#include // 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 +} diff --git a/Examples/ipc/ipcshm1.c b/Examples/ipc/ipcshm1.c new file mode 100644 index 0000000..53b1ddd --- /dev/null +++ b/Examples/ipc/ipcshm1.c @@ -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 // needed for pid_t +#include // needed for wait system call +#include // needed for parameter values for shm_open +#include // needed for fork, getpid, getppid, kill system calls +#include // needed for exit +#include // needed for signal system call +#include // needed for printf, perror +#include // needed for mmap, munmap, shm system calls +#include // 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 +} diff --git a/Examples/ipc/ipcshm2.c b/Examples/ipc/ipcshm2.c new file mode 100644 index 0000000..67ca5c7 --- /dev/null +++ b/Examples/ipc/ipcshm2.c @@ -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 // needed for pid_t +#include // needed for wait system call +#include // needed for parameter values for shm_open +#include // needed for fork, getpid, getppid, kill system calls +#include // needed for exit +#include // needed for signal system call +#include // needed for printf, perror +#include // needed for mmap, munmap, shm system calls +#include // 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 +} + diff --git a/Examples/ipc/ipcsignal.c b/Examples/ipc/ipcsignal.c new file mode 100644 index 0000000..803834a --- /dev/null +++ b/Examples/ipc/ipcsignal.c @@ -0,0 +1,46 @@ +/* + * ipcsignal.c - Shows how to use the kill system call + * to send a signal from one process to another + */ + +#include // needed for pid_t +#include // needed for wait system call +#include // needed for fork, getpid, getppid, kill system calls +#include // needed for exit +#include // needed for signal system call +#include // 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 +} diff --git a/Examples/ipc/signal1.c b/Examples/ipc/signal1.c new file mode 100644 index 0000000..ed57a5c --- /dev/null +++ b/Examples/ipc/signal1.c @@ -0,0 +1,29 @@ +/* + * signal1.c - Shows how to set up a signal handler + * for the interrupt (CTRL+C) signal + */ + +#include // needed for pid_t +#include // needed for getpid, sleep system calls +#include // needed for signal system call +#include // 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); + } +} diff --git a/Examples/ipc/signal2.c b/Examples/ipc/signal2.c new file mode 100644 index 0000000..709e7fc --- /dev/null +++ b/Examples/ipc/signal2.c @@ -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 // needed for pid_t +#include // needed for getpid, sleep system calls +#include // needed for signal system call +#include // 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; +}