simple memory sharing issue - linux

I'm trying to simulate simple shared memory between two processeses on ubuntu. here's the code:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
int main() {
pid_t pid = fork();
if (pid == 0) { //Child
int shm_fd = shm_open("JSHM", O_CREAT | O_RDWR, 0666);
ftruncate(shm_fd, 4096);
void *ptr = mmap(0, 4096, PROT_WRITE, MAP_SHARED, shm_fd, 0);
sprintf(ptr, "Jani");
printf("%i descriptor from child\n", shm_fd);
printf("%p ptr from child\n\n", ptr);
}
if (pid > 0) { //Parent
sleep(5);
int shm_fd = shm_open("JSHM", O_RDONLY, 0666);
printf("%i descriptor from parent\n", shm_fd);
void *ptr = mmap(0, 4096, PROT_WRITE, MAP_SHARED, shm_fd, 0);
//printf("%s", (char *)ptr);
printf("%p ptr from parent\n", ptr);
}
}
And the output:
3 descriptor from child
0xb7750000 ptr from child
3 descriptor from parent
0xffffffff ptr from parent
why doesn't second mmap (from parent) work?
Thanks.

You have a slight error in your requested access permissions. In the parent section, you're passing O_RDONLY to shm_open() but then you're trying to mmap() it with PROT_WRITE which is not allowed.

Related

Why mmap throws error while using MAP_FIXED?

Using Ubuntu 16.04, kernel 4.17.4
I am trying the following code where I have defined a shared memory and mapped current process's address space to the shared memory. When I use MAP_FIXED in mmap() it shows:
mmap: Invalid argument
The code is as follows:
#include <stdio.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define STORAGE_ID "/SHM_TEST"
#define STORAGE_SIZE 4096
int main(int argc, char *argv[])
{
int res, temp;
int fd;
int len;
pid_t pid;
void *addr;
char data[STORAGE_SIZE];
fd = shm_open(STORAGE_ID, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if (fd == -1)
{
perror("open");
return 10;
}
res = ftruncate(fd, STORAGE_SIZE);
if (res == -1)
{
perror("ftruncate");
return 20;
}
addr = mmap(NULL, STORAGE_SIZE, PROT_WRITE, MAP_FIXED, fd, 0);
if (addr == MAP_FAILED)
{
perror("mmap");
return 30;
}
res = munmap(addr, STORAGE_SIZE);
if (res == -1)
{
perror("munmap");
return 40;
}
// shm_open cleanup
fd = shm_unlink(STORAGE_ID);
if (fd == -1)
{
perror("unlink");
return 100;
}
return 0;
}
The reason I am using MAP_FIXED is I will manually put an address instead of NULL (tried this and got same error). What do I need to do to use MAP_FIXED with mmap()?

How to delete/release a named semaphore from linux command prompt?

I have a code as under.It crashes when accessing the semaphore and I have created "named semaphores" that I am unable to delete from command prompt after the crash. How can I delete them using a command prompt utility? The code is trying to create Rock,Paper,Scissors game that can be run from 2 producer(player) command prompts and one consumer (result) command prompt. I would also appreciate suggestions to fix/correct/improve the code.
//compiled by executing --> gcc -pthread logic.c -lrt
#include <stdio.h>
#include <sys/shm.h>
#include <semaphore.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <sys/mman.h>
//don't need an exec call just based on paramters run the processes
typedef struct sync_tools
{
int pid;
void* shm_ptr;
sem_t* sem_array[2];
} sync_tools;
void producer(void * ctx)
{
char choice;
sync_tools *ctxt=(sync_tools*)ctx;
while(1)
{
printf ("Enter the following for input \n");
printf ("R. Rock\n");
printf ("P. Paper\n");
printf ("S. Scissors\n");
scanf ("%c",&choice);
if (ctxt->pid==1)
{
printf ("Process1\n");
sem_wait(ctxt->sem_array[0]);
sprintf((char*) ctxt->shm_ptr, "%c\n", choice);
sem_post(ctxt->sem_array[0]);
printf ("Process1\n");
}
else
{
printf ("Process2\n");
sem_wait(ctxt->sem_array[1]);
sprintf((char*) (ctxt->shm_ptr+1), "%c\n", choice);
sem_post(ctxt->sem_array[1]);
}
}
}
//need 2 semphores for sync
void consumer(void *ctx)
{
sync_tools *ctxt=(sync_tools*)ctx;
char data[2]={0x00};
int flag=1;
while(1)
{
sem_wait(ctxt->sem_array[0]);
sem_wait(ctxt->sem_array[1]);
scanf ((char *) ctxt->shm_ptr, "%c", &data[0]);
scanf ((char *) (ctxt->shm_ptr+1), "%c", &data[1]);
switch(data[0])
{
case 'R':
case 'r':
if ((data[1] =='p')|| (data[1]=='P'));
flag=0;
break;
case'P':
case'p':
if ((data[1] =='s')|| (data[1]=='S'));
flag=0;
break;
case 's':
case'S':
if ((data[1] =='R')|| (data[1]=='R'));
flag=0;
break;
}
if (flag)
printf("Process 1 wins \n");
else
printf("Process 2 wins \n");
sem_post(ctxt->sem_array[0]);
sem_post(ctxt->sem_array[1]);
}
}
int main (int argc, char* argv[])
{
char choice;
int SIZE=4096;
char *name="SHM_WQ";
sync_tools cntxt={}; //initialize without memset
//calling sem_open as I want a named semaphore which is not locally available by copy like in forked processes.
cntxt.sem_array[0]= sem_open ("P1C", O_CREAT | O_EXCL, 0644, 1);
cntxt.sem_array[1]= sem_open ("P2C", O_CREAT | O_EXCL, 0644, 1);
/* shared memory file descriptor */
int shm_fd;
/* pointer to shared memory object */
void* ptr;
/* create the shared memory object */
shm_fd = shm_open(name, O_CREAT | O_RDWR, 0666);
/* configure the size of the shared memory object */
ftruncate(shm_fd, SIZE);
/* memory map the shared memory object */
ptr = mmap(0, SIZE, PROT_WRITE, MAP_SHARED, shm_fd, 0); //sys/mman.h
cntxt.shm_ptr=ptr;
//pass the shared memory and semaphores to all the threads and spawn them
if(strcmp(argv[1], "p1") == 0)
cntxt.pid=1;
else if (strcmp(argv[1], "p2") ==0)
cntxt.pid=2;
else if (strcmp(argv[1], "c") ==0)
cntxt.pid=3;//don't care
producer(&cntxt);
consumer(&cntxt);
}
Modified my code as under:
cntxt.sem_array[0] = sem_open("P1C", 0);
cntxt.sem_array[1] = sem_open("P2C", 0);
if (cntxt.sem_array[0] == NULL)
{
cntxt.sem_array[0]= sem_open ("P1C", O_CREAT | O_EXCL, 0644, 2);
}
if (cntxt.sem_array[1] == NULL)
{
cntxt.sem_array[1]= sem_open ("P2C", O_CREAT | O_EXCL, 0644, 2);
}
if ((cntxt.sem_array[0] == NULL) || (cntxt.sem_array[1] == NULL))
printf("SEM_OPEN ERROR");
Also as mentioned in the comment, created a small utility that takes semname and unlinks it from kernel.

How to get memory address from shm_open?

I want to share memory using a file descriptor with another process created via fork.
The problem is that I get different address regions from mmap.
I want that mmap returns the same address value. Only in such case I can be sure that I really share the memory.
Probably it is possible to use MAP_FIXED flag to mmap, but how to get memory address from shm_open?
Is it possible to share memory via shm_open at all?
Maybe shmget must be used instead?
This is the minimal working example:
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
int main(void)
{
/* Create a new memory object */
int fd = shm_open( "/dummy", O_RDWR | O_CREAT, 0777 );
if(fd == -1) {
fprintf(stderr, "Open failed:%m\n");
return 1;
}
/* Set the memory object's size */
size_t size = 4096; /* minimal */
if (ftruncate(fd, size) == -1) {
fprintf(stderr, "ftruncate: %m\n");
return 1;
}
void *ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (ptr == MAP_FAILED) return 1;
void *ptr2 = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (ptr2 == MAP_FAILED) return 1;
printf("%p\n%p\n", ptr, ptr2);
return 0;
}
Compile it with gcc test.c -lrt.
This is the output:
0x7f3247a78000
0x7f3247a70000
EDIT
If I try to use method described in comment, child does not see changes in memory made by parent. Why? This is how I do it:
In parent:
shm_data = mmap(NULL, shm_size, PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
...
snprintf(shared_memory, 20, "%p", shm_data);
execl("/path/to/prog", "prog", shared_memory, (char *) NULL);
In child:
...
void *base_addr;
sscanf(argv[1], "%p", (void **)&base_addr);
shm_data = mmap(base_addr, shm_size, PROT_READ, MAP_ANONYMOUS | MAP_SHARED | MAP_FIXED, -1, 0);
...
EDIT2
See also this question: How to get memory address from memfd_create?

server only accept once and terminate

I'm learning Unix Network Programming in Chapter5, wait and waitpid functions. I test function wait. I run the server application firstly and then the client(repeat ./a.out several times). But the server can only accept the request one time and terminated. Errno is 4.
/* server.c
*
* gcc server.c -o server
* ./server & (run in background)
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <strings.h>
#include <signal.h>
void sig_chld(int signum) // SIGCHLD handler
{
int stat;
pid_t pid;
pid = wait(&stat);
}
int main(void)
{
int listenfd;
int connfd;
struct sigaction act;
struct sockaddr_in addr;
socklen_t addrlen;
pid_t pid;
act.sa_handler = sig_chld; // register SIGCHLD handler
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGCHLD, &act, NULL);
addrlen = sizeof(addr);
bzero(&addr, addrlen); // fill server address
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
inet_pton(AF_INET, "192.168.0.10", &addr.sin_addr.s_addr);
listenfd = socket(AF_INET, SOCK_STREAM, 0);
bind(listenfd, (struct sockaddr *)&addr, addrlen);
listen(listenfd, 5);
while (1) { // waiting for client request
connfd = accept(listenfd, (struct sockaddr *)&addr, &addrlen);
if (connfd < 0) {
printf("connect\n");
break;
}
pid = fork();
if (pid < 0) {
exit(-1);
} else if (pid == 0) { // child
close(listenfd);
write(connfd, "hello\n", 7);
exit(0);
}
else { // parent
close(connfd);
}
}
return 0;
}
/* client.c
*
* gcc client.c
* ./a.out (repeat several times)
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <strings.h>
#include <errno.h>
#define MAXLINE 4096
int main(void)
{
char buf[1024];
int cliefd;
struct sockaddr_in servaddr;
socklen_t addrlen;
addrlen = sizeof(servaddr);
bzero(&servaddr, addrlen); // fill server address
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(8080);
inet_pton(AF_INET, "192.168.0.10", &servaddr.sin_addr.s_addr);
cliefd = socket(AF_INET, SOCK_STREAM, 0);
if (connect(cliefd, (struct sockaddr *)&servaddr, addrlen) < 0) {
printf("error: %d\n", errno);
exit(-1);
}
read(cliefd, buf, MAXLINE);
fputs(buf, stdout);
return 0;
}
errno value 4 is EINTR. This indicates that a system call was interrupted. In this case, the SIGCHLD is interrupting the accept system call. Keep reading further in that chapter. Below is a quote from it which specifically points that out for this particular example code:
Since the signal was caught by the parent while the parent was blocked in a slow system call (accept), the kernel causes accept to return an error of EINTR (interrupted system call). The parent does not handle this error so it aborts.
The purpose of this example is to show that when writing network programs that catch signals, we must be cognizant of interrupted system calls, and we must handle them.
It goes on further to explain how the signal can be set up to automatically restart interrupted system calls. In summary, set the SA_RESTART flag in the act.sa_flags field:
act.sa_flags |= SA_RESTART;

Shared variable between different executables in Linux

What I want to do is to create a globally shared variable to be accessed by different processes. I want the child process to be replaced by an existing executable.
UPDATE: I think this is the solution. The code is borrowed from here. But since every process needs at least one I/O operation to mmap the file, is there any faster approach?
mycode.h
static void* addr; //static
app1.cc
include
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/stat.h>
int main(void)
{
size_t length = 1024 * 1024;
off_t offset = 0;
int prot = (PROT_READ| PROT_WRITE);
int flags = MAP_SHARED;
int fd = -1;
fd = open("./jim.mymemory", O_RDWR| O_CREAT, S_IRUSR| S_IWUSR );
if (fd == 0) {
int myerr = errno;
printf("ERROR: open failed (errno %d %s)\n", myerr, strerror(myerr));
return EXIT_FAILURE;
}
addr = mmap(NULL, length, prot, flags, fd, offset);
if (addr == 0) {
int myerr = errno;
printf("ERROR (child): mmap failed (errno %d %s)\n", myerr,
strerror(myerr));
}
*((int *) addr)=5;
if (munmap(addr, length) == -1) {
int myerr = errno;
printf("ERROR (child): munmap failed (errno %d %s)\n", myerr,
strerror(myerr));
}
return 0;
}
mycode.cc
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include "mycode.h"
int main(void) {
size_t length = 1024 * 1024;
off_t offset = 0;
int prot = (PROT_READ| PROT_WRITE);
int flags = MAP_SHARED;
int fd = -1;
pid_t pid;
fd = open("./jim.mymemory", O_RDWR| O_CREAT, S_IRUSR| S_IWUSR );
if (fd == 0) {
int myerr = errno;
printf("ERROR: open failed (errno %d %s)\n", myerr, strerror(myerr));
return EXIT_FAILURE;
}
if (lseek(fd, length - 1, SEEK_SET) == -1) {
int myerr = errno;
printf("ERROR: lseek failed (errno %d %s)\n", myerr, strerror(myerr));
return EXIT_FAILURE;
}
write(fd, "", 1);
if ((pid = fork()) == 0) { // child
/*Child process*/
printf("INFO (child): start \n");
execv("./app1", NULL); // **app1**
printf("INFO (child): done \n");
msync(addr,sizeof(int),MS_SYNC|MS_INVALIDATE); // can be commented out, since we wait in the parent process
} else {
/*Parent process*/
unsigned int readval = 0;
addr = mmap(NULL, length, prot, flags, fd, offset);
if (addr == 0) {
int myerr = errno;
printf("ERROR (parent): mmap failed (errno %d %s)\n", myerr,
strerror(myerr));
}
printf("INFO (parent): start read\n");
wait(NULL);
readval = *((int *) addr);
printf("val: %d \n", readval);
printf("INFO (parent): done read\n");
if (munmap(addr, length) == -1) {
int myerr = errno;
printf("ERROR (parent): munmap failed (errno %d %s)\n", myerr,
strerror(myerr));
}
}
if (close(fd) == -1) {
int myerr = errno;
printf("ERROR: close failed (errno %d %s)\n", myerr, strerror(myerr));
}
unlink ("./jim.mymemory");
return EXIT_SUCCESS;
}
Any help is appreciated.
The execve will drop all mappings in the kernel, so this technique will not work. What you can do instead is open a file (as in Vaughn's suggestion) and pass the descriptor to the child process. Open file descriptors are unchanged across an exec. Then you can map it in the child. Alternatively, investigate APIs like shm_open()/shm_unlink() which will manage a global file mapping such that other processes can use it, not just a child.
But basically: you have to mmap() in the child, you can't pass anything in your address space to the child in Unix.

Resources