Reputation: 1
I have a main process that create some process child. I'll name it as master and user.
The master create N users and users have to ask every time for their budget to master. Process communicate through queue in a linux system. After the creation of users, the master wait for their terminations in an infinite loop that breaks when the number of users (and other process child that are not important for the problem) are all terminated. If I don't put a sleep in the while the master and users works correctly unless some error of maths that I will solve. If I put a sleep(1)
in the while
(because as request I've to print the status every second), users printing that are using setBudget and getBudget but don't print the result of the queue and anything after that, like the function don't terminate. The same problem when I don't put any print except for the first, every user print only on the termination and anything before...There is something that I missed and could cause trouble to put a sleep()
before a waitpid()
or a msgrcv()
?
if I missed some function tell me to add, that initil function are basic function to set and get a shared memory. So the function to init sh_memory and semaphore are missed, so as the full code as missed cause it's not necessary.
The main problem I think is the sleep that can't allow the master to receive the msg and so the user will be not able to receive response. But if this would be the mistake, the user will be able to print the error message or that master doesn't send anything back but print only this:
getButget: 6685
setBudget:6685
in an infinite loop and with different pid, like all users go to infinity. master.c
userList = initSharedMemory(SH_KEY_USER,SH_USERS_LIST_SIZE);
nodeList = initSharedMemory(SH_KEY_NODE,SH_NODES_LIST_SIZE);
int queueID = msgget(KEY_QUEUE, IPC_CREAT | 0600);
char *argsUser[2] = {USER_NAME,NULL};
char *argsNode[2] = {NODE_NAME,NULL};
for (int i = 0; i < SO_NODES_NUM; i++)
{
int pid = fork();
switch (pid){
case 0:
if(execvp(NODE_NAME, argsNode)==-1){
perror("error in execvp: ");
}
exit(EXIT_FAILURE);
break;
case -1:
printf("Error in main: fork failed");
break;
default:
printf("\nmaster: launch node %d (pid %d)\n",i,pid);
nodeList[i] = pid;
break;
}
}
for (int i = 0; i < SO_USERS_NUM; i++)
{
int pid = fork();
switch (pid){
case 0:
if(execvp(USER_NAME, argsUser)==-1){
perror("error in execvp: ");
}
exit(EXIT_FAILURE);
break;
case -1:
printf("Error in main: fork failed");
break;
default:
printf("\nmaster: launch user %d (pid %d)\n",i,pid);
userList[i] = pid;
break;
}
}
struct msgUser mt_msg;
pid_t pidnow;
int num_bytes;
while (1)
{
//resumePrint();
sleep(1);
//printf("\nciclo post sleep %d %d",user_done,node_done);
if (user_done+node_done == SO_USERS_NUM+SO_NODES_NUM) {
break;
}
if (user_done == SO_USERS_NUM)
{
for (int i = 0; i < SO_NODES_NUM; i++)
{
kill(nodeList[i], 5);
sleep(2);
kill(nodeList[i], 9);
sleep(1);
}
}
num_bytes = msgrcv(queueID, &mt_msg, MSGUSER_SIZE, MSG_BUDGET, IPC_NOWAIT);
//printf("\nciclo post sleep numbytes:%d",num_bytes);
if (num_bytes >= 0)
{
receivedBudgetRequest(mt_msg,queueID);
}
pidnow= waitpid(-1, NULL, WNOHANG);
//printf("\nciclo post sleep pid_finish:%d ",pidnow);
if(pidnow ==-1)perror("error in waitpid: ");
if(pidnow > 0){
for (int i = 0; i < SO_USERS_NUM; i++)
{
if (pidnow == userList[i])
{
printf("\nTerminato user: done:%d pid:%d\n", user_done, pidnow);
++user_done;
break;
}
}
for (int j = 0; j < SO_NODES_NUM; j++)
{
if (pidnow == nodeList[j])
{
printf("\nTerminato node: done:%d pid:%d\n", node_done, pidnow);
++node_done;
break;
}
}
}
}
user.c
int main(int argc, char const *argv[]){
nodeList = initSharedMemory(SH_KEY_NODE,SH_NODES_LIST_SIZE);
if (!initTransactionPool())
{
printf("\nError in initTransaction\n");
exit(EXIT_FAILURE);
}
int queueID = msgget(KEY_QUEUE, IPC_CREAT | 0600);
setQueueId(queueID);
attach_signals();
srand(getpid());
int finish = 0;
printf("\n sono l'user: %d",getpid()); --> if i didn't put other print this will be pritned only at the end and not when the process start
while (finish < SO_RETRY)
{
checkTransactionCompleted();
getBudget();
int result = setBudget();
if(result == 0){
printf("\n result setBudget: %d user: %d",result,getpid()); --> doesn't print
int val = makeTransaction();
if(val == -1){
++finish;
}
}
int delay = rand() % SO_MAX_TRANS_GEN_NSEC / 1000000000 + SO_MIN_TRANS_GEN_NSEC / 1000000000;
sleep(delay);
}
printf("\nuser terminato");
freeTransactionPool();
return 0;
}
function in library:
void getBudget()
{
printf("\ngetBudget: %d",getpid());
struct msgUser messageUser;
messageUser.mtype = MSG_BUDGET;
messageUser.pid = getpid();
if(msgsnd(queueID, &messageUser, MSGUSER_SIZE, 0)==-1){
perror("\nerror in msgsnd getBudget");
}
}
int setBudget()
{
struct msgUser mt_msg;
int num_bytes;
totalBudget=SO_BUDGET_INIT;
num_bytes = msgrcv(queueID, &mt_msg, MSGUSER_SIZE, id, 0);
printf("\n setBudget: %d",getpid()); --> it print this and anything else
if(num_bytes == -1)perror("msgrcv error in setBudgt:");
if (num_bytes > 0)
{
if((mt_msg.numero>SO_BUDGET_INIT*SO_USERS_NUM) || mt_msg.numero<-(SO_BUDGET_INIT*SO_USERS_NUM)) return mt_msg.numero;
totalBudget += mt_msg.numero;
printf("\n received by master: %d %d",totalBudget,getpid());
}
checkTransactionCompleted(queueID); --> doesn't need if the master don't send anything
int sum = sumMoneyTransactions();
if((totalBudget +sum)<-998) {
printf("\nError addition: %d %d",(totalBudget+sum),getppid());
return -1;
}
totalBudget += sum;
printf("\nBudget: %d %d\n\n",totalBudget,getpid());
return 0;
}
void receivedBudgetRequest(struct msgUser mt_msg,int queueID)
{
int initialBudget = calculateBudgetFor(mt_msg.pid);
struct msgUser messageMaster;
messageMaster.mtype = mt_msg.pid;
messageMaster.pid = getpid();
messageMaster.numero = initialBudget;
msgsnd(queueID, &messageMaster, MSGUSER_SIZE, 0);
printf("\n mando messaggio ad user"); --> doen't print
}
int sumMoneyTransactions()
{
int tot = 0;
for (int i = 0; i < sizePool; i++)
{
tot -= transaction_pool[i].money + transaction_pool[i].reward;
}
return tot;
}
Upvotes: 0
Views: 92
Reputation: 5211
You need to flush the output by putting "\n" at the end of the format passed to printf()
. For example:
printf("getBudget: %d\n",getpid());
Or you can call fflush(stdout)
after the calls to printf()
. For example:
printf("\n received by master: %d %d",totalBudget,getpid());
fflush(stdout);
Moreover, for portability purposes, use signal names instead of hardcoded values when you call kill()
. For example:
kill(nodeList[i], SIGKILL);
Usually, when we kill processes we send SIGTERM (15) first (which can be captured by the target process to make cleanups before exiting) and then SIGKILL (9) if the process does not finish in time. You are using signal 5 (SIGTRAP) instead. But I guess that it is a typo:
for (int i = 0; i < SO_NODES_NUM; i++)
{
kill(nodeList[i], 5); // should be SIGTERM (15)
sleep(2);
kill(nodeList[i], 9); // should be SIGKILL (9)
sleep(1);
}
For the sake of performances, in the master, do not enter in the second loop if the pid has been found in the first one. Use a flag (e.g. found):
if(pidnow > 0){
int found = 0;
for (int i = 0; i < SO_USERS_NUM; i++)
{
if (pidnow == userList[i])
{
printf("\nTerminato user: done:%d pid:%d\n", user_done, pidnow);
++user_done;
found = 1;
break;
}
}
if (found == 0)
{
for (int j = 0; j < SO_NODES_NUM; j++)
{
if (pidnow == nodeList[j])
{
printf("\nTerminato node: done:%d pid:%d\n", node_done, pidnow);
++node_done;
break;
}
}
}
}
In the child processes, if execvp()
fails, it is advised to call _exit() instead of exit(). The latter calls lots of cleanup functions registered with atexit()
in the libraries. In a child process those cleanups may cause various problems. So, you should for example do something like this:
int pid = fork();
switch (pid){
case 0:
if(execvp(NODE_NAME, argsNode)==-1){
perror("error in execvp: ");
}
_exit(EXIT_FAILURE);
break;
[...]
Upvotes: 1