The code below gives preference to only it's first child to modify a shared variable. How to remove this bug? - process

I want to capture the changes done by all the child processes to a shared variable with the parent process.
Here is the problem.
A parent process creates a structure like this:
struct data
{
int pid;
int n;
char c;
};
It creates k processes and the structure is shared with all it's child. Variable c is initialised to 'n' and it works as a flag.
Parent regularly "wakes up" (from "sleep") after a random amount of time (not more than 2 seconds) checks the value of c and if c is 'y' then prints the value of n (along with the corresponding pid) and makes c to be 'n' again.
On the other hand, every child Pi, 1≤ i ≤ k, regularly "wakes up" (from "sleep") after a random amount of time (not more than 2 seconds) checks the value of c and if c is 'n' then assigns some random value to n, along with its process id to pid, prints that value along with its own process id, and makes c to be 'y'.
If the program execution is terminated by the user (by pressing Ctrl-C), parent with all its children should terminate and the allocated memory should be realeased.
Here is the code:
typedef void (*sighandler_t)(int);
int shmid;
struct data
{
int pid;
int n;
char c;
};
void releaseSHM(int signum)
{
int status;
status = shmctl(shmid, IPC_RMID, NULL);
if (status == 0)
fprintf(stderr, "Remove shared memory with id = %d.\n", shmid);
else if (status == -1)
fprintf(stderr,"Cannot remove shared memory of id = %d.\n", shmid);
else
fprintf(stderr, "shmctl() returned wrong value while removing shared memory with id = %d.\n", shmid);
status = kill(0, SIGKILL);
exit(signum);
}
int main(int argc, char *argv[])
{
int num,k=5,i, p1;
struct data *s;
sighandler_t shandler;
shandler = signal(SIGINT, releaseSHM);
shmid = shmget(IPC_PRIVATE, sizeof(struct data), IPC_CREAT | 0777);
if(shmid==-1)
{
perror("shmget() failed");
exit(1);
}
s=(struct data *)shmat(shmid, NULL, 0);
s->c='n';
for(i=0;i<k;i++)
{
p1=fork();
if(p1==0)
{
while(1)
{
//shandler = signal(SIGINT, releaseSHM);
num=(rand()%3);
sleep(num);
if(s->c=='n')
{
s->n=rand();
s->pid=getpid();
printf("Child with pid %d set value of n as %d \n",s->pid,s->n);
s->c='y';
}
sleep(num);
}
}
else
{
while(1)
{
num=(rand()%3);
sleep(num);
if(s->c=='y')
printf("Parent reads value of n as %d set by child with process id %d \n",s->n, s->pid);
s->c='n';
}
}
}
return 0;
}
It gives output as:
Child with pid 13883 set value of n as 846930886
Parent reads value of n as 846930886 set by child with process id 13883
Child with pid 13883 set value of n as 1957747793
Parent reads value of n as 1957747793 set by child with process id 13883
Child with pid 13883 set value of n as 719885386
Parent reads value of n as 719885386 set by child with process id 13883
Child with pid 13883 set value of n as 596516649
Parent reads value of n as 596516649 set by child with process id 13883
Child with pid 13883 set value of n as 1350490027
Parent reads value of n as 1350490027 set by child with process id 13883
^CRemove shared memory with id = 15368197.
Killed
Clearly it captures the changes done by only one child process and don't give chance to other child processes. How to remove this bug?
If any other information required, drop a comment below.

You intend to have a parent and k children contending for shared memory, but instead you spawn only one child regardless of the value of k.
You write this:
....
for (int i = 0; i < k; i++) {
pid_t child = fork();
if (child == 0) do_child_infinite_loop();
else do_parent_infinite_loop(); // <- BUG: the for loop never resumes
}
You mean to do this:
....
for (int i = 0; i < k; i++) {
pid_t child = fork();
if (child == 0) do_child_infinite_loop();
}
do_parent_infinite_loop();
Once you fix this, you can move on to other debugging: the k children will stepping on each other's updates, and they also share the same pseudo-random number sequence.

Related

Creating threads with pthread_create() doesn't work on my linux

I have this piece of c/c++ code:
void * myThreadFun(void *vargp)
{
int start = atoi((char*)vargp) % nFracK;
printf("Thread start = %d, dQ = %d\n", start, dQ);
pthread_mutex_lock(&nItermutex);
nIter++;
pthread_mutex_unlock(&nItermutex);
}
void Opt() {
pthread_t thread[200];
char start[100];
for(int i = 0; i < 10; i++) {
sprintf(start, "%d", i);
int ret = pthread_create (&thread[i], NULL, myThreadFun, (void*) start);
printf("ret = %d on thread %d\n", ret, i);
}
for(int i = 0; i < 10; i++)
pthread_join(thread[i], NULL);
}
But it should create 10 threads. I don't understand why, instead, it creates n < 10 threads.
The ret value is always 0 (for 10 times).
But it should create 10 threads. I don't understand why, instead, it creates n < 10 threads. The ret value is always 0 (for 10 times).
Your program contains at least one data race, therefore its behavior is undefined.
The provided source is also is incomplete, so it's impossible to be sure that I can test the same thing you are testing. Nevertheless, I performed the minimum augmentation needed for g++ to compile it without warnings, and tested that:
#include <cstdlib>
#include <cstdio>
#include <pthread.h>
pthread_mutex_t nItermutex = PTHREAD_MUTEX_INITIALIZER;
const int nFracK = 100;
const int dQ = 4;
int nIter = 0;
void * myThreadFun(void *vargp)
{
int start = atoi((char*)vargp) % nFracK;
printf("Thread start = %d, dQ = %d\n", start, dQ);
pthread_mutex_lock(&nItermutex);
nIter++;
pthread_mutex_unlock(&nItermutex);
return NULL;
}
void Opt() {
pthread_t thread[200];
char start[100];
for(int i = 0; i < 10; i++) {
sprintf(start, "%d", i);
int ret = pthread_create (&thread[i], NULL, myThreadFun, (void*) start);
printf("ret = %d on thread %d\n", ret, i);
}
for(int i = 0; i < 10; i++)
pthread_join(thread[i], NULL);
}
int main(void) {
Opt();
return 0;
}
The fact that its behavior is undefined notwithstanding, when I run this program on my Linux machine, it invariably prints exactly ten "Thread start" lines, albeit not all with distinct numbers. The most plausible conclusion is that the program indeed does start ten (additional) threads, which is consistent with the fact that the output also seems to indicate that each call to pthread_create() indicates success by returning 0. I therefore reject your assertion that fewer than ten threads are actually started.
Presumably, the followup question would be why the program does not print the expected output, and here we return to the data race and accompanying undefined behavior. The main thread writes a text representation of iteration variable i into local array data of function Opt, and passes a pointer to that same array to each call to pthread_create(). When it then cycles back to do it again, there is a race between the newly created thread trying to read back the data and the main thread overwriting the array's contents with new data. I suppose that your idea was to avoid passing &i, but this is neither better nor fundamentally different.
You have several options for avoiding a data race in such a situation, prominent among them being:
initialize each thread indirectly from a different object, for example:
int start[10];
for(int i = 0; i < 10; i++) {
start[i] = i;
int ret = pthread_create(&thread[i], NULL, myThreadFun, &start[i]);
}
Note there that each thread is passed a pointer to a different array element, which the main thread does not subsequently modify.
initialize each thread directly from the value passed to it. This is not always a viable alternative, but it is possible in this case:
for(int i = 0; i < 10; i++) {
start[i] = i;
int ret = pthread_create(&thread[i], NULL, myThreadFun,
reinterpret_cast<void *>(static_cast<std::intptr_t>(i)));
}
accompanied by corresponding code in the thread function:
int start = reinterpret_cast<std::intptr_t>(vargp) % nFracK;
This is a fairly common idiom, though more often used when writing in pthreads's native language, C, where it's less verbose.
Use a mutex, semaphore, or other synchronization object to prevent the main thread from modifying the array before the child has read it. (Left as an exercise.)
Any of those options can be used to write a program that produces the expected output, with each thread responsible for printing one line. Supposing, of course, that the expectations of the output do not include that the relative order of the threads' outputs will be the same as the relative order in which they were started. If you want that, then only the option of synchronizing the parent and child threads will achieve it.

Does child address space affect the parent's address space

I want to know by the example below if the child's address space affect the parent's address space. So will the parent get the changed value of the child to 15 or it will get the value 5. I learnd that child's address space is not affected by its parent's one but what will be the output in this case? Will the output of value be 20 since it the value is incremented by 15 on child's part?
int value = 5;
int main(){
pid_t pid;
pid = fork();
if (pid == 0){
value +=15;
return 0;
}
else if(pid > 0){
wait(NULL);
printf("Parent: value = '%d', value); // Line A
return 0;
}
}
Due to the definition of how fork does work in UNIX-like (see https://en.wikipedia.org/wiki/Fork_(system_call)) systems, definitely output will be 5, because child receives just a copy of parent's virtual memory.
You may also refer to man(2) fork: https://linux.die.net/man/2/fork.

sending characters from parent to child process and returning char count to parent in C

So for an assignment I have for my Computer Systems class, I need to type characters in the command line when the program runs.
These characters (such as abcd ef) would be stored in argv[].
The parent sends these characters one at a time through a pipe to the child process which then counts the characters and ignores spaces. After all the characters are sent, the child then returns the number of characters that it counted for the parent to report.
When I try to run the program as it is right now, it tells me the value of readIn is 4, the child processed 0 characters and charCounter is 2.
I feel like I'm so close but I'm missing something important :/ The char array for a and in the parent process was an attempt to hardcode the stuff in to see if it worked but I am still unsuccessful. Any help would be greatly appreciated, thank you!
// Characters from command line arguments are sent to child process
// from parent process one at a time through pipe.
//
// Child process counts number of characters sent through pipe.
//
// Child process returns number of characters counted to parent process.
//
// Parent process prints number of characters counted by child process.
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h> // for fork()
#include <sys/types.h> // for pid_t
#include <sys/wait.h> // for waitpid()
int main(int argc, char **argv)
{
int fd[2];
pid_t pid;
int status;
int charCounter = 0;
int nChar = 0;
char readbuffer[80];
char readIn = 'a';
//char a[] = {'a', 'b', 'c', 'd'};
pipe(fd);
pid = fork();
if (pid < 0) {
printf("fork error %d\n", pid);
return -1;
}
else if (pid == 0) {
// code that runs in the child process
close(fd[1]);
while(readIn != 0)
{
readIn = read(fd[0], readbuffer, sizeof(readbuffer));
printf("The value of readIn is %d\n", readIn);
if(readIn != ' ')
{
charCounter++;
}
}
close(fd[0]);
//open(fd[1]);
//write(fd[1], charCounter, sizeof(charCounter));
printf("The value of charCounter is %d\n", charCounter);
return charCounter;
}
else
{
// code that runs in the parent process
close(fd[0]);
write(fd[1], &argv, sizeof(argv));
//write(fd[1], &a, sizeof(a));
close(fd[1]);
//open(fd[0]);
//nChar = read(fd[0], readbuffer, sizeof(readbuffer));
nChar = charCounter;
printf("CS201 - Assignment 3 - Andy Grill\n");
printf("The child processed %d characters\n\n", nChar);
if (waitpid(pid, &status, 0) > 0)
{
if (WIFEXITED(status))
{
}
else if (WIFSIGNALED(status))
{
}
}
return 0;
}
}
You're misusing pipes.
A pipe is a unidirectional communication channel. Either you use it to send data from a parent process to a child process, or to send data from a child process to the parent. You can't do both - even if you kept the pipe's read and write channels open on both processes, each process would never know when it was its turn to read from the pipe (e.g. you could end up reading something in the child that was supposed to be read by the parent).
The code to send the characters from parent to child seems mostly correct (more details below), but you need to redesign child to parent communication. Now, you have two options to send the results from child to parent:
Use another pipe. You set up an additional pipe before forking for child-to-parent communication. This complicates the design and the code, because now you have 4 file descriptors to manage from 2 different pipes, and you need to be careful where you close each file descriptor to make sure processes don't hang. It is also probably a bit overkill because the child is only sending a number to the parent.
Return the result from the child as the exit value. This is what you're doing right now, and it's a good choice. However, you fail to retrieve that information in the parent: the child's termination status tells you the number of characters processed, you can fetch this value with waitpid(2), which you already do, but then you never look at status (which contains the results you're looking for).
Remember that a child process has its own address space. It makes no sense to try to read charCounter in the parent because the parent never modified it. The child process gets its own copy of charCounter, so any modifications are seen by the child only. Your code seems to assume otherwise.
To make this more obvious, I would suggest moving the declarations of variables to the corresponding process code. Only fd and pid need to be copied in both processes, the other variables are specific to the task of each process. So you can move the declarations of status and nChar to the parent process specific code, and you can move charCounter, readbuffer and readIn to the child. This will make it very obvious that the variables are completely independent on each process.
Now, some more specific remarks:
pipe(2) can return an error. You ignore the return value, and you shouldn't. At the very least, you should print an error message and terminate if pipe(2) failed for some reason. I also noticed you report errors in fork(2) with printf("fork error %d\n", pid);. This is not the correct way to do it: fork(2) and other syscalls (and library calls) always return -1 on error and set the errno global variable to indicate the cause. So that printf() will always print fork error -1 no matter what the error cause was. It's not helpful. Also, it prints the error message to stdout, and for a number of reasons, error messages should be printed to stderr instead. So I suggest using perror(3) instead, or manually print the error to stderr with fprintf(3). perror(3) has the added benefit of appending the error message description to the text you feed it, so it's usually a good choice.
Example:
if (pipe(fd) < 0) {
perror("pipe(2) error");
exit(EXIT_FAILURE);
}
Other functions that you use throughout the code may also fail, and again, you are ignoring the (possible) error returns. close(2) can fail, as well as read(2). Handle the errors, they are there for a reason.
The way you use readIn is wrong. readIn is the result of read(2), which returns the number of characters read (and it should be an int). The code uses readIn as if it were the next character read. The characters read are stored in readbuffer, and readIn will tell you how many characters are on that buffer. So you use readIn to loop through the buffer contents and count the characters. Something like this:
readIn = read(fd[0], readbuffer, sizeof(readbuffer));
while (readIn > 0) {
int i;
for (i = 0; i < readIn; i++) {
if (readbuffer[i] != ' ') {
charCounter++;
}
}
readIn = read(fd[0], readbuffer, sizeof(readbuffer));
}
Now, about the parent process:
You are not writing the characters into the pipe. This is meaningless:
write(fd[1], &argv, sizeof(argv));
&argv is of type char ***, and sizeof(argv) is the same as sizeof(char **), because argv is a char **. Array dimensions are not kept when passed into a function.
You need to manually loop through argv and write each entry into the pipe, like so:
int i;
for (i = 1; i < argv; i++) {
size_t to_write = strlen(argv[i]);
ssize_t written = write(fd[1], argv[i], to_write);
if (written != to_write) {
if (written < 0)
perror("write(2) error");
else
fprintf(stderr, "Short write detected on argv[%d]: %zd/zd\n", i, written, to_write);
}
}
Note that argv[0] is the name of the program, that's why i starts at 1. If you want to count argv[0] too, just change it to start at 0.
Finally, as I said before, you need to use the termination status fetched by waitpid(2) to get the actual count returned by the child. So you can only print the result after waitpid(2) returned and after making sure the child terminated gracefully. Also, to fetch the actual exit code you need to use the WEXITSTATUS macro (which is only safe to use if WIFEXITED returns true).
So here's the full program with all of these issues addressed:
// Characters from command line arguments are sent to child process
// from parent process one at a time through pipe.
//
// Child process counts number of characters sent through pipe.
//
// Child process returns number of characters counted to parent process.
//
// Parent process prints number of characters counted by child process.
#include <stdlib.h>
#include <stdio.h>
#include <string.h> // for strlen()
#include <unistd.h> // for fork()
#include <sys/types.h> // for pid_t
#include <sys/wait.h> // for waitpid()
int main(int argc, char **argv)
{
int fd[2];
pid_t pid;
if (pipe(fd) < 0) {
perror("pipe(2) error");
exit(EXIT_FAILURE);
}
pid = fork();
if (pid < 0) {
perror("fork(2) error");
exit(EXIT_FAILURE);
}
if (pid == 0) {
int readIn;
int charCounter = 0;
char readbuffer[80];
if (close(fd[1]) < 0) {
perror("close(2) failed on pipe's write channel");
/* We use abort() here so that the child terminates with SIGABRT
* and the parent knows that the exit code is not meaningful
*/
abort();
}
readIn = read(fd[0], readbuffer, sizeof(readbuffer));
while (readIn > 0) {
int i;
for (i = 0; i < readIn; i++) {
if (readbuffer[i] != ' ') {
charCounter++;
}
}
readIn = read(fd[0], readbuffer, sizeof(readbuffer));
}
if (readIn < 0) {
perror("read(2) error");
}
printf("The value of charCounter is %d\n", charCounter);
return charCounter;
} else {
int status;
if (close(fd[0]) < 0) {
perror("close(2) failed on pipe's read channel");
exit(EXIT_FAILURE);
}
int i;
for (i = 1; i < argc; i++) {
size_t to_write = strlen(argv[i]);
ssize_t written = write(fd[1], argv[i], to_write);
if (written != to_write) {
if (written < 0) {
perror("write(2) error");
} else {
fprintf(stderr, "Short write detected on argv[%d]: %zd/%zd\n", i, written, to_write);
}
}
}
if (close(fd[1]) < 0) {
perror("close(2) failed on pipe's write channel on parent");
exit(EXIT_FAILURE);
}
if (waitpid(pid, &status, 0) < 0) {
perror("waitpid(2) error");
exit(EXIT_FAILURE);
}
if (WIFEXITED(status)) {
printf("CS201 - Assignment 3 - Andy Grill\n");
printf("The child processed %d characters\n\n", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
fprintf(stderr, "Child terminated abnormally with signal %d\n", WTERMSIG(status));
} else {
fprintf(stderr, "Unknown child termination status\n");
}
return 0;
}
}
Some final notes:
The shell splits arguments by spaces, so if you start the program as ./a.out this is a test, the code will not see a single space. This is irrelevant, because spaces are supposed to be ignored anyway, but if you want to test that the code really ignores spaces, you need to quote the parameters so that the shell does not process them, as in ./a.out "this is a test" "hello world" "lalala".
Only the rightmost (least significant) 8 bits of a program's exit code are used, so WEXITSTATUS will never return more than 255. If the child reads more than 255 characters, the value will wrap around, so you effectively have a character counter modulo 256. If this is a problem, then you need to go with the other approach and set up a 2nd pipe for child-to-parent communication and write the result there (and have the parent read it). You can confirm this on man 2 waitpid:
WEXITSTATUS(status)
returns the exit status of the child. This consists of the least
significant 8 bits of the status argument that the child
specified in a call to exit(3) or _exit(2) or as the argument for a return
statement in main(). This macro should be employed only if
WIFEXITED returned true.

How does the parent select which forked child process to communicate with?

I created several child processes from one parent, and I also created bidirectional pipes between each process and the parent. Now the problem is how can I go back and forth between the child processes and the parent to do different type of works, and let the children communicate with the parent? I was thinking to keep a track of each process's PID, and use it as an identity to switch. But technically, how? Attached is the code that I have so far.
struct val
{ int testInt;
char testChar;
};
int main ()
{ val val1;
pid_t cpid[3];
int fd[2], fdd[2];
int SFC;
for (int i=0; i<3; i++)
{ pipe(fd);
pipe(fdd);
cpid[i]=fork();
if(cpid[i]==0)
{ cout<<"\nI am a child and my pid is:"<<getpid();
cout<<"\nMy parent is: "<<getppid();
SFC=i;
close(fd[1]);//close fd-pipe write end for child
read(fd[0], &val1, sizeof(val1));//read from pipe
close(fdd[0]);
write(fdd[1], &SFC, sizeof(SFC));
close(fd[0]);
close(fdd[1]);
cout<<"\nChild received value: "<<val1.testInt<<val1.testChar;
cout<<"\nChild sent value: "<<SFC;
cout<<endl;
break;
}
else
{ close(fd[0]);//parent close read end of pipe fd
close(fdd[1]);//parent close write end of pipe fdd
val1.testInt=4;
val1.testChar='A';
write(fd[1], &val1, sizeof(val1));//parent write to fd
read(fdd[0], &SFC, sizeof(SFC));//parent read from fdd
close(fd[1]);
close(fdd[0]);
wait(NULL);
cout<<"\nI am the parent and I have this child: "<<cpid[i];
cout<<"\nParent sent out value: "<<val1.testInt<<val1.testChar;
cout<<"\nParent received value: "<<SFC<<endl;
}
}
return 0;
}

How to get a grandparents/ancestors process ID?

I would like to know - if possible - how to get the pid of a process' grandparent (or further).
To be more specific, I want for a process to print its depth in a process tree.
For example, when starting with the following:
int main() {
int creator_id = (int) getpid();
pid_t pid1 = fork();
pid_t pid2 = fork();
pid_t pid3 = fork();
//print depth in process tree of each process
return 0;
}
According to my theory, the tree will look like this:
0
/|\
/ | \
/ | \
0 0 0
/ \ |
0 0 0
/
0
So my first idea was to somehow see how often I have to go up until I find the creator's pid.
As a little sidenote:
I also wondered if it was possible to make the printing from bottom up, meaning that all processes in the deepest level would print first.
how to get the pid of a process' grandparent (or further).
This depends on which operating system you are using, since you use fork() to create new process in your example, I suppose you are using some Unix-like system.
If you are using Linux and know the pid of a process, you could get its parent process' pid from /proc/[pid]/stat, the fourth field in that file. Through this parent-child chain, you could find a process' all ancestors.
Following #Lee Duhem's hint, I made the following function that returns the nth ancestor of the current process (the 2nd ancestor is the grandparent).
/* Get the process ID of the calling process's nth ancestor. */
pid_t getapid(int n) {
pid_t pid = getpid();
while(n>0 && pid){ // process with pid 0 has no parent
// strlen("/proc/") == 6
// max [pid] for 64 bits is 4194304 then strlen("[pid]") < 7
// strlen("/stat") == 5
// then strlen("/proc/[pid]/stat") < 6 + 7 + 5
char proc_stat_path[6+7+5+1];
sprintf(proc_stat_path, "/proc/%d/stat", pid);
// open "/proc/<pid>/stat"
FILE *fh = fopen(proc_stat_path, "r");
if (fh == NULL) {
fprintf(stderr, "Failed opening %s: ", proc_stat_path);
perror("");
exit(1);
}
// seek to the last ')'
int c;
long pos = 0;
while ((c = fgetc(fh)) != EOF) {
if (c == ')')
pos = ftell(fh);
}
fseek(fh, pos, SEEK_SET);
// get parent
fscanf(fh, " %*c %d", &pid);
// close "/proc/<pid>/stat"
fclose(fh);
// decrement n
n--;
}
if(n>0)
return -1;
else
return pid;
}