Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
I'm struggling with shared mem on linux paltform.
Cosider the following code:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/mman.h>
#include <semaphore.h>
#define SEM_NAME "mysem"
int main (int argc, char *argv[])
{
int fd, zero = 0;
int *ptr;
sem_t *mutex;
pid_t PID = getpid();
int mmap_prot = PROT_WRITE;
if (argc < 2)
{
printf(" Usage: Test [OPTION]\n\tW = Write Only\n\tR = Read Only\n");
return 1;
}
if (*argv[1] == 'W')
{
fd = open("Test_SHM", O_RDWR | O_CREAT, -1);
if (fd == -1)
perror("open");
write(fd, &zero, sizeof(int));
}
else
{
fd = open("Test_SHM", O_RDONLY| O_CREAT, -1);
if (fd == -1)
perror("open");
mmap_prot = PROT_READ;
}
ptr = mmap(NULL, sizeof(int), mmap_prot, MAP_SHARED, fd, 0);
close(fd);
if (ptr == MAP_FAILED)
{
perror("mmap");
return 1;
}
// create, initialize, and unlink semaphore
mutex = sem_open(SEM_NAME, O_CREAT | O_EXCL, -1, 1);
sem_unlink(SEM_NAME);
setbuf(stdout, NULL); /* stdout is unbuffered */
printf("Shared Mem ready..\n");
while(1)
{
sem_wait(mutex);
printf("PID %d Count: %d\n", PID, (*ptr)++);
sem_post(mutex);
sleep(1);
}
return 0;
}
If I launch the app for a read only shared memory I get, as expected, segmentation fault the first time *ptr is incremented inside main loop.
I'm working on a lib that abstract Linux shared memory.
This lib will be deployed to third part developers that will implement some processes for my application on an embedded target.
This lib will implement "global variables" between processes. I was wondering if I can avoid to develop get and set function and simply return address of allocated memory.
In case of wrong permission access I want to give to caller infos about what was wrong in its code. Read segmentation fault on terminal and process termination does not give user a good information.
EDIT2
After #Ctx answer I tried the following solution but it works the first segmentation fault. The second trigger standard segmentation fault and pogram terminate.
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/mman.h>
#include <semaphore.h>
#include <signal.h>
#include <stdbool.h>
#include <setjmp.h>
#define SEM_NAME "mysem"
#define TEST 1
jmp_buf env;
void segvhandler(int arg) {
longjmp(env, 1);
}
bool Test ( int *ptr, sem_t *mutex, pid_t PID)
{
#if (TEST == 1)
signal(SIGSEGV, segvhandler);
#elif (TEST == 2)
sig_t segvhandler_OLD = signal(SIGSEGV, segvhandler);
#endif
int val = setjmp(env);
if (val != 0)
{
printf("Segmentation fault catched.\n");
sem_post(mutex);
#if (TEST == 1)
signal(SIGSEGV, SIG_DFL);
#elif (TEST == 2)
signal(SIGSEGV, segvhandler_OLD);
#endif
return false;
}
sem_wait(mutex);
printf("PID %d Count: %d\n", PID, (*ptr)++);
sem_post(mutex);
#if (TEST == 1)
signal(SIGSEGV, SIG_DFL);
#elif (TEST == 2)
signal(SIGSEGV, segvhandler_OLD);
#endif
return true;
}
int main (int argc, char *argv[])
{
int fd, zero = 0;
int *ptr;
sem_t *mutex;
pid_t PID = getpid();
int mmap_prot = PROT_WRITE;
if (argc < 2)
{
printf(" Usage: Test [OPTION]\n\tW = Write Only\n\tR = Read Only\n");
return 1;
}
if (*argv[1] == 'W')
{
fd = open("Test_SHM", O_RDWR | O_CREAT, -1);
if (fd == -1)
perror("open");
write(fd, &zero, sizeof(int));
}
else
{
fd = open("Test_SHM", O_RDONLY| O_CREAT, -1);
if (fd == -1)
perror("open");
mmap_prot = PROT_READ;
}
ptr = mmap(NULL, sizeof(int), mmap_prot, MAP_SHARED, fd, 0);
close(fd);
if (ptr == MAP_FAILED)
{
perror("mmap");
return 1;
}
// create, initialize, and unlink semaphore
mutex = sem_open(SEM_NAME, O_CREAT | O_EXCL, -1, 1);
sem_unlink(SEM_NAME);
setbuf(stdout, NULL); /* stdout is unbuffered */
printf("Shared Mem ready..\n");
while(1)
{
Test (ptr, mutex, PID);
sleep(1);
}
return 0;
}
Per the mmap() man page:
Use of a mapped region can result in these signals:
SIGSEGV
Attempted write into a region mapped as read-only.
If you want to proceed if the modification doesn't work, you can install a signal handler for SIGSEGV and use (sig)setjmp/longjmp to continue execution at a defined point:
#include <setjmp.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
jmp_buf env;
void segvhandler(int arg) {
siglongjmp(env, 1);
}
void somefunc(void) {
char *ptr = NULL;
signal(SIGSEGV, segvhandler);
if (!sigsetjmp(env, 1)) {
// Direct invocation, try the memory access
*ptr++;
}
signal(SIGSEGV, SIG_DFL);
}
int main (void) {
while (1) {
somefunc();
printf("One more iteration...\n");
}
exit(EXIT_SUCCESS); // Never reached
}
sigsetjmp(env, 1) also saves the blocked signals in env, when it's second argument is non-zero and siglongjmp() then restores these. Otherwise, the signal will still be blocked after longjmp(), since it is not a real return from the signal handler.
Keep in mind that you should only have the handler installed directly before you make the memory access in question and deinstall it afterwards.
a few minutes with the debugger shows the program crashes on the call to sem_wait().
if, after the call to sem_open() insert:
if( SEM_FAILED == mutex )
{
perror( "sem_open failed" );
exit( EXIT_FAILURE );
}
then move the statement:
sem_unlink(SEM_NAME);
to before the statement:
mutex = sem_open(SEM_NAME, O_CREAT | O_EXCL, -1, 1);
then it will become obvious that the remaining problem is in this statement:
printf("PID %d Count: %d\n", PID, (*ptr)++);
which causes a bus error signal to be raised. this bus error signal occurs on the very first pass through the while() loop.
there is a simple cause.
the printf() statement, last parameter is trying to both read and write the memory mapped file, but the memory mapping was only for (depending on the command line parameter) either 'PROT_READ' which allows reading or 'PROT_WRITE' which allows writing. the parameter to the call to mmap() needs to include both capabilities AND the call to open() also needs to
have the mode: O_RDWR. (the open() and the mmap() modes need to match
This is the corrected code after Ctx answer. I also found out THIS that is useful to understan why longjmp is not the right solution with signals.
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/mman.h>
#include <semaphore.h>
#include <signal.h>
#include <stdbool.h>
#include <setjmp.h>
#define SEM_NAME "mysem"
#define TEST 1
jmp_buf env;
void segvhandler(int arg) {
siglongjmp(env, 1);
}
bool Test ( int *ptr, sem_t *mutex, pid_t PID)
{
#if (TEST == 1)
signal(SIGSEGV, segvhandler);
#elif (TEST == 2)
sig_t segvhandler_OLD = signal(SIGSEGV, segvhandler);
#endif
int val = sigsetjmp(env, 1);
if (val != 0)
{
printf("Segmentation fault catched.\n");
sem_post(mutex);
#if (TEST == 1)
signal(SIGSEGV, SIG_DFL);
#elif (TEST == 2)
signal(SIGSEGV, segvhandler_OLD);
#endif
return false;
}
sem_wait(mutex);
printf("PID %d Count: %d\n", PID, (*ptr)++);
sem_post(mutex);
#if (TEST == 1)
signal(SIGSEGV, SIG_DFL);
#elif (TEST == 2)
signal(SIGSEGV, segvhandler_OLD);
#endif
return true;
}
int main (int argc, char *argv[])
{
int fd, zero = 0;
int *ptr;
sem_t *mutex;
pid_t PID = getpid();
int mmap_prot = PROT_WRITE;
if (argc < 2)
{
printf(" Usage: Test [OPTION]\n\tW = Write Only\n\tR = Read Only\n");
return 1;
}
if (*argv[1] == 'W')
{
fd = open("Test_SHM", O_RDWR | O_CREAT, -1);
if (fd == -1)
perror("open");
write(fd, &zero, sizeof(int));
}
else
{
fd = open("Test_SHM", O_RDONLY| O_CREAT, -1);
if (fd == -1)
perror("open");
mmap_prot = PROT_READ;
}
ptr = mmap(NULL, sizeof(int), mmap_prot, MAP_SHARED, fd, 0);
close(fd);
if (ptr == MAP_FAILED)
{
perror("mmap");
return 1;
}
// create, initialize, and unlink semaphore
mutex = sem_open(SEM_NAME, O_CREAT | O_EXCL, -1, 1);
sem_unlink(SEM_NAME);
setbuf(stdout, NULL); /* stdout is unbuffered */
printf("Shared Mem ready..\n");
while(1)
{
Test (ptr, mutex, PID);
sleep(1);
}
return 0;
}
Related
I want the following: first the parent process prints "hello", then the child process prints "world" and this is done 3 times. When I execute my code I only get hello world once but I want to get it printed 3 times.
#include <semaphore.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
int main(int argc, char*argv[]){
char* parent="hello ";
char* child="world";
sem_t *sem = sem_open("/mysem", O_CREAT, 0644, 1);
sem_t *sem1=sem_open("/mysem1", O_CREAT, 0644, 0);
int pid=fork();
if(pid<0){
printf("error in forking");
return -1;
}
int cnt=0, cnt1=0;
if(pid==0){
sem_wait(sem1);
write(1, child, strlen(child));
cnt++;
if(cnt1<3){
sem_post(sem);
}
}else{
sem_wait(sem);
write(1, parent, strlen(parent));
cnt++;
if(cnt<3){
sem_post(sem1);
}
}
return 0;
}
maybe try this :
#include <semaphore.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#define BUFFER_SIZE 7
int main(int argc, char **argv)
{
char parent[BUFFER_SIZE] = "hello ";
char child[BUFFER_SIZE] = "world\n";
sem_t *sem = sem_open("/mysem", O_CREAT, 0644, 1);
sem_t *sem1 = sem_open("/mysem1", O_CREAT, 0644, 0);
int cnt = 0;
int pid = fork();
if (sem == SEM_FAILED || sem1 == SEM_FAILED) {
printf("Error : sem_open() call failed.\n");
return (-1);
}
if (pid < 0) {
printf("error in forking");
return (-1);
}
if (pid == 0) {
while (cnt < 3) {
sem_wait(sem1);
if (write(1, child, strlen(child)) == -1) {
printf("Error : write call failed.\n");
}
cnt++;
sem_post(sem);
}
}
else {
while (cnt < 3) {
sem_wait(sem);
if (write(1, parent, strlen(parent)) == -1) {
printf("Error : write function failed.\n");
}
cnt++;
sem_post(sem1);
}
}
sem_close(sem);
sem_close(sem1);
return (0);
}
Explanation :
#define BUFFER_SIZE 7 this is a macro, 7 because of part 2.
Memory leaks (use valgrind)
Don't do : char *str = "abc";
but do : char str[string_size + 1] = "abc";
Because char *str = doesn't allocate memory so you will probably get memory leaks, always allocate string_size + 1 with C String. You can use malloc too to allocate your own size.
Put your int cnt variables before the fork(), fork will copy all memory so you can put only one variable before your fork call, for more readable code.
You should always call sem_close() as soon as possible and you should always look for returned value from functions that are not your, use C man, for exmaple "man 2 write" in your linux console (not looking for sem_wait semp_post and sem_close returned value shouldn't be problematic here, but you can still print a message if you want).
Your main problem here is that you should switch your if (cnt < 3) into a loop as i did with while, your parent and child process only course once write() if you don't set a loop.
The most important thing, please look for coding style rules to make your code more redeable.
This is pretty long, but you should take at look in all i said.
Compilation :
gcc file.c -pthread
./a.out
I'm on linux, fedora32.
Output :
hello world
hello world
hello world
In fork child, if we modify a global variable, it will not get changed in the main program.
Is there a way to change a global variable in child fork?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int glob_var;
main (int ac, char **av)
{
int pid;
glob_var = 1;
if ((pid = fork()) == 0) {
/* child */
glob_var = 5;
}
else {
/* Error */
perror ("fork");
exit (1);
}
int status;
while (wait(&status) != pid) {
}
printf("%d\n",glob_var); // this will display 1 and not 5.
}
You can use shared memory (shm_open(), shm_unlink(), mmap(), etc.).
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
static int *glob_var;
int main(void)
{
glob_var = mmap(NULL, sizeof *glob_var, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
*glob_var = 1;
if (fork() == 0) {
*glob_var = 5;
exit(EXIT_SUCCESS);
} else {
wait(NULL);
printf("%d\n", *glob_var);
munmap(glob_var, sizeof *glob_var);
}
return 0;
}
Changing a global variable is not possible because the new created process (child)is having it's own address space.
So it's better to use shmget(),shmat() from POSIX api
Or You can use pthread , since pthreadsare sharing the globaldata and the changes in global variable is reflected in parent.
Then read some Pthreads tutorial.
Here is an alternative solution.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
typedef struct
{
int id;
size_t size;
} shm_t;
shm_t *shm_new(size_t size)
{
shm_t *shm = calloc(1, sizeof *shm);
shm->size = size;
if ((shm->id = shmget(IPC_PRIVATE, size, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR)) < 0)
{
perror("shmget");
free(shm);
return NULL;
}
return shm;
}
void shm_write(shm_t *shm, void *data)
{
void *shm_data;
if ((shm_data = shmat(shm->id, NULL, 0)) == (void *) -1)
{
perror("write");
return;
}
memcpy(shm_data, data, shm->size);
shmdt(shm_data);
}
void shm_read(void *data, shm_t *shm)
{
void *shm_data;
if ((shm_data = shmat(shm->id, NULL, 0)) == (void *) -1)
{
perror("read");
return;
}
memcpy(data, shm_data, shm->size);
shmdt(shm_data);
}
void shm_del(shm_t *shm)
{
shmctl(shm->id, IPC_RMID, 0);
free(shm);
}
int main()
{
int var = 1;
shm_t *shm = shm_new(sizeof var);
int pid;
if ((pid = fork()) == 0)
{ /* child */
var = 5;
shm_write(shm, &var);
printf("child: %d\n", var);
return 0;
}
/* Wait for child to return */
int status;
while (wait(&status) != pid);
/* */
shm_read(&var, shm);
/* Parent is updated by child */
printf("parent: %d\n", var);
shm_del(shm);
return 0;
}
Build with:
$ gcc shm.c -o shm && ./shm
I have to create a program that synchronizes two processes each printing only a single letter so that whenever we observe the output of the program, the difference between the amount of "A" and "B" is no greater than 2.
So this would be accepted:
BAABBAABBABA
this wouldn't be because it prints 4 B's and only 2 A's:
ABBABB
So for starters i decided to use the POSIX semaphores.
I created two semaphores , giving them all the permissions using the sem_open
Then i created two child processes and for each child process i open the semaphores i created as described in the man page for sem_open and manipulate them.
I don't think it's the logic of the sem_post and sem_wait that's at fault here, since the program seems to ignore them.
So my question is. What goes wrong?
Edit: I don't really need the solution to the problem. Some guidance alone would be much appreciated and welcoming as an answer. Thank you in advance!
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <semaphore.h>
int main(void){
sem_t *semA = sem_open("/semA", O_CREAT|O_EXCL, S_IRWXU | S_IRWXG | S_IRWXO, 0); //Initialize semaphore(= 0) for process A
sem_t *semB = sem_open("/semB", O_CREAT|O_EXCL, S_IRWXU | S_IRWXG | S_IRWXO, 0); //Initialize semaphore(= 0) for process B
if (fork()){ // parent process
if(fork()){}
else{
sem_t *childsemA = sem_open("/semA", 0);
sem_t *childsemB = sem_open("/semB", 0);
while(1){
printf("A");
sem_post(childsemB);
sem_wait(childsemA);
}
}
}
else{
sem_t *childsemA = sem_open("/semA", 0);
sem_t *childsemB = sem_open("/semB", 0);
while(1){
printf("B"); // child2 process
sem_post(childsemA);
sem_wait(childsemB);
}
}
return 0;
}
Output:
May i suggest you to use System V semaphores? This is the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/sem.h>
#include "Semaphores.h"
#define SEM1_KEY (key_t)888
#define SEM2_KEY (key_t)1234
int sem1, sem2;
int main()
{
pid_t pid;
sem1 = semget(SEM1_KEY, 1, IPC_CREAT | 0666);
if(sem1 < 0)
{
fprintf(stderr, "\nSEMGET Failed\n");
exit(EXIT_FAILURE);
}
sem2 = semget(SEM2_KEY, 1, IPC_CREAT | 0666);
if(sem1 < 0)
{
fprintf(stderr, "\nSEMGET Failed\n");
exit(EXIT_FAILURE);
}
SEM_SET(sem1, 1);
SEM_SET(sem2, 0);
if((pid = fork()) == -1)
{
fprintf(stderr, "\nError in fork()\n");
exit(EXIT_FAILURE);
}
if(pid == 0)
{
while(1)
{
SEM_WAIT(sem2);
printf("%c", 'B');
fflush(stdout);
sleep(1);
SEM_POST(sem1);
}
}
while(1)
{
SEM_WAIT(sem1);
printf("%c", 'A');
fflush(stdout);
sleep(1);
SEM_POST(sem2);
}
wait(0);
SEM_DEL(sem1);
SEM_DEL(sem2);
exit(EXIT_SUCCESS);
}
And this is the header file Semaphores.h which includes the System V semaphores implementation:
#include <sys/sem.h>
union semun
{
int val;
struct semid_ds * buf;
unsigned short * array;
};
int SEM_SET(int sem_id, int sem_val)
{
union semun sem_union;
sem_union.val = sem_val;
return semctl(sem_id, 0, SETVAL, sem_union);
}
int SEM_DEL(int sem_id)
{
return semctl(sem_id, 0, IPC_RMID);
}
int SEM_WAIT(int sem_id)
{
struct sembuf sem_buf;
sem_buf.sem_num = 0;
sem_buf.sem_op = -1;
sem_buf.sem_flg = SEM_UNDO;
return semop(sem_id, &sem_buf, 1);
}
int SEM_POST(int sem_id)
{
struct sembuf sem_buf;
sem_buf.sem_num = 0;
sem_buf.sem_op = 1;
sem_buf.sem_flg = SEM_UNDO;
return semop(sem_id, &sem_buf, 1);
}
The result will be this:
ABABABABABABABABA and so on
fflush() was probably the problem, but your code has some leaks, you need to understand what is a critical section, and you need to check for the return values of fork().
The program should create 200000 integers and write 2000 to a shared memory. A forked process should read 2000 from shared memory and the parent should write the next 2000 to shared memory.
if i use the code below without sleep, the parent first creates all 200000 integers and then the child reads the same integers from shared memory.
With sleep everything looks good, but we have to use semaphore.
shm.c (parent):
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include <sys/wait.h>
#define N_DATA 200000
#define N_SHARED 2000
#define LOCK -1
#define UNLOCK 1
static struct sembuf semaphore;
char shmidArg[32];
char semidArg[32];
int *shmData;
int i, j;
int status;
char *strsignal(int sig);
pid_t pid;
static int shmid;
static int semid;
char *strsignal(int sig);
/** Semaphore Operation */
static int semaphore_operation (int op) {
semaphore.sem_num = 1;
semaphore.sem_op = op;
semaphore.sem_flg = IPC_NOWAIT;
if( semop (semid, &semaphore, 1) == -1) {
perror(" semop ");
exit (EXIT_FAILURE);
}
return 1;
}
int main(int argc, char **argv) {
/* Ein Shared-Memory-Segment einrichten */
shmid = shmget(IPC_PRIVATE, N_SHARED*sizeof(int), IPC_CREAT | SHM_R | SHM_W);
if (shmid == -1) {
perror("shmid");
exit(1);
}
printf("Shared-Memory-ID: %d\n",shmid);
/* Pointer zu Shared-Memory-Segment erhalten */
shmData = (int *)shmat(shmid,0, 0);
if (shmData == (int *)(-1)) {
perror("shmat");
exit(1);
}
/* Semaphore anlegen */
semid = semget(IPC_PRIVATE, 1, IPC_CREAT | SHM_R | SHM_W);
if (semid < 0) {
perror("semid");
exit(1);
}
printf ("Semaphor-ID : %d\n", semid);
/* Semaphor mit 1 initialisieren */
if (semctl (semid, 0, SETVAL, (int) 1) == -1) {
perror("semctl");
}
snprintf(shmidArg,32, "%d", shmid);
snprintf(semidArg,32, "%d", semid);
/** erstellen des Kindprozesses */
pid = fork();
// Kindprozess
if (pid == 0) {
execlp("./shm_child",shmidArg,semidArg,NULL);
} else if (pid < 0) {
perror("Kindprozess konnte nicht erzeugt werden!");
return 1;
}
// Elternprozess
else {
/** ininitalisieren des Zufallsgenerator durch aktuellen Zeitstempel */
srand48(time(NULL));
for(i=0;i<N_DATA;i=i+N_SHARED) {
semaphore_operation(LOCK);
for (j=0; j<N_SHARED; j++) {
shmData[j] = lrand48();
//MSZ
//printf("SHM-->%d-->%d\n",i+1,shmData[i]);
}
// if(i == 0 || i == 2000) {
printf("Parent-->%d-->0-->%d\n",i,shmData[0]);
printf("Parent-->%d-->1999->%d\n",i,shmData[1999]);
// }
semaphore_operation(UNLOCK);
//sleep(1);
}
}
//MSZ
//sleep(2);
printf("PID: %d\n", pid);
if (waitpid(pid, &status, 0) == -1) {
perror("wait konnte nicht erzeugt werden!");
return 1;
}
if (WIFEXITED(status)) {
printf("Exitcode: %d\n", WEXITSTATUS(status));
semctl (semid, 0, IPC_RMID, 0);
shmctl (shmid, IPC_RMID, NULL);
//If process terminaded by a signal
} else if (WIFSIGNALED(status)) {
printf("Signal: %d %s\n", WTERMSIG(status), strsignal(WTERMSIG(status)));
semctl (semid, 0, IPC_RMID, 0);
shmctl (shmid, IPC_RMID, NULL);
}
}
shm_child.c (Child):
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#define N_DATA 6000
#define N_SHARED 2000
#define LOCK -1
#define UNLOCK 1
int i,j;
int *shmData;
static int shmid;
static int semid;
static struct sembuf semaphore;
/** Semaphore Operation */
static int semaphore_operation (int op) {
semaphore.sem_num = 0;
semaphore.sem_op = op;
semaphore.sem_flg = SEM_UNDO;
if( semop (semid, &semaphore, 1) == -1) {
perror(" semop ");
exit (EXIT_FAILURE);
}
return 1;
}
int main(int argc, char **argv) {
shmid = atoi(argv[0]);
semid = atoi(argv[1]);
printf("\nshm_child shared memoryid:%d\n",shmid);
printf("shm_child Semaphoren-ID:%d\n",semid);
/* Pointer auf Shared-Memory erstellen */
shmData = (int *)shmat(shmid,0,0);
if (shmData == (int *)(-1)) {
perror("shmat");
exit(1);
}
for(i=0;i<N_DATA;i=i+N_SHARED) {
semaphore_operation(LOCK);
for(j=0;j<N_SHARED;j++) {
//printf("%d-->%d --> %d\n",i,j+1,shmData[j]);
}
// if(i == 0 || i == 2000) {
printf("child-->%d-->0-->%d\n",i,shmData[0]);
printf("child-->%d-->1999->%d\n",i,shmData[1999]);
// }
semaphore_operation(UNLOCK);
sleep(1);
}
return 0;
}
Please help us
Thank you guys
Edit: Thank you very much for your answers. I can't mark the right answer because i dont know what its right. But i dont want try anything more. 15 hours are enough
The writer process shall give reader a permission to read, and wait for the reading completion. After that the reader shall give writer a permission to proceed, and wait for writing completion.
This goal cannot be achieved with a single semaphore. You need two, along the lines of:
// parent aka writer
writer_barrier = semaphore(UNLOCKED);
reader_barrier = semaphore(LOCKED);
start_reader();
while(...) {
lock(writer_barrier);
write_data();
unlock(reader_barrier);
}
// child aka reader
while(....)
lock(reader_barrier);
read_data();
unlock(writer_barrier);
}
I'm facing a synchronization problem, the problem I'm trying to solve involves sending string from parent to child, reversing it and sending it back to child ( using shared memory ).
However to make sure child is waiting for parent I'm using sleep(3) to give 3 seconds to parent process to enter string, however this is limiting my programs efficiency, I don't want to force user to wait for 3 seconds.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <sys/wait.h> /* Needed for the wait function */
#include <unistd.h> /* needed for the fork function */
#include <string.h> /* needed for the strcat function */
#define SHMSIZE 27
int main() {
int shmid;
char *shm;
if(fork() == 0) {
sleep(3);
shmid = shmget(29009, SHMSIZE, 0);
shm = shmat(shmid, 0, 0);
printf ("Child : Reading %s \n",shm) ;
int len=strlen(shm);
char rev[100],temp;
int i = 0;
int j = strlen(shm) - 2;
while (i < j) {
temp = shm[i];
shm[i] = shm[j];
shm[j] = temp;
i++;
j--;
}
shmdt(shm);
}else {
shmid = shmget(29009, SHMSIZE, 0666 | IPC_CREAT);
shm = shmat(shmid, 0, 0);
printf("Parent : Enter String \n ");
char *s = (char *) shm;
*s = '\0';
char a[100];
fgets(a,100,stdin);
strcat(s,a);
printf ("Parent: sending %s \n",shm);
sleep(3);
printf("Parent: receiving %s" ,shm);
shmdt(shm);
}
return 0;
}
Question:
How could this be implemented in a better way, so that the program is more efficient?
I would suggest using semaphores, this is not a case where you use 'sleep':
http://man7.org/linux/man-pages/man7/sem_overview.7.html
You can use them like in this example:
http://www.csc.villanova.edu/~mdamian/threads/posixsem.html
You cannot know for sure that it will not take more than 3 seconds, so sleep is a realy bad choice. So, it goes something like this:
#include <stdio.h>
#include <semaphore.h>
int main(void)
{
sem_t *sem = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE,
MAP_SHARED|MAP_ANONYMOUS, -1, 0);
sem_init(sem, 1, 1);
if(fork() == 0) {
printf("Child: Waiting to acquire semaphore\n");
sem_wait(sem);
printf("Child acquires lock\n");
/* do whatever you want then relese*/
sem_post(sem);
} else {
printf("Parent: Waiting to acquire semaphore\n");
sem_wait(sem);
printf("Parent acquires lock\n");
/* do whatever you want then relese*/
sem_post(sem);
}
sem_destroy(sem);
return 0;
}
Oh and if you want it parent to be followed by child always (or the other way around), you can use two semaphores, and initialize them accordingly(with 1 and 0, or 0 and 1).
sem_wait(sem1);
printf("Parent acquires lock\n");
/* do whatever you want then relese*/
sem_post(sem2);
/* Other things will be happening here */
sem_wait(sem2);
printf("Child acquires lock\n");
/* do whatever you want then relese*/
sem_post(sem1);
Edit
If you do not have to use shared memory, it would be better to do the communication with sockets.
Thanks to amazing StackOverflow community for coming to my rescue! I have resolved solved the issue using semaphores! I'm sharing my final code so it can be of use for anyone who gets struck in a situation like mine!
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <sys/wait.h> /* Needed for the wait function */
#include <unistd.h> /* needed for the fork function */
#include <string.h> /* needed for the strcat function */
#include <semaphore.h>
#include <sys/mman.h>
#include<fcntl.h>
#include<stdlib.h>
#define SHMSIZE 27
typedef struct {
sem_t one;
sem_t two;
} SemPair;
int main() {
int shm = shm_open("/test", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
ftruncate(shm, sizeof(sem_t));
SemPair *sem = mmap(NULL, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_SHARED, shm, 0);
sem_init(&(sem->one), 1, 0);
sem_init(&(sem->two), 1, 0);
int shmid;
char *shmz;
if(fork() == 0) {
sem_wait(&(sem->one));
shmid = shmget(29009, SHMSIZE, 0);
shmz = shmat(shmid, 0, 0);
printf ("Child : Reading %s \n",shmz) ;
int len=strlen(shmz);
char rev[100],temp;
int i = 0;
int j = strlen(shmz) - 2;
while (i < j) {
temp = shmz[i];
shmz[i] = shmz[j];
shmz[j] = temp;
i++;
j--;
}
shmdt(shmz);
sem_post(&(sem->two));
}
else {
shmid = shmget(29009, SHMSIZE, 0666 | IPC_CREAT);
shmz = shmat(shmid, 0, 0);
printf("Parent : Enter String \n ");
char *s = (char *) shmz;
*s = '\0';
char a[100];
fgets(a,100,stdin);
strcat(s,a);
printf ("Parent: sending %s \n",shmz);
sem_post(&(sem->one));
sem_wait(&(sem->two));
printf("Parent: receiving %s" ,shmz);
shmdt(shmz);
}
return 0;
}