How to create shared memory after fork? - c

My code is
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdio.h>
int main() {
char parent[] = "parent";
char child[] = "child";
char *shmem = (char*)mmap(NULL, 1000, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
char *shmem_child = "NOT CHANGE";
memcpy(shmem, parent, sizeof(parent));
int pid = fork();
if (pid == 0) {
char child_new[] = "new child";
printf("Child read: %s\n", shmem);
memcpy(shmem, child, sizeof(child));
printf("Child wrote: %s\n", shmem);
shmem_child = (char*)mmap(NULL, 1000, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
memcpy(shmem_child, child_new, sizeof(child_new));
printf("Child create: %s\n", shmem_child);
} else {
printf("Parent read: %s\n", shmem);
sleep(1);
printf("After 1s, parent read: %s\n", shmem);
printf("After 1s, parent read shmem_child: %s\n", shmem_child);
}
}
And the output is
Parent read: parent
Child read: parent
Child wrote: child
Child create: new child
After 1s, parent read: child
After 1s, parent read shmem_child: NOT CHANGE
As you can see, the shared memory(shmem) created before fork works, but the shared memory(shmem_child) created inside child does not work.
Am I doing something wrong? How can I create shared memory inside child so that parent and even brothers(other children of same parent) can access?

Anonymous shared memory stays shared across a fork().
So, both the parent and the child(ren) should access the same memory area, shmem.
You cannot create anonymous shared memory in a child process, and have it magically appear in the parent process. Anonymous shared memory must be created in the parent; then it will be accessible to all children.
You can create non-anonymous shared memory, via e.g. shm_open(). The creator ftruncate()s it to appropriate length, and all processes memory-map the descriptor. You do need to remember to remove the shared memory when no longer needed, via shm_unlink().
Here is a simple (tested and verified) example of anonymous shared memory between a parent and a child:
#define _POSIX_C_SOURCE 200809L
#define _GNU_SOURCE
/* SPDX-License-Identifier: CC0-1.0 */
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
typedef struct {
char message[256];
} shared_mem;
static size_t page_aligned(const size_t size)
{
const size_t page = sysconf(_SC_PAGESIZE);
if (size <= page)
return page;
else
return page * (size_t)(size / page + !!(size % page));
/* !!(size % page) is 0 if size is a multiple of page, 1 otherwise. */
}
int child_process(shared_mem *shared)
{
printf("Child: shared memory contains \"%s\".\n", shared->message);
fflush(stdout);
snprintf(shared->message, sizeof shared->message, "Child");
printf("Child: changed shared memory to \"%s\".\n", shared->message);
fflush(stdout);
return EXIT_SUCCESS;
}
int main(void)
{
const size_t size = page_aligned(sizeof (shared_mem));
shared_mem *shared;
pid_t child, p;
shared = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_SHARED, -1, (off_t)0);
if (shared == MAP_FAILED) {
fprintf(stderr, "Cannot map %zu bytes of shared memory: %s.\n", size, strerror(errno));
return EXIT_FAILURE;
}
snprintf(shared->message, sizeof shared->message, "Parent");
printf("Parent: set shared memory to \"%s\".\n", shared->message);
fflush(stdout);
/* Create the child process. */
child = fork();
if (!child) {
/* This is the child process. */
return child_process(shared);
} else
if (child == -1) {
fprintf(stderr, "Cannot create a child process: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
/* This is the parent process. */
/* Wait for the child to exit. */
do {
p = waitpid(child, NULL, 0);
} while (p == -1 && errno == EINTR);
if (p == -1) {
fprintf(stderr, "Cannot reap child process: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
/* Describe the shared memory, */
printf("Parent: shared memory contains \"%s\".\n", shared->message);
fflush(stdout);
/* and tear it down. Done. */
munmap(shared, size);
return EXIT_SUCCESS;
}
Save it as e.g. example.c, then compile and run it via e.g.
gcc -Wall -Wextra -O2 example1.c -o ex1
./ex1
It will output
Parent: set shared memory to "Parent".
Child: shared memory contains "Parent".
Child: changed shared memory to "Child".
Parent: shared memory contains "Child".
showing that this indeed works.
To create shared memory after fork(), or between unrelated processes, all processes have to agree on the name. For POSIX shared memory objects (that you obtain a descriptor to using shm_open(), the name must start with a slash.
Note that I used mode 0600, which corresponds to (decimal 384) -rw-------, i.e. only accessible to processes running as the same user.
Consider the following example:
#define _POSIX_C_SOURCE 200809L
#define _GNU_SOURCE
/* SPDX-License-Identifier: CC0-1.0 */
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
typedef struct {
pid_t changer;
time_t when;
char message[256];
} shared_mem;
static size_t page_aligned(const size_t size)
{
const size_t page = sysconf(_SC_PAGESIZE);
if (size <= page)
return page;
else
return page * (size_t)(size / page + !!(size % page));
/* !!(size % page) is 0 if size is a multiple of page, 1 otherwise. */
}
enum {
ACTION_NONE = 0,
ACTION_CREATE = (1<<0),
ACTION_REMOVE = (1<<1),
ACTION_MODIFY = (1<<2),
};
int main(int argc, char *argv[])
{
const size_t size = page_aligned(sizeof (shared_mem));
shared_mem *shared;
const char *name;
time_t now;
const char *message = NULL;
int action = ACTION_NONE;
int arg, shm_fd;
if (argc < 2 || argc > 4 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
const char *argv0 = (argc > 0 && argv && argv[0]) ? argv[0] : "(this)";
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv0);
fprintf(stderr, " %s /NAME [ +CREATE ] [ MESSAGE ] [ -REMOVE ]\n", argv0);
fprintf(stderr, "\n");
return EXIT_FAILURE;
}
/* Grab and check name */
name = argv[1];
if (name[0] != '/' || name[1] == '\0') {
fprintf(stderr, "%s: Shared memory name must begin with a slash.\n", argv[1]);
return EXIT_FAILURE;
}
/* Check other command-line parameters. */
for (arg = 2; arg < argc; arg++) {
if (argv[arg][0] == '+') {
action |= ACTION_CREATE;
if (argv[arg][1] != '\0') {
message = argv[arg] + 1;
action |= ACTION_MODIFY;
}
} else
if (argv[arg][0] == '-') {
action |= ACTION_REMOVE;
if (argv[arg][1] != '\0') {
message = argv[arg] + 1;
action |= ACTION_MODIFY;
}
} else
if (argv[arg][0] != '\0') {
if (message) {
fprintf(stderr, "%s: Can only set one message (already setting '%s').\n", argv[arg], message);
return EXIT_FAILURE;
}
message = argv[arg];
action |= ACTION_MODIFY;
}
}
if (action & ACTION_CREATE) {
/* Create the shared memory object. */
shm_fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
if (shm_fd == -1) {
fprintf(stderr, "%s: Cannot create shared memory object: %s.\n", name, strerror(errno));
return EXIT_FAILURE;
}
/* Resize it to desired size. */
if (ftruncate(shm_fd, (off_t)size) == -1) {
fprintf(stderr, "%s: Cannot resize shared memory object to %zu bytes: %s.\n", name, size, strerror(errno));
close(shm_fd);
shm_unlink(name);
return EXIT_FAILURE;
}
} else {
/* Open an existing shared memory object. */
shm_fd = shm_open(name, O_RDWR, 0600);
if (shm_fd == -1) {
fprintf(stderr, "%s: Cannot open shared memory object: %s.\n", name, strerror(errno));
return EXIT_FAILURE;
}
}
/* Map the shared memory object. */
shared = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_NORESERVE, shm_fd, (off_t)0);
if (shared == MAP_FAILED) {
fprintf(stderr, "%s: Cannot map %zu bytes of shared memory object: %s.\n", name, size, strerror(errno));
close(shm_fd);
if (action & (ACTION_CREATE | ACTION_REMOVE))
shm_unlink(name);
return EXIT_FAILURE;
}
/* The shared memory object descriptor is no longer needed. */
if (close(shm_fd) == -1) {
fprintf(stderr, "Warning: Error closing shared memory object: %s.\n", strerror(errno));
}
/* Current time in UTC */
now = time(NULL);
/* If we created it, we need to initialize it too. */
if (action & ACTION_CREATE) {
shared->changer = getpid();
shared->when = now;
snprintf(shared->message, sizeof shared->message, "Initialized");
}
/* Show contents. */
printf("Shared memory was last changed %ld seconds ago by process %ld to '%s'.\n",
(long)(now - shared->when), (long)(shared->changer), shared->message);
fflush(stdout);
/* Modify contents. */
if (action & ACTION_MODIFY) {
printf("Changing shared memory contents into '%s'.\n", message);
fflush(stdout);
shared->changer = getpid();
shared->when = now;
snprintf(shared->message, sizeof shared->message, "%s", message);
}
/* Unmap shared memory object. */
munmap(shared, size);
/* Remove shared memory? */
if (action & ACTION_REMOVE) {
if (shm_unlink(name) == -1) {
fprintf(stderr, "Warning: %s: Cannot remove shared memory object: %s.\n", name, strerror(errno));
return EXIT_FAILURE;
} else {
printf("%s: Shared memory object removed successfully.\n", name);
fflush(stdout);
}
}
/* All done. */
return EXIT_SUCCESS;
}
Save it as e.g. example2.c, and compile it using e.g.
gcc -Wall -Wextra -O2 example2.c -lrt -o ex2
Open up multiple windows. In one, run
./ex2 /myshared +
to create the shared memory; and in others, run
./ex2 /myshared newmessage
When you are done, remember to remove the shared memory object using
./ex2 /myshared -

Related

Permission denied issue in mmap function in shared memory in C

In the child process the mapping is giving me issue. the parent is creating a shared memory and child is using it for writing a message to parent but when the mmap executes, it gives an error of permission denied. kindly help me to get rid of this issue. everything else is running file. when I try to create shared memory in child process the issue is resolved but the requirement is creating shared memory in parent process, that is why it is taking time to solve it.
/**
* Simple program demonstrating shared memory in POSIX systems.
*
* This is the consumer process
*
* Figure 3.18
*
* To compile, enter
* gcc shm-posix-consumer.c -lrt
*
* #author Gagne, Galvin, Silberschatz
* Operating System Concepts - Tenth Edition
* Copyright John Wiley & Sons - 2018
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/mman.h>
int main()
{
const int SIZE = 4096;
const char *name = "OS";
//shm_unlink(name);
int shm_fd;
void *ptr;
pid_t pid;
pid=fork();
if(pid==-1) {
printf("Error in creating a child process\n");
return -1;
}
else if(pid==0) {
sleep(2);
printf("child process is executing\n");
/* open the shared memory segment */
shm_fd = shm_open(name, O_RDWR, S_IRWXU);
if (shm_fd == -1) {
printf("opening of shared memory failed\n");
exit(-1);
}
if(ftruncate(shm_fd,SIZE)==-1){
printf("Error in configuring the size of shared memory");
exit(1);
}
/* now map the shared memory segment in the address space of the process */
ptr = mmap(0,SIZE,PROT_WRITE, MAP_SHARED, shm_fd, 0);
if (ptr == MAP_FAILED) {
perror("Map failed in child process\n");
printf("error:%s %d",strerror(errno),errno);
return -1;
}
char *message0;
strcpy(message0,"Greeting to parent");
printf("Message to parent from child:%s\n",message0);
sprintf(ptr,"%s",message0);
ptr += strlen(message0);
if(munmap(ptr,SIZE)==-1){
printf("munmap error:\n");
exit(1);
}
close(shm_fd);
}
else {
printf("parent process is executing\n");
/* create the shared memory segment */
shm_fd = shm_open(name, O_CREAT | O_RDWR, S_IRWXU);
/* configure the size of the shared memory segment */
if(ftruncate(shm_fd,SIZE)==-1){
printf("Error in configuring the size of shared memory");
exit(1);
}
printf("parent process wait after creating shared memory\n");
wait(NULL);
printf("back to parent process after child termination\n");
/* now map the shared memory segment in the address space of the process */
ptr = mmap(0,SIZE, PROT_READ, MAP_SHARED, shm_fd, 0);
if (ptr == MAP_FAILED) {
printf("Map failed in parent\n");
return -1;
}
printf("%s\n",(char *)ptr);
if (shm_unlink(name) == -1) {
printf("Error removing %s\n",name);
exit(-1);
}
}
return 0;
}
At least three problems:
char *message0;
strcpy(message0,"Greeting to parent");
You are using message0 uninitialized, switch to something like
char message0[64];
strcpy(message0,"Greeting to parent");
or simply:
char *message0 = "Greeting to parent";
Here:
void *ptr;
...
ptr += strlen(message0);
You can't use pointer arithmetic with void *, switch to char *ptr;
And here:
ptr += strlen(message0);
if(munmap(ptr,SIZE)==-1){
you need to rewind ptr to the original position returned by mmap before calling munmap, it seems that you can remove this line:
ptr += strlen(message0);
Now it works for me adding #define _XOPEN_SOURCE 500 at the very beginning (for ftruncate).

IPC through shared memory and pipe gives segmentation fault: 11 in C

I am trying to share a file between parent and child processes. Parent sends the file via a pipe and child write that lines into shared memory so that parent can read and print out the file via shared memory. However, I am getting segmentation fault: 11. Besides, I did something similar like the code below, but that time I could not get the correct content and even I was getting different results at each call.
I am not sure about increasing the pointer part. But, it is better to have an extra eye on the code.
Edit: I corrected char* to char[] and segmentation fault is now gone. However, I get different results at each run, some extra characters are seen in output.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#define SHM_NAME_1 "Child 1"
int main(){
pid_t pid;
FILE *file;
char *infile = "in.txt";
pid = fork();
if(pid < 0){
fprintf(stderr, "Fork failed\n");
return 1;
}
if(pid > 0){ // parent
file = fopen(infile, "r");
if(file == 0){
fprintf(stderr, "File failed\n");
return 1;
}
// close read end of pipe
mknod("FIFO", S_IFIFO | 0666, 0);
int fd = open("FIFO", O_WRONLY);
char str[300];
while(fgets(str, sizeof(str), file) > 0)
{
// write all lines of file
write(fd, str, strlen(str));
}
// close file and pipe
close(fd);
fclose(file);
// wait for child to write to shared memory
wait(NULL);
// open shared segment
int shm_first = shm_open(SHM_NAME_1, O_RDONLY, 0666);
if (shm_first == -1) {
fprintf(stderr, "Failed: Shared Memory 1");
exit(-1);
}
// create memory pointer
void *ptr = mmap(0,4096, PROT_READ, MAP_SHARED, shm_first, 0);
if (ptr == MAP_FAILED) {
printf("Map failed 1\n");
return -1;
}
// print out result and unlibk shared segment
fprintf(stdout, "Normal input: \n%s\n", ptr);
shm_unlink(SHM_NAME_1);
} else { // child
// create the shared segment for the first time
int shm_child_1 = shm_open(SHM_NAME_1, O_CREAT | O_RDWR, 0666);
// configure the size of the shared memory segment
ftruncate(shm_child_1,4096);
// map the pointer to the segment
void *ptr_child_1 = mmap(0,4096, PROT_READ | PROT_WRITE, MAP_SHARED, shm_child_1, 0);
if (ptr_child_1 == MAP_FAILED)
{
printf("Map failed in first child\n");
return -1;
}
mknod("FIFO", S_IFIFO | 0666, 0);
int fd = open("FIFO", O_RDONLY);
int num;
char s[300];
while((num = read(fd, s, sizeof(s)))> 0)
{
sprintf(ptr_child_1, "%s", s);
ptr_child_1 += num;
}
close(fd);
exit(0);
}
return 0;
}
One quick observation.
In the following code, you have a char pointer that is not initialized to point to anything. Which causes fgets to copy what it reads from file to some arbitrary place in memory.
char *str;
while(fgets(str, 100, file) > 0)
Now that the buffer issues are resolved, there is also an issue with the braces in the following expression
while((num = read(fd, s, sizeof(s)) > 0))
num is going to be 1 or 0 rather than the number of bytes read or 0 for eof. This should be
while((num = read(fd, s, sizeof(s))) > 0)
Once you have the number of bytes read, you need to zero terminate the buffer. Because you are using sprintf which expects the argument for %s to be a zero terminated string.
while((num = read(fd, s, sizeof(s)))> 0)
{
s[num] = '\0'; // Terminate the string to the number of bytes read
sprintf(ptr_child_1, "%s", s);
ptr_child_1 += num;
}

Fibonacci shared memory processes between two c files

Hello I have a single file in c that shares memory from the parent to child but I need my code separated into two separate files while still sharing the memory. I need the parent to create the shared memory and get the input of the fib number. Then the child process opens the share memory object to read the value n and overwrite the value n by the value of fib(n). and displays the fib series. This is what I have now
#include <stdlib.h>
#include <stdio.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>
// So we could use other sizes without editing the source.
#ifndef MAX_SEQUENCE
# define MAX_SEQUENCE 10
#endif
// Check that MAX_SEQUENCE is large enough!
#if MAX_SEQUENCE < 2
#error MAX_SEQUENCE must be at least 2
#endif
typedef struct{
long fib_sequence[MAX_SEQUENCE];
int sequence_size;
} shared_data;
int main()
{
int a, b, m, n, i;
a = 0; b = 1;
printf("Enter the number of a Fibonacci Sequence:\n");
// Always check whether input conversion worked
if (scanf("%d", &m) != 1) {
printf("Invalid input, couldn't be converted.\n");
return EXIT_FAILURE;
}
if (m <= 0) {
printf("Please enter a positive integer\n");
return EXIT_FAILURE; // exit if input is invalid
} else if (m > MAX_SEQUENCE) {
printf("Please enter an integer less than %d\n", MAX_SEQUENCE);
return EXIT_FAILURE; // exit if input is invalid
}
/* the identifier for the shared memory segment */
int segment_id;
/* the size (in bytes) of the shared memory segment */
size_t segment_size = sizeof(shared_data);
/* allocate a shared memory segment */
segment_id = shmget(IPC_PRIVATE, segment_size, S_IRUSR | S_IWUSR);
// Check result of shmget
if (segment_id == -1) {
perror("shmget failed");
return EXIT_FAILURE;
}
/* attach the shared memory segment */
shared_data *shared_memory = shmat(segment_id, NULL, 0);
// Check whether attaching succeeded
if ((void*)shared_memory == (void*)-1) {
perror("shmat failed");
goto destroy; // clean up
}
printf("\nshared memory segment %d attached at address %p\n", segment_id, (void*)shared_memory);
shared_memory->sequence_size = m;
pid_t pid;
pid = fork();
if (pid == 0){
printf("Child is producing the Fibonacci Sequence...\n");
shared_memory->fib_sequence[0] = a;
shared_memory->fib_sequence[1] = b;
for (i = 2; i < shared_memory->sequence_size; i++){
n = a+b;
shared_memory->fib_sequence[i] = n;
a = b;
b = n;
}
printf("\nChild ends\n");
}
else{
printf("Parent is waiting for child to complete...\n");
wait(NULL);
printf("Parent ends\n");
for(i = 0; i < shared_memory->sequence_size; i++) {
printf("%ld ", shared_memory->fib_sequence[i]);
}
printf("\n");
}
/* now detach the shared memory segment */
if (shmdt(shared_memory) == -1) {
fprintf(stderr, "Unable to detach\n");
}
destroy:
/* now remove the shared memory segment */
shmctl(segment_id, IPC_RMID, NULL);
return 0;
}
There are a couple of options for shared memories :
Datapools - Datapool is an allocated location that kernel provides to a process upon request. Then other processes use the name which was used to create the data pool to connect to it and read/write from it.
Pipelines - Pipeline is another form of sharing resources which again kernel provides upon request. The difference is pipeline is usually one-way whereas data pool can be read and written by all the processes. Also, reads from the pipeline are destructive.
Files - You can also use files which is the most basic and you are probably familiar with it.
These are basic explanations, you have to research on these topics to fully understand and use them. Also, each operating system has a specific way of using these concepts, but all of them provide it (in their own way).
Instead of attaching the shared memory in the parent and then inheriting it in the client, use ftok() to get a common shared memory key that's used by both processes.
Create a file fibonacci in your current directory, this will be used in the calls to ftok().
When the parent forks the child process, it calls execl() to execute the child program, rather than including the child code directly. The child program doesn't need any of the fork() code, it just needs to attach to the same shared memory segment and fill in the results.
fibonacci.h
#ifndef FIBONACCI_H
#define FIBONACCI_H
// So we could use other sizes without editing the source.
#ifndef MAX_SEQUENCE
# define MAX_SEQUENCE 10
#endif
// Check that MAX_SEQUENCE is large enough!
#if MAX_SEQUENCE < 2
#error MAX_SEQUENCE must be at least 2
#endif
#define TOKEN_PATH "fibonacci"
typedef struct{
long fib_sequence[MAX_SEQUENCE];
int sequence_size;
} shared_data;
#endif
testparent.c
#include <stdlib.h>
#include <stdio.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>
#include "fibonacci.h"
int main()
{
int m, i;
printf("Enter the number of a Fibonacci Sequence:\n");
// Always check whether input conversion worked
if (scanf("%d", &m) != 1) {
printf("Invalid input, couldn't be converted.\n");
return EXIT_FAILURE;
}
if (m <= 0) {
printf("Please enter a positive integer\n");
return EXIT_FAILURE; // exit if input is invalid
} else if (m > MAX_SEQUENCE) {
printf("Please enter an integer less than %d\n", MAX_SEQUENCE);
return EXIT_FAILURE; // exit if input is invalid
}
/* the identifier for the shared memory segment */
int segment_id;
/* the size (in bytes) of the shared memory segment */
size_t segment_size = sizeof(shared_data);
/* Get shared memory token */
key_t token = ftok(TOKEN_PATH, 0);
if (token == -1) {
perror("ftok");
return EXIT_FAILURE;
}
/* allocate a shared memory segment */
segment_id = shmget(token, segment_size, S_IRUSR | S_IWUSR | IPC_CREAT);
// Check result of shmget
if (segment_id == -1) {
perror("shmget failed");
return EXIT_FAILURE;
}
/* attach the shared memory segment */
shared_data *shared_memory = shmat(segment_id, NULL, 0);
// Check whether attaching succeeded
if ((void*)shared_memory == (void*)-1) {
perror("shmat failed");
goto destroy; // clean up
}
printf("\nshared memory segment %d attached at address %p\n", segment_id, (void*)shared_memory);
shared_memory->sequence_size = m;
pid_t pid;
pid = fork();
if (pid == 0){
execl("./testchild", "./testchild", (char *)NULL);
perror("execl"); // If it returns it must have failed
return EXIT_FAILURE;
}
else{
printf("Parent is waiting for child to complete...\n");
wait(NULL);
printf("Parent ends\n");
for(i = 0; i < shared_memory->sequence_size; i++) {
printf("%ld ", shared_memory->fib_sequence[i]);
}
printf("\n");
}
/* now detach the shared memory segment */
if (shmdt(shared_memory) == -1) {
fprintf(stderr, "Unable to detach\n");
}
destroy:
/* now remove the shared memory segment */
shmctl(segment_id, IPC_RMID, NULL);
return 0;
}
testchild.c
#include <stdlib.h>
#include <stdio.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>
#include "fibonacci.h"
int main()
{
int a, b, n, i;
a = 0; b = 1;
/* the identifier for the shared memory segment */
int segment_id;
/* the size (in bytes) of the shared memory segment */
size_t segment_size = sizeof(shared_data);
/* Get shared memory token */
key_t token = ftok(TOKEN_PATH, 0);
if (token == -1) {
perror("ftok");
return EXIT_FAILURE;
}
/* allocate a shared memory segment */
segment_id = shmget(token, segment_size, S_IRUSR | S_IWUSR);
// Check result of shmget
if (segment_id == -1) {
perror("shmget failed");
return EXIT_FAILURE;
}
/* attach the shared memory segment */
shared_data *shared_memory = shmat(segment_id, NULL, 0);
// Check whether attaching succeeded
if ((void*)shared_memory == (void*)-1) {
perror("shmat failed");
return EXIT_FAILURE;
}
printf("\nshared memory segment %d attached at address %p\n", segment_id, (void*)shared_memory);
printf("Child is producing the Fibonacci Sequence...\n");
shared_memory->fib_sequence[0] = a;
shared_memory->fib_sequence[1] = b;
for (i = 2; i < shared_memory->sequence_size; i++){
n = a+b;
shared_memory->fib_sequence[i] = n;
a = b;
b = n;
}
printf("\nChild ends\n");
/* now detach the shared memory segment */
if (shmdt(shared_memory) == -1) {
fprintf(stderr, "Unable to detach\n");
}
return 0;
}

C Using pipes to transfer data to write to file using shared memory

I am trying to use pipes in C. I have two create two pipes between parent and child process.I have to read a file in chunks of 4096 bytes (or smaller if there is less) and I have to send through the pipes the amount of data that was read and how many times there have been readings. For example, to copy a 6KB
file, the parent writes the first 4KB data of the file to the shared memory and send two integers, 1 and 4096, to the child via the pipe. The child receives these two numbers, copies 4096 bytes from the shared memory to the output file, and sends back 1 to the parent via the other pipe. After receiving 1,
the parent copies the left 2KB data to the shared memory and send 2 and 2048 to the child. The child receives them from the pipe, copies 2048 bytes to the output file, and replies with 2 to the parent. The parent then send 0, 0 to the child. The child receives 0 and replies with a 0 and then exit. The parent
receives 0 and exits too.
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define SIZE 4096
#define NUM_OF_PIPES 2
#define P_READ 0
#define P_WRITE 1
#define C_READ 2
#define C_WRITE 3
int main(int argv, char *argc[]) {
/*Check if program is called correctly*/
if(argv != 3) {
printf("Please call program appropriately\n");
exit(EXIT_FAILURE);
}
FILE *r, *w, *check;
void *sharedMem;
int pipes[4];
int shm;
char userInput[5];
char *name = "dm11ad_cop4610";
int inChild = 0;
int inParent = 0;
r = fopen(argc[1], "rb");
check = fopen(argc[2], "rb");
/*Check if read file can open*/
if(r == NULL) {
perror("Error opening read file");
exit(EXIT_FAILURE);
}
/*Check if write file can open*/
if(check == NULL) {
perror("Error with write file");
exit(EXIT_FAILURE);
}
else {
fseek(check, 0, SEEK_END);
int writeLen = ftell(check);
if(writeLen > 0) {
rewind(check);
printf("Would you like to overwrite file (yes/no): ");
scanf("%s", userInput);
if(!strcmp(userInput, "yes")) {
printf("Overwriting file...\n");
w = fopen(argc[2], "wb");
}
else if(!strcmp(userInput, "no")) {
printf("Will not overwrite\n");
exit(EXIT_FAILURE);
}
else {
printf("User input not accepted\n");
exit(EXIT_FAILURE);
}
}
}
for (int i = 0; i < NUM_OF_PIPES; i++) {
if (pipe(pipes+(i*2)) < 0) {
perror("Pipe");
exit(EXIT_FAILURE);
}
}
/*Check if forking process is successful*/
pid_t pid = fork();
if(pid < 0) {
perror("Fork");
exit(EXIT_FAILURE);
}
shm = shm_open(name, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
if(shm == -1) {
perror("Shared memory");
exit(EXIT_FAILURE);
}
if(ftruncate(shm, SIZE) == -1) {
perror("Shared Memory");
exit(EXIT_FAILURE);
}
sharedMem = mmap(NULL, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm, 0);
if(sharedMem == MAP_FAILED) {
perror("Mapping shared memory");
exit(EXIT_FAILURE);
}
if(pid == 0) {
while(inParent);
inChild = 1;
printf("I am in child\n");
close(pipes[P_READ]);
close(pipes[P_WRITE]);
printf("Closed P pipes\n");
int cBytes, len;
printf("Im stuck\n");
len = read(pipes[C_READ], &cBytes, sizeof(cBytes));
printf("There are %i bytes\n", len);
if(len < 0) {
perror("Failed to read from pipe");
exit(EXIT_FAILURE);
}
else if(len == 0) {
printf("End of fle reached\n");
}
else {
printf("Writing to file\n");
fwrite(sharedMem, 1, sizeof(sharedMem), w);
}
printf("Closing C pipes\n");
close(pipes[C_READ]);
close(pipes[C_WRITE]);
printf("Exiting Child\n");
inChild = 0;
}
else {
while(inChild);
inParent = 1;
close(pipes[C_READ]);
close(pipes[C_WRITE]);
int pBytes;
int P2SHM = fread(sharedMem, 1, SIZE, r);
if(P2SHM < 0) {
perror("Could not store to shared memory");
exit(EXIT_FAILURE);
}
if(write(pipes[P_WRITE], &P2SHM, sizeof(int)) < 0) {
perror("Failed to write to pipe");
exit(EXIT_FAILURE);
}
int C2P = read(pipes[P_READ], &pBytes, sizeof(int));
if(C2P < 0) {
perror("Failed to read value from pipe");
exit(EXIT_FAILURE);
}
else if(C2P == 0) {
printf("End of file reached\n");
}
else {
printf("Received succesfully\n");
}
close(pipes[P_READ]);
close(pipes[P_WRITE]);
inParent = 0;
printf("Waiting for child\n");
wait(NULL);
}
return 0;
}
The printfs are there to help me see where the program is during execution. It gets stuck in child process, it seems during
len = read(pipes[C_READ], &cBytes, sizeof(cBytes));
This is an assignment, so please do not post code as an answer but rather please hep me understand what I am doing wrong. Thanks
Synchronization mechanism between child and parent looks suspicious:
while(inParent);
inChild = 1;
and
while(inChild);
inParent = 1;
Initial values for inChild and inParent is 0. After child process created each process has it's own copy of variable values. When you change inChild = 1 and inParent = 1, it's changed inside the current process only. Other process doesn't see new values and cannot wait for the input/output.
To fix it you should use better synchronization algorithm, e.g. processes semaphores. Read "5.2 Processes Semaphores" to get details.
It gets stuck in child process, it seems during
len = read(pipes[C_READ], &cBytes, sizeof(cBytes));
Well yes, I imagine it does.
You've been a bit too clever, I think, in setting up a single 4-element array for the pipe-end file descriptors. That's not inherently wrong, but it tends to obscure what's going on a bit.
Consider what the pipes are supposed to do for you: one process writes to the write end of a pipe, and the other reads what was written from the read end of that same pipe. Look carefully at which file descriptors each process is reading from and writing to.

Writing in System V shared memory segment

I'm studying shared memory and now I'm writing a program that use system v shared memory. This is my code:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <fcntl.h>
#define PG_SIZE sysconf(_SC_PAGESIZE)
#define PROJ_ID 1
#define SHM_FILE "/dev/shm/myshm"
void executeChild( void );
void executeParent( void );
int main ( int argc, char *argv[] )
{
unlink(SHM_FILE);
int fd = creat(SHM_FILE, S_IRWXU );
if( fd == -1 ){
perror("Segment memory file creation failed");
exit(1);
}
int pid = fork();
if( pid < 0){
perror("Fork failed\n");
return EXIT_FAILURE;
}
if( pid ){
executeParent();
printf("Parent waiting...\n");
int status = 0;
wait(&status); //wait for child process
printf("Parent done\n");
}else{
executeChild();
}
close( fd );
return EXIT_SUCCESS;
}
void executeChild( void )
{
printf("Child running\n");
sleep(15);
}
void executeParent( void )
{
printf("Parent running\n");
key_t token = ftok(SHM_FILE, PROJ_ID);
if( token == -1 ){
perror("Token creation failed");
exit(1);
}
int segment = shmget( token, PG_SIZE, IPC_CREAT | IPC_EXCL | S_IRWXU);
if ( segment == -1 ){
perror("Segment creation failed");
exit(1);
}
void * shm_ptr = shmat(segment, NULL, 0);
if( shm_ptr == (void *)(-1) ){
perror("Segment attachament failed");
exit(1);
}
printf("Shared memory ( %d ) attached\n", segment);
struct shmid_ds shm_info;
if( shmctl(segment, IPC_STAT, &shm_info) == -1 ){
perror("shmctl failed");
exit(1);
}
printf("Segment size = %zu\n", shm_info.shm_segsz);
printf("Writing...\n");
const char * test = "teste";
memcpy(shm_ptr, test, strlen(teste));
}
The file of the shared memory is created. I can see it on /dev/shm and icps commando also show it. But the size of the file of my shared memory segment is not increasing. So I presume that the memcpy is not working properly like I was expected. Why?
Finally, I got it how shared memory really works... special thanks for Thiago Klock.
The file passed to ftok does not change its size. It exists just for generate the unique key used in the Interprocess Communication... :)
This is my source code where my parent process write a simple message to the child process:
https://github.com/guilhermesft/vanzstuff/blob/master/ipc/shmsysv.c

Resources