I am trying to find a way to make this algorithm run concurrent and be simultaneous. So far it has only 1 for-loop that reads each file and then makes a process for each file.
I believe this algorithm runs sequentially which is not what I want...
I thought about creating an outer for-loop where I put the wait(null) and read command. But when I tried it did not work, didn't produce output. Currently I have the wait(null) command in the parent process.
Any suggestions?
Code:
int main(int argc, char *argv[])
{
// ATTRIBUTES //
int freq[argc-1], ipc[argc][2], nbytes, i;// freq of words, pipes, counters
ssize_t errorfi;
char readbuffer[9999];
char** k = malloc(50);
char** op = malloc(50);
if(argc == ONE) { // there are no files given, throw error and close
fprintf(stderr, "There are no files found from %s\n", argv[0]);
exit(0);
}
for(i = 1; i < argc; i++) // creates pipes for ipc
pipe(ipc[i]);
pid_t pids[argc-1]; // array of pids
for(i = 1; i < argc; i++) { // reads input after position 0(a.out)
pid_t pid = fork(); // creates process
pids[i-1] = pid;
if( pid < 0 ) { // bad fork process: error
perror("bad fork");
exit(0);
}
else if(pid > 0) { //parent process
close(ipc[i][1]);
wait(NULL);
nbytes = read(ipc[i][0], readbuffer, sizeof(readbuffer));
if(nbytes > 0)
printf("%s\n", readbuffer);
}
else if(pid == 0) { // child process
close(ipc[i][0]);
k = inputReader(argv[i]); // finds filename,w1,w2,w3,uniqueWords
char info[50] = "";
strcat(info, k[0]);
strcat(info, " ");
strcat(info, k[1]);
strcat(info, " ");
strcat(info, k[2]);
strcat(info, " ");
strcat(info, k[3]);
strcat(info, " ");
strcat(info, k[4]);
int uniqueWordint = atoi(k[4]);
freq[i-1] = uniqueWordint; // freq of all uniqueWords
errorfi = write(ipc[i][1], info, strlen(info)+1); // writes info to pipe i
if (errorfi < 0 ) {
fprintf(stderr, "error found when writing in pipe errofi: %d\n", errorfi);
exit(0);
}
exit(0); // close process
} // closes child process
} // closes for-loop for each process
for(j = 0; j < argc-1; j++) {
wait(2); // if i put read command here it won't work
}
return(0); // close main
}
This is pretty much a sequential execution indeed. The parent-process enters a loop, forks a child-process, and then it won't continue to the next loop until that child-process is done.
What you could do is create a pid_t array of size argc, to store each fork()'s return value.
Also create a new loop after "for-loop for each process", where the parent-process would wait for all of his children, using wait(2) or waitpid(2), depending on whether you need to process each child's result in the specific order or not, and continue processing them (reading or whatever needed.
Related
My program uses systems calls to count number of words in a file, and I need a way to check when all child have exited so that I can print a statement saying "x files have been counted". So I need a way to stop it from printing the print statement twice, and it should only print once at the end.
My output:
Child process for input_file_1.txt: word count is: 6
All 2 files have been counted
Child process for input_file_2.txt: word count is: 14
All 2 files have been counted
my main code (assume the wordCount function is correct):
int main(int argc , char *argv[]){
int *pid;
int parentPid, status;
if (argc < 2){
printf("Usage: a.out(./a.out) filename1 filename2 ..\n");
return -1;
}
pid = (int*)malloc(argc-1 * sizeof(int));
parentPid = getpid();
for (int i = 1; i < argc; i++){
pid = fork();
if (pid == 0){
if (countWord(parentPid,argv[i]) > 0)
printf("Child process for %s: word count is: %d\n",argv[i],countWord(parentPid,argv[i]));
}
else{
if(parentPid == getpid()){
wait(&status);
if (WIFEXITED(status) != 0)
printf("All %d files have been counted\n",argc - 1);
}
}
}
return 0;
}
I'm trying to use exec to execute a list of commands given as arguments.
Example input when In run the program would be ./assn2 ls date.
When I do this only the first command is executed.
#include<stdio.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<unistd.h>
int main(int argc, char *argv[])
{
int args = argc-1;
pid_t childpid = fork();
// error
if (childpid < 0)
{
perror("fork() error");
exit(-1);
}
// parent process
if (childpid != 0)
{
printf("Parent Process started, now waiting for ID: %d\n", childpid);
wait(NULL);
printf("Parent Process resumeed. Child exit code 0. Now terminating\n");
exit(0);
}
// child process
if (args > 0)
{
printf("Child process has begun. %d argument/s provided\n", args);
int i;
for (i = 1; i <= argc; i++)
{
execlp(argv[i], argv[i], NULL);
}
execvp(argv[1], argv);
}
else
{
printf("No arguments provided, terminating child\n");
}
return 0;
}
Once the first child process execs (and succeeds), the for loop no longer continues because the an execlp would just replace the current process image with the command being exec'ed.
What you want to do is to loop over the command line arguments in the parent process and exec once for each of the command. Something like is probably what you're after:
for(int i = 1; i < argc; i++) {
pid_t pid = fork();
if (pid == 0) {
execlp(argv[i] ,argv[i], (char*)0);
perror("exec");
} else if (pid > 0) {
wait(NULL);
} else {
perror("fork");
exit(1);
}
}
What are you trying to achieve with the sequential calls to execlp() and execvp()? These functions are not meant to return. I think you should read the ref:
The exec() family of functions replaces the current process image with a new process image. [..] The exec() functions only return if an error has occurred.
As a result you cannot execute them one after another in the same process.
Read about fork():
fork() creates a new process by duplicating the calling process.
Moreover, here:
for(i = 1; i <= argc; i++)
you go out of bounds, since argv starts indexing from 0, and ends at argc - 1.
Chnage it to:
for(i = 1; i < argc; i++)
I want to do that 2 child processes will put their names and wait until other process put his name. For instance, if there are first and second process, first will put her name and will wait for other's name in screen. So I want to work with processes and I wanna to see they are working sequentially.
Output:
first
second
first
second
first
second
I just tried something about C(linux).
int main(void)
{
pid_t child_a, child_b;
int pipe1[2], pipe2[2];
char mesazhi1[] = "first";
char mesazhi2[] = "second";
char buf[1024];
int first_pipe = pipe(pipe1);
pipe(pipe2);
if(first_pipe == -1){
perror("pipe");
exit(1);
}
child_a = fork();
if (child_a == 0)
{
/* Child A code */
int i;
for (i = 0; i < 3; i++)
{
write(pipe1[1],mesazhi1, strlen(mesazhi1) + 1);
//printf("first\n");
int a = read(pipe2[0], buf, strlen(mesazhi2) + 1);
printf("%s - %d\n", buf, a);
}
}
else
{
child_b = fork();
if (child_b == 0)
{
int i;
for (i = 0; i < 3; i++)
{
write(pipe2[1],mesazhi2, strlen(mesazhi2) + 1);
//printf("second\n");
int a = read(pipe1[0], buf, strlen(mesazhi1) + 1);
printf("%s - %d\n", buf, a);
}
}
else
{
/* Parent Code */
int returnStatusA,returnStatusB;
waitpid(child_a, &returnStatusA, 0); // Parent process waits here for child to terminate.
waitpid(child_b, &returnStatusB, 0); // Parent process waits here for child to terminate.
if (returnStatusA == 0 && returnStatusB == 0) // Verify child process terminated without error.
{
printf("%s\n", "The child processes terminated normally.\n");
}
if (returnStatusA == 1 && returnStatusB == 1)
{
printf("%s\n", "The child processes terminated with an error!. \n" );
}
}
}
}
It is putting name randomly. I mean that I think, sometimes second process works faster than first. Output like that:
first
second
second
first
second
...
So why second process doesn't wait for first one, because I think that read() function should wait until there is something in pipe1.
In the posted code, both processes write to their respective pipes, and then read. After that, it's a race to see which process gets to print first.
For a more controlled situation, have child B call read and printf before calling write. That way B has to wait for A before printing, and vice versa.
if (child_b == 0)
{
int i;
for (i = 0; i < 3; i++)
{
int a = read(pipe1[0], buf, strlen(mesazhi1) + 1);
printf("%s - %d\n", buf, a);
write(pipe2[1],mesazhi2, strlen(mesazhi2) + 1);
}
}
Would this code be considered concurrent or sequential? I think it is concurrent because there a for-loop and it creates processes and for each process there a child,parent, etc... so they all run in parallel. Would this be right?
Code:
int main(int argc, char *argv[])
{
// ATTRIBUTES //
int freq[argc-1], ipc[argc][2], nbytes, i;// freq of words, pipes, counters
ssize_t errorfi;
char readbuffer[9999];
char** k = malloc(50);
char** op = malloc(50);
if(argc == ONE) { // there are no files given, throw error and close
fprintf(stderr, "There are no files found from %s\n", argv[0]);
exit(0);
}
for(i = 1; i < argc; i++) // creates pipes for ipc
pipe(ipc[i]);
for(i = 1; i < argc; i++) { // reads input after position 0(a.out)
pid_t pid = fork(); // creates process
if( pid < 0 ) { // bad fork process: error
perror("bad fork");
exit(0);
}
else if(pid > 0) { //parent process
close(ipc[i][1]);
wait(NULL);
nbytes = read(ipc[i][0], readbuffer, sizeof(readbuffer));
if(nbytes > 0)
printf("%s\n", readbuffer);
}
else if(pid == 0) { // child process
close(ipc[i][0]);
k = inputReader(argv[i]); // finds filename,w1,w2,w3,uniqueWords
char info[50];
strcat(info, k[0]);
strcat(info, " ");
strcat(info, k[1]);
strcat(info, " ");
strcat(info, k[2]);
strcat(info, " ");
strcat(info, k[3]);
strcat(info, " ");
strcat(info, k[4]);
int uniqueWordint = atoi(k[4]);
freq[i-1] = uniqueWordint; // freq of all uniqueWords
errorfi = write(ipc[i][1], info, strlen(info)+1); // writes info to pipe i
if (errorfi < 0 ) {
fprintf(stderr, "error found when writing in pipe errofi: %d\n", errorfi);
exit(0);
}
exit(0); // close process
}// closes child process
} // closes for-loop for each process
return(0); // close main
} // closes main
I've been working on creating my own Unix Shell in C to get practice with its interworkings...I'm having some issues getting my process to run in the background while allowing my shell to continue taking user input. If you could take the time to dissect what I've got below it would be much appreciated!
My variables are below, just incase that helps understand things more...
#define TRUE 1
static char user_input = '\0';
static char *cmd_argv[5]; // array of strings of command
static int cmd_argc = 0; // # words of command
static char buffer[50]; // input line buffer
static int buffer_characters = 0;
int jobs_list_size = 0;
/* int pid; */
int status;
int jobs_list[50];
Here is my main function.
int main(int argc, char **argv)
{
printf("[MYSHELL] $ ");
while (TRUE) {
user_input = getchar();
switch (user_input) {
case EOF:
exit(-1);
case '\n':
printf("[MYSHELL] $ ");
break;
default:
// parse input into cmd_argv - store # commands in cmd_argc
parse_input();
//check for zombie processes
check_zombies();
if(handle_commands() == 0)
create_process();
printf("\n[MYSHELL] $ ");
}
}
printf("\n[MYSHELL] $ ");
return 0;
}
Parse Input...I know, I can't get readline to work on this box :(
If provided the & operator, create the job in the background... (see below)
void parse_input()
{
// clears command line
while (cmd_argc != 0) {
cmd_argv[cmd_argc] = NULL;
cmd_argc--;
}
buffer_characters = 0;
// get command line input
while ((user_input != '\n') && (buffer_characters < 50)) {
buffer[buffer_characters++] = user_input;
user_input = getchar();
}
// clear buffer
buffer[buffer_characters] = 0x00;
// populate cmd_argv - array of commands
char *buffer_pointer;
buffer_pointer = strtok(buffer, " ");
while (buffer_pointer != NULL) {
cmd_argv[cmd_argc] = buffer_pointer;
buffer_pointer = strtok(NULL, " ");
//check for background process execution
if(strcmp(cmd_argv[cmd_argc], "&")==0){
printf("Started job %d\n", getpid());
make_background_job();
}
cmd_argc++;
}
}
Make background job. Closes child process STDIN, opens new STDIN, and executes.
void make_background_job()
{
int pid;
pid = fork();
fclose(stdin); // close child's stdin
fopen("/dev/null", "r"); // open a new stdin that is always empty
fprintf(stderr, "Child pid = %d\n", getpid());
//add pid to jobs list
jobs_list[jobs_list_size] = getpid();
/* printf("jobs list %d", *jobs_list[jobs_list_size]); */
jobs_list_size++;
execvp(*cmd_argv,cmd_argv);
// this should never be reached, unless there is an error
fprintf (stderr, "unknown command: %s\n", cmd_argv[0]);
}
The meat of my job control. Fork spawns child, returns 0 for child and PID for parent.
void create_process()
{
pid_t pid;
pid = fork();
status = 0;
switch(pid){
case -1:
perror("[MYSHELL ] $ (fork)");
exit(EXIT_FAILURE);
case 0:
make_background_job();
printf("\n\n----Just made background job in case 0 of create_process----\n\n");
break;
default:
printf("\n\n----Default case of create_process----\n\n");
// parent process, waiting on child...
waitpid(pid, &status, 0);
if (status != 0)
fprintf (stderr, "error: %s exited with status code %d\n", cmd_argv[0], status);
else
break;
}
}
My problem is when I execute a job in the background, its executing the command twice, and exiting out of the shell. (It functions correctly otherwise if no background process is enabled). Where am I getting confused? I think it may have to do with issues regarding my PID's, as I'm not populating the list correctly either in 'make_background_job'
Here is my output, the example.sh just throws out helloWorld:
[MYSHELL] $ ./example.sh &
Started job 15479
Child pid = 15479
Child pid = 15481
Hello World
Hello World
What seems to happen is
in main() the prompt is displayed, expecting a command
when a command is input, parse_input() is called
it builds the commands array until it finds & where it calls make_background_jobs()
that function forks quickly, and executes in parallel, in two processes, execvp()
execvp() replaces each of the two processes to execute the command
thus two "Hello world" appear.
The problem is in make_background_jobs() where, I think, the expected behavior was that only one of the two processes should execute the command, and the other one (father) returns, to keep the program active.
This can be solved by modifying that function, making the father process return:
void make_background_job()
{
int pid;
pid = fork();
if (pid) return; // The father process returns to keep program active
...
edit
I gave it a try, removing the unnecessary
void make_background_job()
{
int pid;
pid = fork();
if ( ! pid)
{
fclose(stdin); // close child's stdin
fopen("/dev/null", "r"); // open a new stdin that is always empty
fprintf(stderr, "Child Job pid = %d\n", getpid());
//add pid to jobs list
jobs_list[jobs_list_size] = getpid();
/* printf("jobs list %d", *jobs_list[jobs_list_size]); */
jobs_list_size++;
execvp(*cmd_argv,cmd_argv);
// this should never be reached, unless there is an error
fprintf (stderr, "unknown command: %s\n", cmd_argv[0]);
exit(1);
}
waitpid(pid, &status, 0);
}
The background job is created in another process. The father waits for the job to complete.
void parse_input()
{
// clears command line
while (cmd_argc != 0) {
cmd_argv[cmd_argc] = NULL;
cmd_argc--;
}
buffer_characters = 0;
// get command line input
while ((user_input != '\n') && (buffer_characters < 50)) {
buffer[buffer_characters++] = user_input;
user_input = getchar();
}
// clear buffer
buffer[buffer_characters] = 0x00;
// populate cmd_argv - array of commands
char *buffer_pointer;
buffer_pointer = strtok(buffer, " ");
int ok = 0;
while (buffer_pointer != NULL) {
cmd_argv[cmd_argc] = buffer_pointer;
buffer_pointer = strtok(NULL, " ");
//check for background process execution
if(strcmp(cmd_argv[cmd_argc], "&")==0){
ok = 1;
break;
}
cmd_argc++;
}
if (!ok) cmd_argv[cmd_argc = 0] = NULL; // If no & found, reset commands
}
Only parses input.
Below a new handle_commands() that return 0 if there is a command to play, and the main follows.
int handle_commands() { return cmd_argc > 0 ? 0:1; }
int main(int argc, char **argv)
{
printf("[MYSHELL] $ ");
while (TRUE) {
user_input = getchar();
switch (user_input) {
case EOF:
exit(-1);
case '\n':
printf("[MYSHELL] $ ");
break;
default:
// parse input into cmd_argv - store # commands in cmd_argc
parse_input();
//check for zombie processes
check_zombies();
if(handle_commands() == 0)
make_background_job(); // Call directly the bg job
printf("\n[MYSHELL] $ ");
}
}
printf("\n[MYSHELL] $ ");
return 0;
}
The main() calls directly make_background_job().
There is only one fork() in make_background_job. create_process() has been removed.