For loop is executing in wrong order - c

I am doing a for loop to create children with fork and print their pids, but instead of printing them in order it mixes them up.! what it prints :
[PARENT/PID=953] Created child 0 [PID=954] and initial state f
[PARENT/PID=953] Created child 3 [PID=957] and initial state f
[PARENT/PID=953] Created child 2 [PID=956] and initial state t
[PARENT/PID=953] Created child 1 [PID=955] and initial state f
also prints in other orders
tried to put wait(&status) in different places but it didnt help.
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#define PERMS 0644
int main(int argc, char **argv) {
int n = strlen (argv[1]);
int status;
for(int i=0;i<n;i++) // loop will run n times
{
if(fork() == 0)
{
char s[5];
sprintf(s, "%c", argv[1][i]);
char *const argv2[] = {"./child.c",s,NULL};
wait(&status);
status = execv("./child", argv2);
printf("[PARENT/PID=%d] Created child %d [PID=%d] and initial state %s \n",getppid(),i,getpid(),s);
exit(0);
}
}
for(int i=0;i<n;i++) // loop will run n times
wait(NULL);
}

Related

Communicating through pipes and sending messages

I am having trouble communicating with the child process. I am trying to make quick.c simply get an input from stdin and send it to sand.c to capitialise it and send it back to the parent and then print it to stdout. Right now the program asks for an input twice instead of only asking once.
this is quick.c
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
int fds[2];
int test[2];
int pid;
pid_t child_a;
char buffer[50], buff[50];
if(pipe(fds)) {
perror("Pipe:");
exit(0);
}
if(pipe(test)) {
perror("Pipe:");
exit(0);
}
child_a = fork();
if (child_a == 0) {
//Child
FILE *f = fdopen(fds[0], "r");
FILE *e = fdopen(test[1], "w");
close(fds[1]);
close(test[0]);
//dup2(fds[0],0); causes infinite loop
dup2(test[1],1);
execlp("./sand", "sand", NULL);
fclose(e);
fclose(f);
} else {
// Parent
// Wrap the pipes
FILE *f = fdopen(fds[1], "w");
FILE *e = fdopen(test[0], "r");
close(fds[0]);
close(test[1]);
fgets(buffer,50, stdin);
fprintf(f,"%s",buffer);
while(fgets(buff, 50, e)) {
printf("Parent receive %s", buff);
}
fflush(stdout);
fclose(f);
fclose(e);
wait(NULL);
}
return 0;
}
This method is sand.c
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
int main(int argc, char *argv[]) {
char buffer[50];
int i = 0;
fgets(buffer,50,stdin);
char chr;
// Loop
while (buffer[i]) {
buffer[i] = toupper(buffer[i]);
i++;
}
fprintf(stdout,"%s",buffer);
return 0;
}
On running the code in my machine, the commented dup2 line does not loop to infinity. That may be because pipe programs run differently on different machines. However, the program terminates after taking input. Here are the things that are wrong with your code:
You aren't waiting for the child to write data to test pipe before printing in the parent. You must put the wait statement after taking input.
You've used file pointers for handling pipes. Pipes are accessed with file descriptors and cause unexpected results when handled with file pointers. Instead of fgets and fprintf, use read and write methods to work with file descriptors.
Error in the execlp command which I've commented.
There are errors regarding buffers, I've commented them in the code where they occur.
This is quick.c
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
int fds[2];
int test[2];
int pid;
pid_t child_a;
char buffer[50], buff[50];
if(pipe(fds)) {
perror("Pipe:");
exit(0);
}
if(pipe(test)) {
perror("Pipe:");
exit(0);
}
child_a = fork();
if (child_a == 0) {
//Child
//CHANGED: No need to open file pointers here. Pipes are already open and accessed by file descriptor instead of file pointer. File pointers create trouble when used with pipes. 0 is file descriptor of stdin, 1 for stdout.
close(fds[1]);
close(test[0]);
dup2(fds[0],0);
dup2(test[1],1);
//CHANGED: There was an error with the command you wrote.
//That's because ./sand arg will look for a 'sand' directory which doesn't exist
//This line will throw warnings because execlp requires needs a command as the second argument, but in this case the filename is the command.
//NOTE: before running quick.c, compile sand.c as sand.out and not a.out
execlp("./sand.out",NULL);
printf("Exec Error\n"); //this will only execute if execlp didn't run. Always have this line in your code to know what's happening.
}
else
{
// Parent
// Wrap the pipes
//Got rid of the file pointers
close(fds[0]);
close(test[1]);
//CHANGE: fgets is only used with file pointers. While handling pipes, we work with file descriptors, with which read and write methods are used
int n = read(0,buffer,50); //If this is new to you, I strongly recommend reading manual pages for read and write, but for right now
// The signature should be enough to understand - read/write(int file_descriptor, char *buffer, int number_of_bytes)
write(fds[1],buffer,n);
//MOST IMPORTANT: You need to wait for child after this point. Because test pipe doesn't have data yet which will be received by child.
wait(NULL);
//CHANGE: printf statements do not work well with buffere, because buffers are not terminated with null
//%s specifier will always look for a null or print garbage
//If you still want to use printf, look into $man bzero
while((n = read(test[0],buff, 50))>0)
{
write(1,buff,n);
}
fflush(stdout);
}
return 0;
}
This is sand.c
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
int main(int argc, char *argv[])
{
char buffer[50];
int i = 0;
int n = read(0,buffer,sizeof(buffer)); //changed fgets to read, to get number of bytes read.
char chr;
// Loop
// we have number of bytes. So change while to for
for (i=0;i<n;i++)
{
buffer[i] = toupper(buffer[i]);
}
write(1,buffer,n); //Changed fprintf to write to get rid of %s problem.
//Again, to fill remaining places of buffer with null, look up bzero.
//The reason I haven't done that is to not confuse you with so many changed methods.
return 0;
}
Let me know, if the solution also helps you find the source of the infinity loop.

C communicate parent and child to increase and print counter

I am trying to write a program so that the parent and child process can communicate back and forth between each other. The parent process and the child process ought to print the values from 1-100 where each process prints the value incrementing it by 1 each time. Now the issue I face is that, I know nothing much about pipes. What I gather from reading materials online is that I can use a pipe to read and write values. I have leveraged this to print something in the child process, and send back something to the parent. Now, I am not sure how to get the parent to return to the child after printing for itself? I know my code is probably all wrong, but I am really not sure what I should do.
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main(int argc, const char * argv[]) {
int fd[2];
if (pipe(fd)== -1){
printf("An error occured while opening the pipe\n");
}
int id = fork();
int i = 0;
if (id == 0){
close(fd[0]);
printf("In child: %d", i);
i ++;
write(fd[1], &i, sizeof(int));
close(fd[1]);
} else {
wait(NULL);
close(fd[1]);
int y;
read(fd[0],&y, sizeof(int));
close(fd[0]);
}
}
To keep it simple, it's up to you to check return values and handle errors. This will only do it between 0 - 9 and you will have to expand the mathematics.
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char *argv[])
{
int pipefd_1[2];
int pipefd_2[2];
pid_t cpid;
pipe(pipefd_1);
pipe(pipefd_2);
cpid = fork();
if (cpid == 0) { /* Child reads from pipe 1, writes to pipe 2*/
char cval[] = {'0'};
close(pipefd_1[1]); /* Close unused write and read ends */
close(pipefd_2[0]);
while (atoi(cval) != 9) {
read(pipefd_1[0], cval, 1);
printf("Child print %d\n", atoi(cval));
cval[0] += 1;
write(pipefd_2[1], cval, 1);
}
} else {
char cval[] = {'0'}; /* Parent writes buf to pipe 1 */
close(pipefd_1[0]); /* Close unused read end */
close(pipefd_2[1]);
while (atoi(cval) != 9) {
write(pipefd_1[1], cval, 1);
read(pipefd_2[0], cval, 1);
printf("Parent print %d\n", atoi(cval));
cval[0] += 1;
}
}
}
Output

cannot execute execl after fork

My goal is to create N process child with one process father.
I am using two files.The first is named forkn.c which contains this code:
#include <sys/types.h>
#include <wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char* argv[] )
{
int status =4;
int i=1;
int wpid;
for (int cpt=0;cpt<atoi(argv[1]);cpt++)
{
if (i>0)
{
i=fork();
if(i>0)
printf("I create process number %d \n",cpt+1);
}
}
//The father process created argv [1] son ​​process. This ensures a single father and argv [1] son ​​process
if(i==0)
{
execl("~/tpBash/tp2/argv[2]","argv[2]",(char*) NULL);
}
if (i>0)
{
for(int cpt=0;cpt<atoi(argv[1]);cpt++)
{
wait(&status) ;
}
printf("I am the father, I waited all my son processes, I finished \n");
}
}
And there is the code of trait2.c:
#include <sys/types.h>
#include <wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
int status =4;
printf("I am the child process,my PID is : %d \n",getpid());
exit(status);
}
After i compile :
gcc -std=c99 forkn.c -o forkn
gcc -std=c99 trait2.c -o trait2
And then run the executable file :
./forkn 3 trait2
the first arguments is N (the number of child process to create ) and the second argument is the name of file to execute.
My problem is that the child process does not work.
Any idea please
The problem you are seeing is related to the way you're trying to do string interpolation:
execl("~/tpBash/tp2/argv[2]","argv[2]",(char*) NULL);
I suggest you try snprintf:
char buffer[ENOUGH];
snprintf(buffer, sizeof buffer, "%s/tpBash/.../...%s", home, argv[2]);
execl(buffer, argv[2]...);
Also, the second argument to execl should be argv[2] and not "argv[2]".

execl() works on one of my code, but doesn't work on another

I already used execl() in code, and it worked well.
But this time, I really have no idea why it doesn't work.
So here's the code that do not work
#include <unistd.h>
#include <stdio.h>
int main()
{
int i = 896;
printf("please\n");
execl("home/ubuntu/server/LC/admin/admin", (char*)i, NULL);
printf("i have no idea why\n");
return 0;
}
And here's the admin.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/msg.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int mid = argv[0];
printf("hi from child\n");
printf("%d\n", mid);
return 0;
}
Of course I compiled admin.c to admin, and the path isn't wrong.
>ls
admin admin.c why why.c
>pwd
/home/ubuntu/server/LC/admin
>./admin
hi from child
-1180858374
>./why
please
i have no ida why
Anyone know why it doesn't work?
My C is a bit rusty but your code made many rookie mistakes.
execl will replace the current process if it succeeds. So the last line ("i have no idea why") won't print if the child can launch successfully. Which means...
execl failed and you didn't check for it! Hint: check the typecast to char *.
You cast an int to a char * in the execl call, then again when you launch the child (admin). This is a big no-no in C. It freely allows you to misinterpret types. The only warning is most often a crash. GGC will warn you about it. I don't know about the compiler on AWS.
Check your array's bound! You don't know how many parameters admin was launched with. argv[0] always exist because it contains a representation of the program name. argv[1] may not be defined. Accessing array out-of-bound is an undefined behavior and highly dangerous.
The standard way to start another process in C is to fork the parent, then call one of the functions in the exec family to start the other process.
Consider this instead (I took the liberty to emit different messages to make them clearer).
parent.c
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main()
{
int i = 896;
char str[15];
int pid;
printf("Hello from parent\n");
sprintf(str, "%d", i); // convert the number into string
pid = fork();
if (pid == -1)
{
printf("Fork failed\n");
}
else if (pid == 0)
{
printf("Continue from parent\n");
}
else
{
// start the child process
execl("home/ubuntu/server/LC/admin/admin", str, NULL);
// check if it started properly
if (errno != 0)
{
printf("Error launching child process: %s\n", strerror(errno));
return 1;
}
}
printf("Goodbye from parent\n");
return 0;
}
admin.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/msg.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
char * mid;
// argc is always 1 or more
if (argc >= 2)
mid = argv[1];
else
mid = "<nothing>";
printf("hello from child\n");
printf("argc = %d, argv[1] = %s\n", argc, mid);
return 0;
}

combine fork execl wait

I have to create prog1 which take one argument with number of children have to create. (example "./prog1 5" - will create 5 children) Each of children will generate random number from 1 to 20. This number will be given to execl which will start prog2 (in same folder) which take as a argument this random number. Prog2 should sleep this random number time. After that it should return this random number to parent.
I created something like this but it still don't work properly.
prog1:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int n, i, pid;
int u = getppid();
int procesy = 0;
pid_t proc_id;
n = atoi(argv[1]);
for(i = 0; i < n; i++)
{
proc_id = fork();
if(proc_id==0)
{
srand(getpid());
u = 1 + rand()%20;
execl("./prog2", "prog2", u,0);
}
else
{
procesy++;
}
}
if(u == getppid())
{
for(i = 0; i < n; i++)
{
pid = wait(&u);
printf("Process %d ende\n", pid);
procesy--;
}
if(procesy == 0) printf("endc\n");
}
return 1;
}
prog2:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int n;
n = atoi(argv[1]);
sleep(n);
exit(n);
}
Change your loop to look something like the following, in order to properly call execl():
if(proc_id==0)
{
char arg[16];
srand(getpid());
sprintf(arg, "%d", 1 + rand()%20);
execl("./prog2", "prog2", arg, 0);
printf("I should not be here!\n");
exit(-1);
}
Then get rid of if(u == getppid()) (but keep the contents of the conditional). It seems with that if you were trying to filter out the child from running that block. When execl() works, the child is not going to run anything in this code after the execl()... the printf and exit I added will not be run. Those lines will only run if execl() fails, and in this simple case, the only way it will fail is if you've provided improper arguments.

Resources