For the application I'm developing (under Linux, but I'm trying to maintain portability) I need to switch to shared memory for sharing data across different processes (and threads inside processes). There is a father process generating different children
I need for example to get every process able to increment a shared counter using a named semaphore.
In this case everything is ok:
#include <sys/mman.h>
#include <sys/wait.h>
#include <semaphore.h>
#include <fcntl.h>
#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;
#define SEM_NAME "/mysem"
#define SM_NAME "tmp_sm.txt"
int main(){
int fd, nloop, counter_reset;
int *smo;
sem_t *mutex;
nloop = 100;
counter_reset = 1000;
if (fork() == 0) {
/* child */
/* create, initialize, and unlink semaphore */
mutex = sem_open(SEM_NAME, O_CREAT, 0777, 1);
//sem_unlink(SEM_NAME);
/* open file, initialize to 0, map into memory */
fd = open(SM_NAME, O_RDWR | O_CREAT);
smo = (int *) mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
close(fd);
/* INCREMENT */
for (int i = 0; i < nloop; i++) {
sem_wait(mutex);
cout << "child: " << (*smo)++ << endl;
if(*smo>=counter_reset){
(*smo)=0;
}
sem_post(mutex);
}
exit(0);
}
/* parent */
/* create, initialize, and unlink semaphore */
mutex = sem_open(SEM_NAME, O_CREAT, 0777, 1);
sem_unlink(SEM_NAME);
/* open file, initialize to 0, map into memory */
fd = open(SM_NAME, O_RDWR | O_CREAT);
smo = (int *) mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
close(fd);
/* INCREMENT */
for (int i = 0; i < nloop; i++) {
sem_wait(mutex);
cout << "parent: " << (*smo)++ << endl;
if(*smo>=counter_reset){
(*smo)=0;
}
sem_post(mutex);
}
exit(0);
}
So far so good: both semaphore and shared counter are ok (same address in memory) and increment and reset work fine.
The program fails simply by moving child source code into a new source file invoked by exec. Shared memory and named semaphore addresses are different therefore increment fails.
Any suggestion? I used named semaphores and named shared memory (using a file) to try to get the same pointer values.
UPDATE:
as requested by Joachim Pileborg, this is the "server side" improvements respect above original code:
...
if (fork() == 0) {
/* child */
/*spawn child by execl*/
char cmd[] = "/path_to_bin/client";
execl(cmd, cmd, (char *)0);
cerr << "error while istantiating new process" << endl;
exit(EXIT_FAILURE);
}
...
And this is the "client" source code:
#include <sys/mman.h>
#include <sys/wait.h>
#include <semaphore.h>
#include <fcntl.h>
#include <iostream>
#include <stdlib.h>
using namespace std;
#define SEM_NAME "/mysem"
#define SM_NAME "tmp_ssm.txt"
int main(){
int nloop, counter_reset;
int *smo;
sem_t *mutex;
/* create, initialize, and unlink semaphore */
mutex = sem_open(SEM_NAME, O_CREAT, 0777, 1);
//sem_unlink(SEM_NAME);
/* open file, initialize to 0, map into memory */
int fd = open(SM_NAME, O_RDWR | O_CREAT);
smo = (int *) mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
close(fd);
nloop=100;
counter_reset=1000;
/* INCREMENT */
for (int i = 0; i < nloop; i++) {
sem_wait(mutex);
cout << "child: " << (*smo)++ << endl;
if(*smo>=counter_reset){
(*smo)=0;
}
sem_post(mutex);
}
exit(0);
}
executing this code cause the process to block (deadlock) and waiting for an infinite time. looking at addresses they are tipically found to be:
father semaphore: 0x7f2fe1813000
child semahpore: 0x7f0c4c793000
father shared memory: 0x7f2fe1811000
child shared memory: 0x7ffd175cb000
removing 'sem_post' and 'sem_wait' everything is fine but I need mutual exlusion while incrementing...
Don't unlink the semaphore. it actually removes the semaphore.
From the sem_unlink manual page:
sem_unlink() removes the named semaphore referred to by name. The semaphore name is removed immediately. The semaphore is destroyed once all other processes that have the semaphore open close it.
This means that once you've created the semaphore in the parent process, you immediately remove it. The child process then will not be able to find the semaphore, and instead creates a new one.
Related
I have 2 processes:
producer: creates an Transaction object inside a shared memory then waiting for consumer to read the data. The waiting is done via a semaphore which is a member of Transaction
consumer: reads the Transaction object created by producer and tells producer the reading is done by posting semaphore in Transaction object.
Consumer is able to read the data from shared memory but it crashes when it tries to post semaphore.
What's wrong with this implementation?
//producer
#include <string>
#include <semaphore.h>
#include <cstddef>
#include <sys/mman.h>
#include <sys/stat.h> /* For mode constants */
#include <fcntl.h> /* For O_* constants */
#include <unistd.h>
#include <iostream>
constexpr char g_shared_mem_name[]="/our_shared_mem_new";
class Transaction
{
public:
virtual ~Transaction() = default;
Transaction(const uint32_t f_count) : m_count{f_count}{}
sem_t m_sem;
uint32_t m_count{0};
};
void* createSharedMem(const char *f_name, const size_t f_size) {
int fd = shm_open(f_name, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
if (fd != -1) {
// A new shared memory object initially has zero length.
// The size of the object can be set using ftruncate.
// The newly allocated bytes of a shared memory object are
// automatically initialized to 0
if (ftruncate(fd, f_size) == -1) {
return nullptr;
}
return mmap(NULL, f_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 );
}
else {
return nullptr;
}
}
int main()
{
void * shared_mem = createSharedMem(g_shared_mem_name, sizeof(Transaction));
if ( shared_mem )
{
Transaction* shared_obj = new(shared_mem) Transaction(2022);
if( sem_init(&shared_obj->m_sem,1,0) == -1) {
std::cerr << "Unable to initialize m_sem_init\n";
}
//waiting for consumer to finish reading data
if( sem_wait(&shared_obj->m_sem) == -1) {
std::cerr << "Error on waiting for semaphore\n";
}
}
else {
std::cerr << "Unable to create shared object\n";
}
shm_unlink(g_shared_mem_name);
return EXIT_SUCCESS;
}
Consumer
#include <string>
#include <semaphore.h>
#include <cstddef>
#include <sys/mman.h>
#include <sys/stat.h> /* For mode constants */
#include <fcntl.h> /* For O_* constants */
#include <unistd.h>
#include <iostream>
constexpr char g_shared_mem_name[]="/our_shared_mem_new";
class Transaction
{
public:
virtual ~Transaction() = default;
Transaction(const uint32_t f_count) : m_count{f_count}{}
sem_t m_sem;
uint32_t m_count{0};
};
void* openSharedMem(const char * f_name, const size_t f_size) {
int fd = shm_open(f_name, O_RDONLY, 0);
if( fd != -1 ) {
return mmap(NULL, f_size, PROT_READ, MAP_SHARED, fd, 0 );
}
else {
return nullptr;
}
}
int main()
{
void* shared_mem = openSharedMem(g_shared_mem_name, sizeof(Transaction));
if (shared_mem) {
Transaction * m_inst = (Transaction*)shared_mem;
std::cout << "value of cnt:" << m_inst->m_count << std::endl;
//crash here
if (sem_post(&m_inst->m_sem) == -1) {
std::cerr << "Unable to post semaphore\n";
}
}
else {
std::cerr << "Unable to open shared object\n";
}
return EXIT_SUCCESS;
}
Output
value of cnt:2022
Segmentation fault (core dumped)
I plan on rewriting this to assembly so I can't use c or c++ standard library. The code below runs perfectly. However I want a thread instead of a second process. If you uncomment /*CLONE_THREAD|*/ on line 25 waitpid will return -1. I would like to have a blocking function that will resume when my thread is complete. I couldn't figure out by looking at the man pages what it expects me to do
#include <sys/wait.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
int globalValue=0;
static int childFunc(void*arg)
{
printf("Global value is %d\n", globalValue);
globalValue += *(int*)&arg;
return 31;
}
int main(int argc, char *argv[])
{
auto stack_size = 1024 * 1024;
auto stack = (char*)mmap(NULL, stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
if (stack == MAP_FAILED) { perror("mmap"); exit(EXIT_FAILURE); }
globalValue = 5;
auto pid = clone(childFunc, stack + stack_size, /*CLONE_THREAD|*/CLONE_VM|CLONE_SIGHAND|SIGCHLD, (void*)7);
sleep(1); //So main and child printf don't collide
if (pid == -1) { perror("clone"); exit(EXIT_FAILURE); }
printf("clone() returned %d\n", pid);
int status;
int waitVal = waitpid(-1, &status, __WALL);
printf("Expecting 12 got %d. Expecting 31 got %d. ID=%d\n", globalValue, WEXITSTATUS(status), waitVal);
return 0;
}
If you want to call functions asynchronously with threads I recommend using std::async. Example here :
#include <iostream>
#include <future>
#include <mutex>
#include <condition_variable>
int globalValue = 0; // could also have been std::atomic<int> but I choose a mutex (to also serialize output to std::cout)
std::mutex mtx; // to protect access to data in multithreaded applications you can use mutexes
int childFunc(const int value)
{
std::unique_lock<std::mutex> lock(mtx);
globalValue = value;
std::cout << "Global value set to " << globalValue << "\n";
return 31;
}
int getValue()
{
std::unique_lock<std::mutex> lock(mtx);
return globalValue;
}
int main(int argc, char* argv[])
{
// shared memory stuff is not needed for threads
// launch childFunc asynchronously
// using a lambda function : https://en.cppreference.com/w/cpp/language/lambda
// to call a function asynchronously : https://en.cppreference.com/w/cpp/thread/async
// note I didn't ues the C++ thread class, it can launch things asynchronously
// however async is both a better abstraction and you can return values (and exceptions)
// to the calling thread if you need to (which you do in this case)
std::future<int> future = std::async(std::launch::async, []
{
return childFunc(12);
});
// wait until asynchronous function call is complete
// and get its return value;
int value_from_async = future.get();
std::cout << "Expected global value 12, value = " << getValue() << "\n";
std::cout << "Expected return value from asynchronous process is 31, value = " << value_from_async << "\n";
return 0;
}
I've been attempting to solve the longest common subsequence problem using multiprocessing and multi-threading, and I have implemented a multiprocess version of the code, using the usual dynamic programming approach: generate a score matrix, each element depends on the one to its left, north-west and directly above.
In my multiprocess approach, I have adopted propagating the wavefront along the anti-diagonals of the score matrix, and to make life easy, I have performed a shear transform on said score matrix, so that each antidiagonal is now horizontal (this is for improved memory access):
Following is my code (admittedly rather long, which allows for some set-up):
#include <algorithm>
#include <atomic>
#include <cstring>
#include <fcntl.h>
#include <fstream>
#include <iostream>
#include <string>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <x86intrin.h>
#define LOGICAL_CORES (int) sysconf(_SC_NPROCESSORS_CONF) /* Number of
logical cores on system: indirectly determines number of processes run */
#define MAX_WORK_SIZE 256 /* Maximum amount
of work for each worker process */
#define NUM_ANTIDIAGS (X + Y + 1) /* Number of anti-
diagonals to process */
#define ANTIDIAG_SIZE std::max(X, Y) /* Length of each
anti-diagonal of the score matrix */
#define ANTIDIAG_REAL_SIZE ((ANTIDIAG_SIZE + MAX_WORK_SIZE - 1) & -MAX_WORK_SIZE) /* Length of each
anti-diagonal in memory: a multiple of MAX_WORKER_SIZE */
#define NUM_WORKERS (ANTIDIAG_REAL_SIZE / MAX_WORK_SIZE) /* Total number of
worker processes */
// The sizes of the input strings
u_int32_t X, Y;
u_int32_t *back; /* The back anti-diagonal, read from */
u_int32_t *middle; /* The middle antidiagonal, read from */
u_int32_t *front; /* The front antidiagonal, written to */
struct sync_container {
pthread_barrier_t barrier; /* A barrier, to ensure all threads are synchronised */
pthread_barrierattr_t barrierattr; /* Barrier attributes */
std::vector<pid_t> pids; /* A list of process IDs of the worker processes */
};
u_int32_t *data;
sync_container *sync_data;
std::string seq_1;
std::string seq_2;
void read_files(std::ifstream f1, std::ifstream f2)
{
if (f1.fail() || f2.fail())
{
std::cout << "Error reading files; exiting." << std::endl;
exit(EXIT_FAILURE);
}
f1 >> X >> seq_1;
f1.close();
f2 >> Y >> seq_2;
f2.close();
}
void shm_setup()
{
data = reinterpret_cast<u_int32_t *>(mmap(nullptr, 4 * ANTIDIAG_REAL_SIZE * sizeof(uint32_t),
PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, 0, 0));
back = reinterpret_cast<u_int32_t *>(data);
middle = back + ANTIDIAG_REAL_SIZE;
front = middle + ANTIDIAG_REAL_SIZE;
memset(back, 0, ANTIDIAG_REAL_SIZE * sizeof(u_int32_t));
memset(middle, 0, ANTIDIAG_REAL_SIZE * sizeof(u_int32_t));
memset(front, 0, ANTIDIAG_REAL_SIZE * sizeof(u_int32_t));
sync_data = static_cast<sync_container *>(mmap(nullptr, sizeof(sync_container),
PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, 0, 0));
}
void cleanup()
{
munmap(data, 3 * ANTIDIAG_REAL_SIZE * sizeof(u_int32_t));
munmap(sync_data, sizeof(sync_container));
exit(0);
}
int main(int argc, char **argv)
{
if (argc != 3)
{
std::cout << "Usage: [executable] [file 1] [file 2]" << std::endl;
return 1;
}
read_files(std::ifstream(argv[1], std::ifstream::in),
std::ifstream(argv[2], std::ifstream::in));
// Initialise shared memory and arrays
shm_setup();
// Initialise barrier
pthread_barrierattr_init(&sync_data->barrierattr);
pthread_barrierattr_setpshared(&sync_data->barrierattr, PTHREAD_PROCESS_SHARED);
pthread_barrier_init(&sync_data->barrier, &sync_data->barrierattr, NUM_WORKERS + 1);
int pid = 0;
int worker_id = 0;
for (; worker_id < NUM_WORKERS; ++worker_id)
{
pid = fork();
if (pid) sync_data->pids[worker_id] = pid;
else
break;
}
pthread_barrier_wait(&sync_data->barrier);
for (int antidiag_idx = 2; antidiag_idx < NUM_ANTIDIAGS; ++antidiag_idx)
{
pthread_barrier_wait(&sync_data->barrier);
if (!pid) // worker processes go here
{
for (int element = MAX_WORK_SIZE * worker_id; element < (antidiag_idx * worker_id) + MAX_WORK_SIZE; ++element)
{
if (!element || element >= ANTIDIAG_SIZE) continue;
char vert = seq_1[antidiag_idx - 1 - element];
char horz = seq_2[element - 1];
front[element] = horz == vert ? back[element - 1] + 1
: std::max(middle[element - 1], middle[element]);
}
}
if (pid) // parent process moves pointers
{
back = middle;
middle = front;
front = back;
}
pthread_barrier_wait(&sync_data->barrier);
}
if (!pid) exit(0);
std::cout << middle[ANTIDIAG_SIZE] << std::endl;
cleanup();
}
Now, this code does not work. This is strange, because with a small input size (specifically, < 256), this code only spawns one worker process and one parent process to manage it, and it still fails.
However, when the fork(), various pthread_barrier_wait() calls, and if (pid) control flow paths are removed in the for loop, the code executes perfectly and returns the correct expected length of the LCS between two strings specified in the input files. In other words, it degenerates into effectively a single-threaded, single-process version of the dynamic programming solution, but with the shear transform thing.
There is clearly an issue with my synchronisation, and I can't figure out where it is. I've tried several permutations of adding more pthread_barrier_wait()s, but this hasn't led anywhere.
Where is the synch issue, and how may I fix it?
I'm trying to write a writer's preference code to prevent a writer from being starved in the event that it's in a queue and readers skip it due to their priority. The counter checking how many readers have read is protected by a semaphore (readerCount), a try semaphore is used to indicate a reader is trying to enter (psembufT), and the resource semaphore (psembufF).
I need to write to a text file (code written) in one Terminal window and read from the other in another window, whenever I try to read i get Segmentation fault [core dumped] error.
#include <iostream>
#include <fstream>
#include <string>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <inttypes.h>
using namespace std;
#define SHM_KEY 9876
#define SEMKEY 1234
struct sembuf vsembufR, psembufR, vsembufW, psembufW;
struct sembuf vsembufF, psembufF, vsembufT, psembufT;
int main()
{
union semun{
int val;
struct semid_ds *buf;
ushort myArray[0];
} arg;
string op;
ifstream myFile; // makes an ifstream object to read from myFile
int shmid = shmget(SHM_KEY, 256, 0777|IPC_CREAT);
int *readerCount = (int*)shmat(shmid, 0, 0);
int semid = semget(SEMKEY, 2, 0777|IPC_CREAT); // Creates two semaphores
int pause;
readerCount = 0;
psembufR.sem_num=0; // init reader mutex members
psembufR.sem_op=-1;
psembufR.sem_flg=SEM_UNDO;
vsembufR.sem_num=0;
vsembufR.sem_op=1;
vsembufR.sem_flg=SEM_UNDO;
psembufF.sem_num=1; // resource
psembufF.sem_op=-1;
psembufF.sem_flg=SEM_UNDO;
vsembufF.sem_num=1;
vsembufF.sem_op=1;
vsembufF.sem_flg=SEM_UNDO;
psembufW.sem_num=0; // writer
psembufW.sem_op=-1;
psembufW.sem_flg=SEM_UNDO;
vsembufW.sem_num=0;
vsembufW.sem_op=1;
vsembufW.sem_flg=SEM_UNDO;
psembufT.sem_num=1;
psembufT.sem_op=-1;
psembufT.sem_flg=SEM_UNDO;
vsembufT.sem_num=1;
vsembufT.sem_op=1;
vsembufT.sem_flg=SEM_UNDO;
arg.val = 1;
semctl(semid, 0, SETVAL, arg);
semctl(semid, 1, SETVAL, arg);
while(1){
cout << "Reader1:\n";
pause = getchar();
semop(semid, &psembufT, 1);
semop(semid, &psembufR, 1);
cout << "count inc" << endl;
*readerCount++;
if(*readerCount == 1) // is this first reader
semop(semid, &psembufF, 1); // lok resource from writers if 1st reader
semop(semid, &vsembufR, 1); // unlock reader mutex (for other readers)
semop(semid, &vsembufT, 1); // unlock try mutex (done accessing file)
// Critical Section
myFile.open ("myFile.txt", ios::out | ios::app); // ::app appends the myFile (new line)
if(myFile.is_open()){
while(getline(myFile, op)){
cout << op << endl; // reads
}
myFile.close();
}
semop(semid, &psembufR, 1); // lock reader mutex (avoid race)
*readerCount--;
if(*readerCount == 0) // is this the last reader
semop(semid, &vsembufF, 1); // unlock resource
semop(semid, &vsembufR, 1); // unlock reader mutex
}
I think it's something to do with how I declared readerCount but I can't figure it out.
readerCount = 0; should be *readerCount = 0;
readerCount is an int* so when you do readerCount = 0; you set that pointer to point at address zero. When you later try to update the value at that address, you are most likely to get crashes.
I am trying to synchronize a father and children, the following code is not working (apparently usr_interrupt++ is not atomic). Semaphores does not seems to help either.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <unistd.h>
#include <cstdlib>
#include <iostream>
#include <unistd.h>
#include <cstring>
#include <string>
#include <semaphore.h>
#include <fcntl.h>
using namespace std;
/* When a SIGUSR1 signal arrives, set this variable. */
volatile sig_atomic_t usr_interrupt;
sem_t *mutex;
char* SEM_NAME;
void
synch_signal (int sig)
{
// sem_wait(mutex);
usr_interrupt++;
// sem_post(mutex);
}
/* The child process executes this function. */
void
child_function (void)
{
/* Perform initialization. */
cerr << "I'm here!!! My pid is " << (int)getpid() << " my usr_int=" << usr_interrupt << endl;
/* Let parent know you're done. */
kill (getppid (), SIGUSR1);
/* Continue with execution. */
cerr << "Bye, now...." << endl;
exit(0);
}
int
main (void)
{
usr_interrupt = 0;
string s_sem_name = "lir";
SEM_NAME = new char[s_sem_name.size()+1];
memcpy(SEM_NAME, s_sem_name.c_str(), s_sem_name.size());
SEM_NAME[s_sem_name.size()] = '\0';
mutex = sem_open (SEM_NAME,O_CREAT,0644,1);
if(mutex == SEM_FAILED) {
perror("unable to create semaphore");
sem_unlink(SEM_NAME);
exit(-1);
}
struct sigaction usr_action;
sigset_t mask, oldmask;
pid_t child_id, child_id2;
/* Set up the mask of signals to temporarily block. */
sigemptyset (&mask);
sigaddset (&mask, SIGUSR1);
/* Establish the signal handler.*/
usr_action.sa_handler = synch_signal;
usr_action.sa_flags = 0;
sigaction (SIGUSR1, &usr_action, NULL);
/* Create the 2 children processes. */
child_id = fork ();
if (child_id == 0)
child_function ();
child_id2 = fork();
if (child_id2 == 0)
child_function ();
/* Wait for a signal to arrive. */
sigprocmask (SIG_BLOCK, &mask, &oldmask);
while (usr_interrupt != 2) {
sigsuspend (&oldmask);
}
sigprocmask (SIG_UNBLOCK, &mask, NULL);
/* Now continue execution. */
puts ("That's all, folks!");
return 0;
}
Can anyone suggest a fix? (I cannot use threads)
Best,
-- Liron
You can't count signals. Two signals of the same type has the same semantic meaning as one signal of that type. You could use two different signal types like USR1 and USR2. But honestly, you shouldn't use signals as a communication mechanism. Use something sensible like a pipe.