Below is the code for an assignment on processor farming. The focus is on the comments with "HERE $resp is always the same/different". That's my problem: when the worker process does it's job and sends the response data to the farmer, the farmer always receives the same response data (the same pointer address), even though worker sends different data every time.
Example: workers send data at addresses: 0x7fff42318a90,0x7ffddba97390,0x7ffc69e8e060 etc. and farmer keeps receiving data from only one address 0x7ffdb1496f30
I've done my best to abstract the code and question as much as possible. If I've omitted important information please let me know, I'm new to process management programming and I could use some guidance.
UPDATE: also printing the contents of resp s.a resp.b where b is an integer returns the same value, even though the value is different in worker.
UPDATE: I tried writing some runnable code only this time the worker might not be receiving.
//both in farmer and in worker
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <unistd.h> // for execlp
#include <mqueue.h> // for mq
typedef struct{
int a;
} REQUEST;
typedef struct{
int b;
} RESPONSE;
static char mq_farmer[80];
static char mq_worker[80];
//farmer:
int main (int argc, char * argv[])
{
REQUEST req;
RESPONSE resp;
sprintf (mq_farmer, "/mq_request_%s_%d", "foo", getpid());
sprintf (mq_worker, "/mq_response_%s_%d", "bar", getpid());
//define attr
struct mq_attr attr;
attr.mq_maxmsg= 10;
attr.mq_msgsize = sizeof(REQUEST);
mqd_t reqQueue = mq_open(mq_farmer, O_WRONLY | O_CREAT | O_EXCL, 0600, &attr);
attr.mq_msgsize = sizeof(RESPONSE);
mqd_t respQueue = mq_open(mq_worker, O_WRONLY | O_CREAT | O_EXCL, 0600, &attr);
// * create the child processes (see process_test() and message_queue_test())
int i;
for(i = 0; i < 3; i++)
{
pid_t processID = fork();
if(processID < 0)
{
//error
}
else if(processID == 0)
{
//some code
execlp("./worker","worker", getpid(), i, NULL);
}
}
pid_t pid = fork();
if(pid < 0)
{
//error
}
else
{
if(pid == 0) //receiving done here
{
for(i = 0; i < 3; i++)
{
// read the messages from the worker queue
mqd_t received = mq_receive (respQueue, (char *) &resp, sizeof(resp), NULL);
printf("Farmer received worker response: %p\n with value %d\n", &resp, resp.b);
//HERE &resp is always the same
}
// end worker process
req.a = -1;
mqd_t sent = mq_send(reqQueue, (char *) &req,sizeof(req), 0);
}
else //sending done here
{
for(i = 0; i < 3; i++)
{
req.a = i;
mqd_t sent = mq_send(reqQueue, (char *) &req,sizeof(req), 0);
}
}
}
waitpid(pid, NULL, 0);
mq_close(reqQueue);
mq_close(respQueue);
//clean up the message queues
mq_unlink(mq_farmer);
mq_unlink(mq_worker);
return 0;
}
//worker:
int main (int argc, char * argv[])
{
REQUEST req;
RESPONSE resp;
int arg1;
sscanf(argv[1], "%d", &arg1);
sprintf (mq_farmer, "/mq_request_%s_%d", "foo", arg1);
sprintf (mq_worker, "/mq_response_%s_%d", "bar",arg1);
mqd_t reqQueue = mq_open (mq_farmer, O_RDONLY);
mqd_t respQueue = mq_open (mq_worker, O_WRONLY);
while (true){
//receiving
mqd_t received = mq_receive (reqQueue, (char *) &req,
sizeof(req), NULL);
printf("Worker received %p with value %d\n", &req, req.a);
//received stop signal
if(req.a < 0){
printf("stopping worker\n");
break;
}
//waiting for farmer to fork
sleep(3);
//do something with request data
resp.b = req.a;
//send response
mqd_t sent = mq_send (respQueue, (char *) &resp,
sizeof (resp), NULL);
printf("Worker sent response: %p\n", &resp);
//HERE &resp is always different (doesn't print)
}
mq_close(reqQueue);
mq_close(respQueue);
//clean up the message queues
mq_unlink(mq_farmer);
mq_unlink(mq_worker);
return 0;
}
When you call
mq_receiveit places the data at the buffer pointed to by the second argument, which you give as&resp. It does not change the pointer itself.&respis a fixed address in the parent, unless you change it, which appears unlikely from the posted code [which does not show the definition ofresp], so:You will always get the same value.
What you [probably] want to do is print what
respcontainsUPDATE:
Okay, there were a few more bugs.
The big bug is that while you can have one queue for worker-to-farmer messages (i.e. the response queue), you can not use a single queue for requests to workers. They each need their own request queue.
Otherwise, a single worker can absorb/monopolize all requests, even ones that belong to others. If that happened, the farmer would likely see messages that were stamped from only that worker.
This is what you're seeing, because, the first worker [probably #0] has its
mq_receivecomplete first. It is, then, so fast that it does all of themq_receive/mq_sendbefore any others can get to them.It will then see a "stop" message and exit. If the others are "lucky", the first worker left the remaining stop messages in the queue. But, no request messages, so they never send a response.
Also, the response queue was opened by the farmer with
O_WRONLYinstead ofO_RDONLY.I've produced two versions of your program. One with annotations for bugs. Another that is cleaned up and working.
Here's the annotated version [please pardon the gratuitous style cleanup]:
Here's the cleaned up and working version. Note that, for ease/simplicity, I combined both the farmer and worker programs into a single one, using a little bit of trickery in
main:UPDATE #2:
Here's a version that demonstrates single vs. multiple request queues. The workers now check the destination id in the message they receive matches their worker number.
If you just run it with no options, you'll get multiple queues and the "good" output.
If you run it with
-b[and optionally-s] you'll get a single request queue and the program will see misrouted messages (e.g. worker 0 grabs a message intended for worker 1).Single queue is a subset. As long as workers are "equal", it's okay. But, if they're not (e.g. one worker can do things others can't), being able to queue to the correct worker is important. An example would be a network node that has special FPGA assisted calculation hardware that other ones don't and some requests need that acceleration.
Also, single queue is self balancing by the workers. That is one form of scheduling, but there are other models. (e.g. the farmer wants to retain control of the distribution of labor). Or, the farmer has to stop one worker and keep the others going (e.g. the system being stopped will be powered off for maintenance).