Sharing data between threads in C - c

I have the following main.c:
#include <unistd.h> //has thread calls for fork()
#include <stdio.h>
struct globalInfo{
int x;
};
int do this()
{
info.x++;
printf("%d\n",info.x);
return 0;
}
int main{
struct globalInfo info = { .x = 2};
for(int i = 0 ; i < 5 ; i++)
{
if(fork() = 0)
{
dothis();
}
}
}
This isn't my exact code, but my question is more easily demonstrated here.
Now the output for the above function is:
3
3
3
3
3
What I want is:
3
4
5
6
7
How do share this struct between threads? It seems like every thread is just creating its own copy of the struct and manipulating its own copy. I've tried to pass a pointer to the info struct as a parameter to dothis(), but that doesn't fix it either. I've also tried placing the info initialization out of the main; that didn't work either..
Help would be greatly appreciated.

fork() doesnot create a thread it creates processes, processess will have different address spaces altogether hence data wil not be shared even if it is global data.
in case you are thinking of threads use pthreads
incase you are looking for processes you need to use IPC mechanisms

Use any IPC to share data b/w processes. In threads the data can be shared by following methods:
return value of thread passed to pthread_exit and catch in pthread_join
shared/global resource of process accessed by synchronizing methods

As people have already noted you are creating processes not threads. Sharing data among processes is harder. Every process has its own memory address space which means that they may share same code, but their data is private.
There are several techniques if you want to have shared data among processes. One of them is shared memory with memory map
#include <unistd.h> //has thread calls for fork()
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
struct globalInfo{
int x;
};
char *shm = "/asd8"; // change this before every run to avoid error 22
int dothis()
{
// sleep(1); // helps to simulate race condition
int len = sizeof(struct globalInfo);
int fd = shm_open(shm, O_RDWR, 0);
void *addr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (addr == MAP_FAILED){
perror(""); // error handling
}
struct globalInfo *info = (struct globalInfo *)addr;
info->x++;
printf("%d %d\n", info->x, getpid());
return 0;
}
int main(){
struct globalInfo info = { .x = 2};
int len = sizeof(struct globalInfo);
int fd = shm_open(shm, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
if (fd == -1)
perror(""); // error handling
if(ftruncate(fd, len) == -1){
printf("%d\n", errno); // osx produces error 22 don't know why
perror("");
}
void *addr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
memcpy(addr, &info, len);
for(int i = 0 ; i < 5 ; i++)
{
if(fork() == 0)
{
dothis();
break;
}
}
}
sample output
3 30588
4 30589
6 30590
6 30591
7 30592
Also it would be great if you read chapters from The Linux Programming Interface: A Linux and UNIX System Programming Handbook
24 Process Creation
49 Memory Mapping
53 POSIX Semaphores (you have to solve synchronisation problem after the data is shared)
54 POSIX Shared Memory

Related

synchronisation problem in multiple threads of multiple processes

I have a code. There are 2 processes. Parent is writer on file a.txt. Child is reader on a.txt.Parent has 2 threads and child has 2 threads. Parent's 1st thread opens a file parent1.txt . reads 128 chars. writes to a.txt.Parent's 2nd thread opens file parent2.txt.reads 128 chars. writes to a.txt. Child's 1st thread reads 128 chars from a.txt and writes to child1.txt. child's 2nd thread reads 128 chars from a.txt and child2.txt. Any parent thread , after writing, should generate event and invoke the child's reader threads.I have implemented a solution using mutex and condition variable.Parent's writer threads generate pthread_cond_signal after writing to a.txt. 1>But child's reader threads are not running after that. both parent threads are running in loop.2>Parent reads frm parent1.txt. the fread is successfull. But when it writes to a.txt, it is not successfull. the a.txt file is always empty.I think mutex can not be use between multiple processes. That may be 1 problem
My code is as follows
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h> /* For mode constants */
#include <fcntl.h> /* For O_* constants */
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;
FILE*fd,*fdRead1,*fdRead2,*fdWrite1,*fdWrite2;
pthread_mutex_t *mut1;
pthread_mutexattr_t attrmutex;
pthread_cond_t *cond_var;
pthread_condattr_t attrcond;
#define OKTOWRITE "/condwrite"
#define MESSAGE "/msg"
#define MUTEX "/mutex_lock"
void* R1(void *)
{
char buf[128];
int size;
fdWrite1 = fopen("child1.txt","w+");
cout<<"R1Thread"<<endl;
for(int i=0;i<10;i++)
{
cout<<"R1Thread-1"<<endl;
pthread_mutex_lock(mut1);
pthread_cond_wait(cond_var,mut1);
cout<<"R1Thread-2"<<endl;
size = fread(buf,128,1,fd);
fwrite(buf,size,1,fdWrite1);
pthread_mutex_unlock(mut1);
}
fclose(fdWrite1);
}
void* R2(void *)
{
char buf[128];
int size;
fdWrite2 = fopen("child2.txt","w+");
cout<<"R2Thread"<<endl;
for(int i=0;i<10;i++)
{
cout<<"R2Thread-1"<<endl;
pthread_mutex_lock(mut1);
pthread_cond_wait(cond_var,mut1);
cout<<"R2Thread-2"<<endl;
size = fread(buf,128,1,fd);
fwrite(buf,size,1,fdWrite2);
pthread_mutex_unlock(mut1);
}
fclose(fdWrite2);
}
void* W1(void *)
{
char buf[128];
int size;
fdRead1 = fopen("parent1.txt","r");
for(int i=0;i<10;i++)
{
pthread_mutex_lock(mut1);
size = fread(buf,128,1,fdRead1);
fwrite(buf,size,1,fd);
pthread_cond_signal(cond_var);
cout<<"W2Thread-1"<<endl;
pthread_mutex_unlock(mut1);
sleep(10);
}
fclose(fdRead1);
}
void* W2(void *)
{
char buf[128];
int size;
fdRead2 = fopen("parent2.txt","r");
for(int i=0;i<10;i++)
{
pthread_mutex_lock(mut1);
size = fread(buf,128,1,fdRead2);
fwrite(buf,size,1,fd);
pthread_cond_signal(cond_var);
cout<<"W2Thread-1"<<endl;
pthread_mutex_unlock(mut1);
sleep(1000);
}
fclose(fdRead2);
}
int main()
{
int des_cond, des_msg, des_mutex;
int mode = S_IRWXU | S_IRWXG;
des_mutex = shm_open(MUTEX, O_CREAT | O_RDWR | O_TRUNC, mode);
if (des_mutex < 0) {
perror("failure on shm_open on des_mutex");
exit(1);
}
if (ftruncate(des_mutex, sizeof(pthread_mutex_t)) == -1) {
perror("Error on ftruncate to sizeof pthread_cond_t\n");
exit(-1);
}
mut1 = (pthread_mutex_t*) mmap(NULL, sizeof(pthread_mutex_t),
PROT_READ | PROT_WRITE, MAP_SHARED, des_mutex, 0);
if (mut1 == MAP_FAILED ) {
perror("Error on mmap on mutex\n");
exit(1);
}
des_cond = shm_open(OKTOWRITE, O_CREAT | O_RDWR | O_TRUNC, mode);
if (des_cond < 0) {
perror("failure on shm_open on des_cond");
exit(1);
}
if (ftruncate(des_cond, sizeof(pthread_cond_t)) == -1) {
perror("Error on ftruncate to sizeof pthread_cond_t\n");
exit(-1);
}
cond_var = (pthread_cond_t*) mmap(NULL, sizeof(pthread_cond_t),
PROT_READ | PROT_WRITE, MAP_SHARED, des_cond, 0);
if (cond_var == MAP_FAILED ) {
perror("Error on mmap on condition\n");
exit(1);
}
/* Initialise attribute to mutex. */
pthread_mutexattr_init(&attrmutex);
pthread_mutexattr_setpshared(&attrmutex, PTHREAD_PROCESS_SHARED);
/* Allocate memory to pmutex here. */
/* Initialise mutex. */
pthread_mutex_init(mut1, &attrmutex);
/* Initialise attribute to condition. */
pthread_condattr_init(&attrcond);
pthread_condattr_setpshared(&attrcond, PTHREAD_PROCESS_SHARED);
/* Allocate memory to pcond here. */
/* Initialise condition. */
pthread_cond_init(cond_var, &attrcond);
pthread_t thR1,thR2,thW1,thW2;
fd = fopen("a.txt","w+");
int res = fork();
if(res<0) perror("error forking\n");
if(res==0)//child
{
cout<<"child created"<<endl;
pthread_create(&thR1,0,R1,0);
//pthread_create(&thR2,0,R2,0);
pthread_join(thR1,0);
//pthread_join(thR2,0);
fclose(fd);
}
else//parent
{
//fdRead = fopen("parent.txt","r");
pthread_create(&thW1,0,W1,0);
//pthread_create(&thW2,0,W2,0);
pthread_join(thW1,0);
//pthread_join(thW2,0);
fclose(fd);
wait(0);
}
}
The output is as follows-
child created
W2Thread-1
R1Thread
R1Thread-1
W2Thread-1
W2Thread-1
W2Thread-1
W2Thread-1
W2Thread-1
W2Thread-1
W2Thread-1
W2Thread-1
The condition_wait in child never comes out.
There are potential issues with using multiple processes, each with multiple threads, but they mostly revolve around program state at the time of the fork. Since your program forks before it creates any additional threads, you can be confident about its state at that time, and in particular, you can be confident that its one thread is not at that time executing in a critical section. This is fine.
However, you are missing two key details:
Although you set the mutex to be process-shared, the version of the code you initially presented failed to do the same for the condition variable.
Setting pthread_* synchronization objects to be process-shared is necessary, but not sufficient, for inter-process use. For that, you need the synchronization objects to reside in shared memory accessed by all participating processes. Only that way can all the process access the same objects.

Segmentation fault when using semaphores with POSIX shared memory

I have a problem with some simple code I'm writing to teach myself about semaphores and POSIX shared memory.
The idea is that one program, the server, opens the shared memory and writes a structure (containing a semaphore and an array) to it. Then it waits for input and after input increments the semaphore.
Meanwhile the client opens the shared memory, waits on the semaphore, and after it is incremented by the server, reads the structure.
The server seems to work okay, however I am encountering a segfault in the client at the sem_wait function, immediately (even before the server increments it). I can't figure out what is wrong.
Server code:
#define _XOPEN_SOURCE 500
#include <stdio.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <semaphore.h>
#include <stdbool.h>
#define ARRAY_MAX 1024
typedef struct {
sem_t inDataReady;
float array[ARRAY_MAX];
unsigned arrayLen;
} OsInputData;
int main() {
int shm_fd;
OsInputData *shm_ptr;
if((shm_fd = shm_open("/my_shm", O_CREAT | O_RDWR, 0666)) == -1) {
printf("shm_open failure\n");
return 1;
}
if(ftruncate(shm_fd, sizeof(OsInputData)) == -1) {
printf("ftruncate failure\n");
return 1;
}
if((shm_ptr = (OsInputData*)mmap(0, sizeof(OsInputData), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0)) == MAP_FAILED) {
printf("mmap failure\n");
return 1;
}
sem_init(&(shm_ptr->inDataReady), true, 0);
shm_ptr->array[0] = 3.0;
shm_ptr->array[1] = 1.0;
shm_ptr->array[2] = 2.0;
shm_ptr->array[3] = 5.0;
shm_ptr->array[4] = 4.0;
shm_ptr->arrayLen = 5;
getchar();
sem_post(&(shm_ptr->inDataReady));
sem_destroy(&(shm_ptr->inDataReady));
munmap(shm_ptr, sizeof(OsInputData));
close(shm_fd);
return 0;
}
Client code:
#define _XOPEN_SOURCE 500
#include <stdio.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <semaphore.h>
#include <stdbool.h>
#define ARRAY_MAX 1024
typedef struct {
sem_t inDataReady;
float array[ARRAY_MAX];
unsigned arrayLen;
} OsInputData;
int main() {
int shm_fd;
OsInputData *shm_ptr;
if((shm_fd = shm_open("/my_shm", O_RDONLY, 0666)) == -1) {
printf("shm_open failure\n");
return 1;
}
if((shm_ptr = (OsInputData*)mmap(0, sizeof(OsInputData), PROT_READ, MAP_SHARED, shm_fd, 0)) == MAP_FAILED) {
printf("mmap failure\n");
return 1;
}
sem_wait(&(shm_ptr->inDataReady));
printf("%u\n", shm_ptr->arrayLen);
munmap(shm_ptr, sizeof(OsInputData));
close(shm_fd);
return 0;
}
The actual outcome depends upon your system, but in general your program contains an error. You can’t destroy a semaphore that is reachable by another process/thread. Just because you have executed a sem_post doesn’t mean that your system has switched to the process waiting for it. When you destroy it, the other guy might still be using it.
SIGSEGV, in this case, is a kindness. Few programmers check the return values of sem_wait, which can lead to programs thinking they are synchronized when they are not.
Turns out I had to open the shared memory in the client with both read and write permissions, as well as update the protections accordingly in mmap.
Quite a stupid mistake, as it's obvious the client needs write permissions as well to actually modify the semaphore.
So in the client code the following changes solved it
...
shm_fd = shm_open("/my_shm", O_CREAT | O_RDWR, 0755)
...
shm_ptr = (OsInputData*)mmap(0, sizeof(OsInputData), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0)
...

One Process per Client Server using Shared Memory in C

I am creating an "One Process per Client" server using the TCP protocol for academic purpose.
I use a global struct like the one bellow:
struct keyvalue
{
char a[4096];
char b[4096];
}data[1000];
I use fork() to create a child for each client.
I know that each child sees this struct as an exact copy of the parent process however if a child makes a change it is not visible to the other children and this is my goal.
I searched in google for hours and the only proper solution i found is mmap()
Bellow I present how i tried to solve this task:
int main ( int argc, char *argv[])
{
for(int c = 0; c < 1000 ; c++)
{
data[c] = mmap(NULL, sizeof(data[c]), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
}
.
.
.
return 0;
}
However I think that I haven't understand properly the use of this function and the documentation didn't help for this project.
It would be great if someone explained to me the exact way to use this function for my project.
EDIT:
This global struct is used by two global function:
void put(char *key, char *value)
{
.
.
.
strcpy(data[lp].keys,key);
strcpy(data[lp].values,value);
.
.
.
}
Thank you in behave and sorry for my bad English.
You can use the following piece of code to create an array of structs that is shared across multiple forked processes.
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <semaphore.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define MAX_LEN 10000
struct region {
int len;
char buf[MAX_LEN];
};
int fd;
int main(void){
//Create shared memory
fd = shm_open("/myregion", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
if (fd == -1)
printf("Error: shm_open\n");
//Expand to meet the desirable size
if (ftruncate(fd, MAX_LEN*sizeof(struct region)) == -1)
printf("Error: ftruncate\n");
pid_t pid = fork();
if (pid == 0){
struct region * ptr = mmap(NULL, MAX_LEN*sizeof(struct region), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (ptr == MAP_FAILED)
printf("Error\n");
memset(ptr, 0, 50*sizeof(struct region));
usleep(1500000);
ptr[33].len = 42;
}else if (pid > 0){
struct region * ptr = mmap(NULL, MAX_LEN*sizeof(struct region), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (ptr == MAP_FAILED)
printf("Error\n");
usleep(1000000);
printf("Value before: %d\n", ptr[33].len);
usleep(1000000);
printf("Value after: %d\n", ptr[33].len);
}else{
printf("Error: fork\n");
}
shm_unlink("/myregion");
return 0;
}
Compilation: gcc -o shmem_test shmem_test.c -lrt
EDIT: If you can't use shm_open, alternatively you can do the following in your main function:
int main(void){
struct region * ptr = mmap(NULL, MAX_LEN*sizeof(struct region), PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_SHARED,-1,0);
pid_t pid = fork();
if (pid == 0){
usleep(1500000);
ptr[33].len = 42;
}else if (pid > 0){
usleep(1000000);
printf("Value before: %d\n", ptr[33].len);
usleep(1000000);
printf("Value after: %d\n", ptr[33].len);
}else{
printf("Error: fork\n");
}
return 0;
}
The difference between the two, is that shm_open creates a named shared memory, which means that different processes in different executables can map this memory, given that they have the struct region definition. In the second case this cannot be done, i.e the shared memory is anonymous.

Shared memory with mmap returns when writing to mapped memory on linux

I was trying to figure out shared memory and tried to write a simple program involving a consumer and a producer. I didnt make it to the consumer part and found this weird little problem: The parent will return at the *spool=3; with no rhyme or reason on why. Nothing on dmesg.
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#define PRODUCER_ERROR(msg) \
do {perror("Producer Error: " msg "\n"); exit(1); }while(0)
#define SHM_NAME "/my_sharedmem"
void producer();
int main(int argc, char *argv[])
{
pid_t pID = fork();
if (pID == 0) {
// Code only executed by child process
printf ("Son here\n");
return 0;
} else if (pID < 0) {
perror("Unable to fork\n");
exit(1);
} else {
// Code only executed by parent process
printf ("Parent here\n");
producer();
return 0;
}
return 0;
}
void producer()
{
int fd, d;
unsigned* spool;
printf("<<Producer>> started\n");
fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, S_IRWXU | S_IRWXG | S_IRWXO ); // FIXED
printf ("<<Producer>> memory file opened\n");
spool = mmap(NULL, sizeof(unsigned), PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, fd, 0);
printf ("<<Producer>> mmaped to %p\n\tGonna write.\n", spool);
perror(NULL);
*spool = 3;
// msync(spool, sizeof(unsigned), MS_SYNC | MS_INVALIDATE);
printf("<<Producer>> ended\n");
}
EDIT: fixed shm_open mode argument
The object you get with shm_open is zero size. You need to allocate some space for it. mmap will allow you to map things beyond their size (both shm objects and files), but you'll crash when you access that memory.
Something like this after shm_open is what you want to do:
ftruncate(fd, <the size you want>);
You can do it after mmap too, if that floats your boat.
You have the mode argument to shm_open wrong. This is supposed to be a mode specification as for open. Probably your version here by conincidence forbids writing to the address, so the process then crashes when you try to write to it.
BTW: you should always check the return of library calls such as shm_open and mmap.
Edit: As I also observed in a comment below, you are also missing to scale the segment to an appropriate size with ftruncate.

linux dynamic shared memory in different programs

I create a shared memory in program A with the following codes:
shm = shm_open("/mfs_hash_pool_container", O_CREAT|O_RDWR, 0666);
size = sizeof(struct mfs_hash_pool_container);
ftruncate(shm, size);
mfs_hash_pool_stat_p = (struct mfs_hash_pool_container *)mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, shm, 0);
I use that to store a hash table.
The other program B, will receives the addr (mfs_hash_pool_stat_p+offset) send from program A, but I can not write it in B.
Does this means I must also open this shared memory in B? Is there any other way to solve that? Because I create this memory automatically.
Thanks you guys.
You can't just use that address in the other program. B has to:
Obtain the file descriptor: shm_open("/mfs_hash_pool_container", O_RDWR, 0)
Map memory for the file descriptor: mmap just like A does
Notes:
You need to check the return value of mmap (it could return MAP_FAILED)
You need not cast the return value of mmap
Separate processes do not share memory, so the address being passed to B from A will not point to the shared memory segment. Each process must call shm_open() and mmap() the segment individually.
If you have information about the segment that every process needs to be aware of, pack it at the beginning of the segment in an order that each process is aware of.
I am not sure about how your program A and program B are related, but if you manage to spawn 'B' from 'A', using fork() + execve() combination, then you need not worry about passing the memory pointer as both processes will have the same copy.
For your reference I am pasting a nice code example present at IBM developerworks here-
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/file.h>
#include <sys/mman.h>
#include <sys/wait.h>
void error_and_die(const char *msg)
{
perror(msg);
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[])
{
int r;
const char *memname = "sample";
const size_t region_size = sysconf(_SC_PAGE_SIZE);
int fd = shm_open(memname, O_CREAT | O_TRUNC | O_RDWR, 0666);
if (fd == -1)
error_and_die("shm_open");
r = ftruncate(fd, region_size);
if (r != 0)
error_and_die("ftruncate");
void *ptr = mmap(0, region_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (ptr == MAP_FAILED)
error_and_die("mmap");
close(fd);
pid_t pid = fork();
if (pid == 0)
{
u_long *d = (u_long *)ptr;
*d = 0xdbeebee;
exit(0);
}
else
{
int status;
waitpid(pid, &status, 0);
printf("child wrote %#lx\n", *(u_long *)ptr);
}
r = munmap(ptr, region_size);
if (r != 0)
error_and_die("munmap");
r = shm_unlink(memname);
if (r != 0)
error_and_die("shm_unlink");
return 0;
}
Read the full article in the above link to gain a better understanding of shared memory!
Process do not share memory by default. If you want this 2 processes to communicate or share memory, you'll have to make that happen. Check this question.
Another solution is to use threads, which share code and memory alike.

Resources