Creating Multiple Processes and Communicating with Pipes in C++ - c++

I am attempting to create two child processes with a shared parent, the parent must print the output of each of the child processes, It sends a list of numbers to the Child A, child A sorts these numbers, and sends the sorted list to both the parent and child B, child B then finds the median of the list of numbers, and sends it to the parent where it prints both results.
The problem I'm running into is that it executes fine for Child A and prints child A's results, but does not print anything for child b.
#include <string>
#include <vector>
#include <cstdlib>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <bits/stdc++.h>
using namespace std;
int main(int argc, char **argv)
{
vector<int> numbers;
vector<int> sortedVector;
if (argc == 6)
{
//Create vector
numbers.push_back(atoi(argv[1]));
numbers.push_back(atoi(argv[2]));
numbers.push_back(atoi(argv[3]));
numbers.push_back(atoi(argv[4]));
numbers.push_back(atoi(argv[5]));
}
else
{
cout << "Error\n";
}
int fd[2];
int fd1[2];
if (pipe(fd) == -1 || pipe(fd1) == -1)
{
std::cout << "Pipe failed.\n";
}
int child_a, child_b;
child_a = fork();
//Child A process
if (child_a == 0)
{
//Child A code
sortedVector = numbers;
sort(sortedVector.begin(), sortedVector.end());
int array1[5] = { sortedVector.at(0), sortedVector.at(1), sortedVector.at(2), sortedVector.at(3), sortedVector.at(4) };
//Send to parent
close(fd[0]);
write(fd[1], array1, 5*sizeof(int));
close(fd[1]);
//Send to B
close(fd1[0]);
write(fd1[1], array1, 5*sizeof(int));
close(fd1[1]);
}
else
{
child_b = fork();
if (child_b == 0)
{
//Child B code
cout << "Array from A ";
//Read from A
int arrayFromA[5] = { 0, 0, 0, 0, 0 };
close(fd1[1]);
read(fd1[0], arrayFromA, 5*sizeof(int));
close(fd1[0]);
//Find median
double median;
int arraySize = sizeof(arrayFromA) / sizeof(arrayFromA[0]);
median = (double)(arrayFromA[(arraySize - 1) / 2] + arrayFromA[arraySize / 2]) / 2.0;
//Send to Parent
close(fd1[0]);
write(fd1[1], &median, sizeof(double));
close(fd1[1]);
}
else
{
//Parent code
//Read from Child A
vector<int> vectorFromChild;
close(fd[1]);
int array2[5] = { 0, 0, 0, 0, 0 };
read(fd[0], array2, 5*sizeof(int));
close(fd[0]);
std::cout << "The sorted list: ";
for (int i = 0; i < 5; i++)
{
if (i != 4)
{
std::cout << array2[i] << ", ";
}
else
{
std::cout << array2[i] << ".";
}
}
std::cout << std::endl;
//Read from Child B
double medianOutput;
close(fd1[1]);
read(fd1[0], &medianOutput, sizeof(double));
close(fd1[0]);
std::cout << "The median of the list is: " << medianOutput << std::endl;
wait(NULL);
}
}
}
It won't even print: "The median of list is: "

In Child B code, you close(fd[1]) at line 68, then write to it at line 80: write(fd1[1], &median, sizeof(double));.
UNIX gave us return values for a reason.

Related

Value in shared memory different after shared memory access C++

I am trying to create shared memory, but whenever I access it from a child process its value is different than what it should be. I think that I am using shmget() correctly. I have tried a lot of stuff that I have found online, but I can't find anyone with the same problem I am having. No matter what I enter num as, whenever I try to get l->returnLicense it outputs 0. I'm really at a loss as to what to try next.
#include "license.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <string>
#include <sys/wait.h>
#include <iostream>
#include <unistd.h>
using namespace std;
int validateArguments (int num) {
if (num == -69) {
//no arg
return 10;
}
if (num < 1 || num > 20) {
//warning use 20 as num
return 10;
}
return num;
}
int initSharedMemory (License *l) {
key_t key = ftok("/tmp", 'J');
cout << "key: " << key << endl;
int shmid = shmget(key, sizeof(l), 0666|IPC_CREAT);
if (shmid == -1) {
perror("Shared memory");
return -1;
}
l = (License*)shmat(shmid, (void*)0, 0);
if (l == (void*) -1) {
perror("Shared memory attach");
return -1;
}
return shmid;
}
void detachSharedMemory (License *l) {
shmdt(l);
}
void destroySharedMemory (int shmid) {
shmctl(shmid, IPC_RMID, NULL);
}
void spawn (int shmid) {
pid_t c_pid = fork();
if (c_pid == -1) {
perror("fork");
} else if (c_pid > 0) {
cout << "parent" << shmid << endl;
c_pid = wait(NULL);
} else {
cout << "child" << endl;
License *l;
key_t key = ftok("/tmp", 'J');
cout << "key: " << key << endl;
int shmid = shmget(key, sizeof(l), 0666);
cout << shmid;
l = (License*) shmat(shmid,0,0);
if(l == (void*) -1) {
perror("memory attach");
exit(0);
}
int num = l->returnLicense();
cout << num << "num\n";
shmdt(l);
char* args[] = {"./testChild", NULL};
execvp(args[0],args);
exit(0);
}
}
int main (int argv, char *argc[]) {
int num;
if (argv == 2) {
num = atoi(argc[1]);
} else {
num = -69;
}
num = validateArguments (num);
License *l;
int shmid = initSharedMemory (l);
License *tmp = l;
tmp->initLicense(num);
spawn(shmid);
cout << l->returnLicense() << endl;
detachSharedMemory(l);
destroySharedMemory(shmid);
return 0;
}
I'm not including the entirety of my code, but I think this is enough to illustrate my problem. I copied code from the testChild that I exec from this process so that you can see the problem I'm facing all in one file.
License *l; // uninitialized
int shmid = initSharedMemory (l); // pass l by value, UB!
License *tmp = l; // copy uninitialized pointer, UB!
tmp->initLicense(num); // call member function through uninitialized pointer, BOOM!
spawn(shmid);
cout << l->returnLicense() << endl; // call member function through uninitialized pointer, BOOM!
Probably you meant for your initSharedMemory() function to have a reference-typed parameter, so that it would affect the License * l; variable in main().

dup() creating file but not printing to it

I am trying to create a shell in c++. It creates a child process which executes a command and pipes the response back to the parent. I want to specify if the second argument of a command is -o then I would like to redirect the output of the command to a file. (output.txt).I used dup() to redirect output to my file. However, when I run the program and enter for example wc -o fileName the program creates the file output.txt but does not write to it when I specify to print the result of my child process.
#include <iostream>
#include <unistd.h>
#include <stdio.h>
#include <cstring>
#include <fcntl.h>
#include <vector>
#include <sys/wait.h>
int main(){
// array of file descriptors for parent and child
int filedes[2];
char foo[4096];
char** argv;
std::cout << "$$-> ";
char command[128];
std::cin.getline(command, 128);
if(strlen(command) != 0) {
std::vector<char *> args;
char *prog = strtok(command, " ");
char *tmp = prog;
while(tmp != NULL) {
args.push_back(tmp);
tmp = strtok(NULL, " ");
}
argv = new char *[args.size() + 1];
for (int k = 0; k < args.size(); k++) {
argv[k] = args[k];
}
argv[args.size()] = NULL;
}
char* newargc = argv[0];
char *newargv[] = {newargc,argv[2],NULL};
if(pipe(filedes) < 0){
std::cout << "There was an error creating the pipe";
}
int pid = fork();
if(pid == 0){
// writing to the pipe
// close read end of pipe
close(filedes[0]);
close(STDOUT_FILENO);
dup(filedes[1]);
if(strcmp(argv[1],(char*)"-o") == 0 ||strcmp(argv[1], (char*) "-b") == 0){
execvp(newargv[0], newargv);
}
else{
execvp(argv[0],argv);
}
}
else if (pid > 0) {
std::cout << "This is the parent process\n";
while(wait(NULL) > 0);
close(filedes[1]);
int output_fd = open("output.txt", O_CREAT, O_TRUNC, O_RDWR);
read(filedes[0], foo, sizeof(foo));
if(strcmp(argv[1],(char*)"-o") == 0){
close(STDOUT_FILENO);
dup(output_fd);
write(output_fd, foo, sizeof(foo));
}
else if(strcmp(argv[1], (char*) "-b") == 0){
int stdoutHolder = dup(STDOUT_FILENO);
close(STDOUT_FILENO);
dup(output_fd);
std::cout<< foo;
dup2(stdoutHolder, 1);
}
std::cout << foo;
}
//pid is less than 0 if error
else{
std::cout << "There is an error.";
}
return 0;
}

System V Message Queue Trouble

I am having a bit of trouble getting a System V Message Queue setup and working properly on Linux. The idea is to get a central node to pull data from several other nodes. The trouble is that the central node ends up sitting there waiting for the other nodes to send messages. I have looked at the values for the mailboxes and they are the same across all processes. I.E. 0 for the central mailbox, 32769 for other process 1, ect. I have no idea on why it appears to fail. I have tried to change the priority parameter in msgrcv to 0 to accept all incoming messages and the same issue occurs. Any help would be much appriciated. (Sorry for the lack of comments.)
Here is the code for the central node:
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <iostream>
struct{
long priority;
int temperature;
int pid;
int stable;
} msgp;
const int mainMailID = 8484;
using namespace std;
int main(int argc, char* argv[]){
//declare needed variables
int centralMailID;
int externalMailID[4];
int tempdata;
int externalTempature[4];
int externalTemperatureLast[4];
//set initial values for msgp
msgp.priority = 2;
msgp.temperature = atoi(argv[1]);
msgp.pid = 0;
msgp.stable = 0;
//create the central mailbox
centralMailID = msgget(mainMailID, 0600 | IPC_CREAT);
if(centralMailID == -1){
cout << "Message Queue Creation Failed" << endl;
}
else{
cout << "Message Queue Created" << endl;
}
//create the external mailboxes
for(int i = 0; i < 4 ; i++){
externalMailID[i] = msgget(mainMailID + i+1, 0600 | IPC_CREAT);
if(externalMailID[i] == -1){
cout << "Message Queue " << i << " Creation Failed" << endl;
}
else{
cout << "Message Queue " << i << " Created" << endl;
}
}
printf("%i", externalMailID[0]);
while(msgp.stable == 0){
int centralTemperature = msgp.temperature;
//get the tempatures from the external sensors.
for(int i = 0; i<4; i++){
tempdata = msgrcv(externalMailID[i], &msgp, sizeof(msgp)-sizeof(long), 2, 0);
cout << "Recived data from sensor " << msgp.pid << endl;
externalTempature[i] = msgp.temperature;
}
if(externalTempature[0] == externalTempature[1] == externalTempature[2] == externalTempature[3] == centralTemperature){
msgp.stable = 1;
continue; //could also use break
}
int sum = 0;
for(int i = 0; i<4; i++){
sum = sum + externalTempature[i];
}
centralTemperature = ((2 * centralTemperature) + sum)/6;
msgp.temperature = centralTemperature;
for(int i = 0; i<4; i++){
tempdata = msgsnd(externalMailID[i], &msgp, sizeof(msgp)-sizeof(long), 0);
printf("Sent data to external mailbox %i", i);
}
}
printf("Process ended");
return 0;
}
Here is the code for the other nodes:
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <iostream>
struct{
long priority;
int temperature;
int pid;
int stable;
} msgp;
const int mainMailID = 8484;
using namespace std;
int main(int argc, char* argv[]){
int centralMailID = msgget(mainMailID, 0600 | IPC_CREAT);
int pid = atoi(argv[2]);
int externalMailID = msgget(mainMailID + pid, 0600 | IPC_CREAT);
int externalTemperature = atoi(argv[1]);
int tempdata;
cout << externalMailID << endl;
msgp.priority = 2;
msgp.pid = pid;
msgp.stable = 0;
while(msgp.stable == 0){
msgp.temperature = externalTemperature;
tempdata = msgsnd(centralMailID, &msgp, sizeof(msgp)-sizeof(long), 0);
tempdata = msgrcv(externalMailID, &msgp, sizeof(msgp)-sizeof(long), 2, 0);
externalTemperature = ((externalTemperature * 3) + (msgp.temperature * 2))/5;
if(msgp.stable == 1){
continue;
}
}
printf("Child Process Ended");
return 0;
}
You're using the system V api, which is probably not what you want. See here for more details:
http://mij.oltrelinux.com/devel/unixprg/#ipc__posix_msgqs
The msgget, msgctl, msgsnd, msgrcv commands are part of the older, system V api, and while the semantics are similar, are not posix queues. A couple of quick google search for system V queue tutorials/examples are likely to solve your problem.
If you're genuinely looking to use posix queues, switch to and look for documentation on the mq_open, mq_close, mq_unlink, mq_send, mq_receive, mq_getattr, mq_setattr api.

Producers, Consumers, and my cute Deadlock

I'm working on a Producer Consumer (Multithreaded) problem and I'm encountering a deadlock. My question is how?
I have multiple producers creating n products, and putting them in a global queue. The producers must WAIT if there is no room in the queue.
My consumers are accessing the queue by using First Come First Serve. The consumer must WAIT if there is nothing in the queue. The consumer will consume PART of the product, and the product will only be removed from the queue when it has been totally consumed. The consumers will stop consuming when there are no more products to be consumed.
I get the deadlock when the first item is done being consumed. I am using a mutex to lock the queue, 2 condition variables to signal when an item is added or removed to the queue, and a semaphore to keep track of total items being consumed.
Any idea why it may be deadlocking?
Input: 2 (Producers) 3 (Consumers) 5 (Items to produce) 2 (Queue Size) 0 (N/A) 50 (Quantum) 1 (Seed)
Producer:
#include <pthread.h>
#include <semaphore.h>
#include "producerConsumer.h"
#include <iostream>
#include <sys/time.h>
void* producer(void* args)
{
tProducer producer = *((tProducer*) args);
tProduct products[producer.products];
unsigned int queueMax = producer.queueMax;
timeval time;
std::string output;
for(int i = 0; i < producer.products; i++)
{
//Create item
products[i].productId = i;
products[i].life = producer.lifeOfProduct;
gettimeofday(&time, NULL);
products[i].timestamp = time.tv_sec;
//Lock and add to queue
pthread_mutex_lock(&queueLock);
//Queue is full and must wait
if(queue.size() >= queueMax)
{
output = "Producer: " + std::to_string(producer.id) + " is waiting\n";
std::cout << output;
pthread_cond_wait(&removeSignal, &queueLock);
}
//Debug message
output = "Producer: " + std::to_string(producer.id) + " is producing.\n";
std::cout << output;
//Add item to queue and signal
queue.push(products[i]);
pthread_cond_signal(&addSignal);
pthread_mutex_unlock(&queueLock);
//pthread_cond_signal(&addSignal);
//Debug message
output = "Producer: " + std::to_string(producer.id) + " just produced.\n";
std::cout << output;
}
pthread_exit(NULL);
}
Consumer:
#include <pthread.h>
#include <semaphore.h>
#include "producerConsumer.h"
#include <iostream>
void* consumer(void* args)
{
tConsumer consumer = *((tConsumer*) args);
int id = consumer.id;
int quantum = consumer.quantum;
std::string output;
while(true)
{
//Exit when nothing else is being created
if(sem_trywait(&totalProductsLeft) < 0)
{
break;
}
//Subtract life from product, and remove from queue if done
pthread_mutex_lock(&queueLock);
//Wait until item is in queue
if(queue.size() <= 0)
{
//Debug message
output = "Consumer: " + std::to_string(id) + " is waiting.\n";
std::cout << output;
pthread_cond_wait(&addSignal, &queueLock);
}
//Debug message
output = "Consumer: " + std::to_string(id) + " is ready.\n";
std::cout << output;
tProduct& product = queue.front();
product.life -= quantum;
//Item is done being consumed
if(product.life <= 0)
{
//Debug message
output = "Product: " + std::to_string(product.productId) + " is dead.\n";
std::cout << output;
//Remove a spot
queue.pop();
pthread_cond_signal(&removeSignal);
sem_wait(&totalProductsLeft);
}
else
{
//Debug message
output = "Product: " + std::to_string(product.life) + "hp is not done.\n";
std::cout << output;
}
pthread_mutex_unlock(&queueLock);
}
//May need to broadcast
pthread_exit(NULL);
}
Main (Just to show how I initialize everything):
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <pthread.h>
#include <semaphore.h>
#include "producerConsumer.h"
std::queue<tProduct> queue;
pthread_cond_t addSignal;
pthread_cond_t removeSignal;
sem_t totalProductsLeft;
pthread_mutex_t queueLock;
int main(int argc, char** argv)
{
//Handle input
const int NUM_INPUTS = 8;
int numberProducers;
int numberConsumers;
int numberOfProducts;
int queueSize;
int scheduleType;
int quantum;
int seed;
//Error check for input
if(argc != NUM_INPUTS)
{
std::cout << "Invalid number of arguments.\n";
return -1;
}
//Grab arguments
numberProducers = atoi(argv[1]);
numberConsumers = atoi(argv[2]);
numberOfProducts = atoi(argv[3]);
queueSize = atoi(argv[4]);
scheduleType = atoi(argv[5]);
quantum = atoi(argv[6]);
seed = atoi(argv[7]);
//Get rid of warnings for now
std::cout << numberOfProducts << std::endl;
std::cout << queueSize << std::endl;
std::cout << quantum << std::endl;
std::cout << seed << std::endl;
std::cout << scheduleType << std::endl;
//Create threads
pthread_t producerThreads[numberProducers];
pthread_t consumerThreads[numberConsumers];
tProducer producerArgs[numberProducers];
tConsumer consumerArgs[numberConsumers];
//Initialize global
pthread_mutex_init(&queueLock, NULL);
pthread_cond_init(&addSignal, NULL);
pthread_cond_init(&removeSignal, NULL);
std::cout << "Total Items: " << (numberProducers * numberOfProducts) << std::endl;
sem_init(&totalProductsLeft, 0, numberProducers * numberOfProducts);
//Start threads
srand(seed);
for(int i = 0; i < numberProducers; i++)
{
producerArgs[i].id = i;
producerArgs[i].products = numberOfProducts;
producerArgs[i].lifeOfProduct = rand() % 1024;
producerArgs[i].queueMax = queueSize;
pthread_create(&(producerThreads[i]), 0, producer, &producerArgs[i]);
}
for(int i = 0; i < numberConsumers; i++)
{
consumerArgs[i].id = i;
consumerArgs[i].quantum = quantum;
pthread_create(&(consumerThreads[i]), 0, consumer, &consumerArgs[i]);
}
//Wait for threads to end
for(int i = 0; i < numberProducers; i++)
{
pthread_join(producerThreads[i], NULL);
}
for(int i = 0; i < numberConsumers; i++)
{
pthread_join(consumerThreads[i], NULL);
}
return 0;
}
I ended up figuring it out. sem_trywait, in my consumer, is decrementing when items weren't done being consumed. The sem_wait inside my consumer is then blocking because there are no items left.

WaitForSingleObject on a semaphore does not wait, returns immediately

I'm trying to make a simple client-server program using windows file mapping and that uses semaphores. The clients send to the server 2 numbers, the server computes nr1+nr2 and nr1 * nr2. I tried something but it doesn't even work for 1 client and I want it to work for more clients. Here's the code:
the server:
#include <windows.h>
#include <stdio.h>
#include <iostream>
using namespace std;
typedef struct {
int nr1;
int nr2;
} Mesaj;
int main(int argc, char** argv) {
Mesaj* mesaj;
HANDLE createSemaphore = CreateSemaphore(NULL, 1, 1, "Semafor");
if (createSemaphore == NULL || createSemaphore == INVALID_HANDLE_VALUE) {
wcout << "Failed to create a semaphore\n";
} else {
wcout << "Created the semaphore\n";
}
HANDLE hMemory = CreateFileMapping(INVALID_HANDLE_VALUE, NULL,
PAGE_READWRITE, 0, sizeof(Mesaj), "SharedMemory");
WaitForSingleObject(createSemaphore, INFINITE);
mesaj = (Mesaj*) MapViewOfFile(hMemory, FILE_MAP_READ, 0, 0, sizeof(Mesaj));
printf("The numbers received are: %d, %d\n", mesaj->nr1, mesaj->nr2);
int produs = mesaj->nr1 * mesaj->nr2;
int suma = mesaj->nr1 + mesaj->nr2;
printf("\nSuma numerelor este: %d iar produsul lor este: %d", suma, produs);
ReleaseSemaphore(createSemaphore, 1, NULL);
Sleep(INFINITE);
return 0;
}
the client:
#include <windows.h>
#include <stdio.h>
#include <iostream>
using namespace std;
typedef struct {
int nr1;
int nr2;
} Mesaj;
int main(int argc, char** argv) {
Mesaj* mesaj, *mesaj2;
mesaj2 = (Mesaj*) malloc(sizeof(Mesaj));
HANDLE hMemory = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE,
"SharedMemory");
if (hMemory == NULL) {
wcout << "Error at OpenFileMapping\n";
}
HANDLE openSemaphore = OpenSemaphore(SEMAPHORE_ALL_ACCESS,TRUE,"Semafor");
if(openSemaphore != NULL || openSemaphore != INVALID_HANDLE_VALUE){
wcout<<"the semaphore is opened\n";
}
mesaj2 = (Mesaj*) MapViewOfFile(hMemory, FILE_MAP_WRITE, 0, 0,
sizeof(Mesaj));
int nr1 = 0, nr2 = 0;
printf("Give a number: ");
scanf("%d", &nr1);
printf("Give another number: ");
scanf("%d", &nr2);
mesaj2->nr1 = nr1;
mesaj2->nr2 = nr2;
if (mesaj2 == NULL) {
wcout << "Error\n"
} else {
wcout << "I sent " << mesaj2->nr1 << " and " << mesaj2->nr2 << endl;
}
system("pause");
return 0;
}
What exactly am I doing wrong? How should I work with semaphores?
When I open the server it doesn't wait for the client.
The documentation for CreateSemaphore says
The state of a semaphore object is signaled when its count is greater than zero, and nonsignaled when its count is equal to zero. The lInitialCount parameter specifies the initial count.
You passed lInitialCount=1 when you created the semaphore. and 1 > 0, so the semaphore is signaled and the WaitForSingleObject returns immediately.
You presumably want to create the semaphore with an initial count of 0, so that it does not become signaled until somebody calls ReleaseSemaphore.