C beginner here. My C program is supposed to read a file input.txt line by line and then write a line to one of the mapper_pipes. There are four lines of text and each line gets sent to a different mapper pipe. 4 processes are forked, one for each of the lines of text. The program is properly reading the lines of text from the file, I know because I console log them in the send_line_to_mapper function. The code inside the mapper forks which seem to be working since printf("hello from mapper/n"); prints but it does not print the messages sent through the pipe here printf("forked mapper read line: %s\n", ibuf);. Appreciate any help.
Update 2
*Edit: I moved close to immediatly after fork and now its working.
Closed write end of the pipe after writing to pipe and after forking process that reads from pipe, still not reading EOF.
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#define BUFFER_SIZE 1024
#define ALPHA_OFFSET 97
#define LETTERS 26
const int NUM_OF_MAPPERS = 4;
const int NUM_OF_REDUCERS = 26;
const int PIPE_READ_END = 0;
const int PIPE_WRITE_END = 1;
const int PIPE_BUFFER_SIZE = 1000;
int mapper_pipes[4][2];
int reducer_pipes[26][2];
void pipe_wrapper(int pipefd[]) {
int ret = pipe(pipefd);
if (ret == -1) {
perror("Error. Failed when trying to create pipes.");
exit(EXIT_FAILURE);
}
}
void create_mapper_pipes(void) {
int i;
for (i = 0; i < NUM_OF_MAPPERS; i++) {
pipe_wrapper(mapper_pipes[i]);
}
}
void create_reducer_pipes(void) {
int i;
for (i=0; i < NUM_OF_REDUCERS; i++) {
pipe_wrapper(reducer_pipes[i]);
}
}
// Prints an error msg and exits if one occurs. Else, returns the system call value.
int print_if_err(int syscall_val, const char* syscall_name) {
if (syscall_val < 0) {
perror(syscall_name);
exit(errno);
} else {
//No syscall error we can return
return syscall_val;
}
}
void fork_mappers(void) {
/* Constants useful to all children */
char ibuf[PIPE_BUFFER_SIZE]; // input pipe buffer
int rlen = 0;
int i;
for (i=0; i<NUM_OF_MAPPERS; i++) {
pid_t mapper_pid = print_if_err(fork(), "fork");
if (mapper_pid == 0) {
close(mapper_pipes[i][PIPE_WRITE_END]);
rlen = print_if_err(read(mapper_pipes[i][PIPE_READ_END], ibuf, 1000), "read");
while(rlen > 0) {
printf("read line from forked_mappers, p%d: %s\n", i, ibuf);
rlen = print_if_err(read(mapper_pipes[i][PIPE_READ_END], ibuf, 1000), "read");
}
_exit(0);
}
}
}
void fork_reducers(void) {
int i;
for (i = 0; i < NUM_OF_REDUCERS; i++) {
pid_t reducer_pid = print_if_err(fork(), "fork");
if (reducer_pid == 0) {
}
}
}
void send_lines_to_mappers(void) {
int wlen = 0;
char obuf[PIPE_BUFFER_SIZE];
int ob_size;
int count = 0;
char buff[BUFFER_SIZE]; // a buffer for each line of the file
FILE *input_file = fopen("input.txt", "r");
// read the input file line by line
while(fgets(buff, BUFFER_SIZE, input_file) > 0) {
printf("read line from send_lin_to_mappers: %s\n", buff);
ob_size = sizeof buff;
switch(count) {
case 0 :
write(mapper_pipes[0][PIPE_WRITE_END], buff, ob_size);
close(mapper_pipes[0][PIPE_WRITE_END]);
break;
case 1 :
write(mapper_pipes[1][PIPE_WRITE_END], buff, ob_size);
close(mapper_pipes[1][PIPE_WRITE_END]);
break;
case 2 :
write(mapper_pipes[2][PIPE_WRITE_END], buff, ob_size);
close(mapper_pipes[2][PIPE_WRITE_END]);
break;
case 3 :
write(mapper_pipes[3][PIPE_WRITE_END], buff, ob_size);
close(mapper_pipes[3][PIPE_WRITE_END]);
break;
default :
printf("you did something wrong in send_lines_to_mappers loop");
}
count++;
}
fclose(input_file);
}
int main(void) {
// Setup the mapper pipes
create_mapper_pipes();
create_reducer_pipes();
fork_mappers();
//fork_reducers();
send_lines_to_mappers();
return 0;
}
Updated Code After Help From Comments
Now everything seems to be working but forked processes in fork_mappers never leave while loop and exit.
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#define BUFFER_SIZE 1024
#define ALPHA_OFFSET 97
#define LETTERS 26
const int NUM_OF_MAPPERS = 4;
const int NUM_OF_REDUCERS = 26;
const int PIPE_READ_END = 0;
const int PIPE_WRITE_END = 1;
const int PIPE_BUFFER_SIZE = 1000;
int mapper_pipes[4][2];
int reducer_pipes[26][2];
void pipe_wrapper(int pipefd[]) {
int ret = pipe(pipefd);
if (ret == -1) {
perror("Error. Failed when trying to create pipes.");
exit(EXIT_FAILURE);
}
}
void create_mapper_pipes(void) {
int i;
for (i = 0; i < NUM_OF_MAPPERS; i++) {
pipe_wrapper(mapper_pipes[i]);
}
}
void create_reducer_pipes(void) {
int i;
for (i=0; i < NUM_OF_REDUCERS; i++) {
pipe_wrapper(reducer_pipes[i]);
}
}
// Prints an error msg and exits if one occurs. Else, returns the system call value.
int print_if_err(int syscall_val, const char* syscall_name) {
if (syscall_val < 0) {
perror(syscall_name);
exit(errno);
} else {
//No syscall error we can return
return syscall_val;
}
}
void fork_mappers(void) {
/* Constants useful to all children */
char ibuf[PIPE_BUFFER_SIZE]; // input pipe buffer
int rlen = 0;
int i;
for (i=0; i<NUM_OF_MAPPERS; i++) {
pid_t mapper_pid = print_if_err(fork(), "fork");
if (mapper_pid == 0) {
rlen = print_if_err(read(mapper_pipes[i][PIPE_READ_END], ibuf, 1000), "read");
while(rlen > 0) {
printf("read line from forked_mappers, p%d: %s\n", i, ibuf);
rlen = print_if_err(read(mapper_pipes[i][PIPE_READ_END], ibuf, 1000), "read");
}
_exit(0);
}
}
}
void fork_reducers(void) {
int i;
for (i = 0; i < NUM_OF_REDUCERS; i++) {
pid_t reducer_pid = print_if_err(fork(), "fork");
if (reducer_pid == 0) {
}
}
}
void send_lines_to_mappers(void) {
int wlen = 0;
char obuf[PIPE_BUFFER_SIZE];
int ob_size;
int count = 0;
char buff[BUFFER_SIZE]; // a buffer for each line of the file
FILE *input_file = fopen("input.txt", "r");
// read the input file line by line
while(fgets(buff, BUFFER_SIZE, input_file) > 0) {
printf("read line from send_lin_to_mappers: %s\n", buff);
ob_size = sizeof buff;
switch(count) {
case 0 :
write(mapper_pipes[0][PIPE_WRITE_END], buff, ob_size);
break;
case 1 :
write(mapper_pipes[1][PIPE_WRITE_END], buff, ob_size);
break;
case 2 :
write(mapper_pipes[2][PIPE_WRITE_END], buff, ob_size);
break;
case 3 :
write(mapper_pipes[3][PIPE_WRITE_END], buff, ob_size);
break;
default :
printf("you did something wrong in send_lines_to_mappers loop");
}
count++;
}
fclose(input_file);
}
int main(void) {
// Setup the mapper pipes
create_mapper_pipes();
create_reducer_pipes();
fork_mappers();
//fork_reducers();
send_lines_to_mappers();
return 0;
}
C Code
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#define BUFFER_SIZE 1024
#define ALPHA_OFFSET 97
#define LETTERS 26
const int NUM_OF_MAPPERS = 4;
const int NUM_OF_REDUCERS = 26;
const int PIPE_READ_END = 0;
const int PIPE_WRITE_END = 1;
const int PIPE_BUFFER_SIZE = 32;
int mapper_pipes[4][2];
int reducer_pipes[26][2];
void pipe_wrapper(int pipefd[]) {
int ret = pipe(pipefd);
if (ret == -1) {
perror("Error. Failed when trying to create pipes.");
exit(EXIT_FAILURE);
}
}
void create_mapper_pipes(void) {
int i;
for (i = 0; i < NUM_OF_MAPPERS; i++) {
pipe_wrapper(mapper_pipes[i]);
}
}
void create_reducer_pipes(void) {
int i;
for (i=0; i < NUM_OF_REDUCERS; i++) {
pipe_wrapper(reducer_pipes[i]);
}
}
// Prints an error msg and exits if one occurs. Else, returns the system call value.
int print_if_err(int syscall_val, const char* syscall_name) {
if (syscall_val < 0) {
perror(syscall_name);
exit(errno);
} else {
//No syscall error we can return
return syscall_val;
}
}
void fork_mappers(void) {
/* Constants useful to all children */
char ibuf[PIPE_BUFFER_SIZE]; // input pipe buffer
int rlen = 0;
int i;
for (i=0; i<NUM_OF_MAPPERS; i++) {
pid_t mapper_pid = print_if_err(fork(), "fork");
printf("%d\n", mapper_pid);
if (mapper_pid == 0) {
printf("hello from mapper/n");
rlen = print_if_err(read(mapper_pipes[i][PIPE_READ_END], ibuf, 1000), "read");
while(rlen > 0) {
printf("forked mapper read line: %s\n", ibuf);
rlen = print_if_err(read(mapper_pipes[i][PIPE_READ_END], ibuf, 1000), "read");
}
_exit(0);
}
}
}
void fork_reducers(void) {
int i;
for (i = 0; i < NUM_OF_REDUCERS; i++) {
pid_t reducer_pid = print_if_err(fork(), "fork");
if (reducer_pid == 0) {
}
}
}
void send_lines_to_mappers(void) {
int wlen = 0;
char obuf[PIPE_BUFFER_SIZE];
int obptr = 0;
int count = 0;
char buff[BUFFER_SIZE]; // a buffer for each line of the file
FILE *input_file = fopen("input.txt", "r");
// read the input file line by line
while(fgets(buff, BUFFER_SIZE, input_file) > 0) {
printf("read line: %s\n", buff);
switch(count) {
case 0 :
write(mapper_pipes[0][PIPE_WRITE_END], obuf, obptr);
break;
case 1 :
write(mapper_pipes[1][PIPE_WRITE_END], obuf, obptr);
break;
case 2 :
write(mapper_pipes[2][PIPE_WRITE_END], obuf, obptr);
break;
case 3 :
write(mapper_pipes[3][PIPE_WRITE_END], obuf, obptr);
break;
default :
printf("you did something wrong in send_lines_to_mappers loop");
}
count++;
}
fclose(input_file);
}
int main(void) {
// Setup the mapper pipes
create_mapper_pipes();
create_reducer_pipes();
fork_mappers();
//fork_reducers();
send_lines_to_mappers();
return 0;
}
Output
9592
9593
9594
9595
read line: I like coding in C.
read line: I like manually allocating memory, and opening the registers window in Visual Studio to see the values of the eax register and blitting graphics to the screen and all the stuff that Dr. Dobbs wrote about in the 90s.
read line: My programming friends seem to believe that understanding this level of programming is good in a hand-wavy, theoretical sense, but when you consider all the web development, Java frameworks, and existing libraries most programmers today rely on, it's hard to really pin down a solid answer to the question "Why learn C?"
read line: This is my attempt to answer that question, and I believe it comes down to the basic programming concept of abstraction.
0
0
0
0
Related
I'm trying to write a program which read output of another program and write to the program as input.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char str[30];
printf("Input string : ");
fflush(stdout);
scanf("%s", &str);
fflush(stdout);
printf("entered string is %s\n", str);
return 0;
}
This program1 is a simple program reading input from stdin and print the string entered.
And here in the program2, I tried to create 2 pipes and execute the program1.
And read the output of program1 and get user input and deliver the string user entered to program1.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
typedef struct pipe_rw
{
pid_t cpid;
int pipe_r[2];
int pipe_w[2];
} RWPIPE;
char *get_user_input(void)
{
char buf[128];
char *input;
char ch;
int n;
int len = 0;
memset(buf, 0x0, 128);
while((ch = fgetc(stdin)) != 0xa)
{
buf[len] = ch;
len++;
}
input = malloc(sizeof(char) * (len));
strncpy(input, buf, (len));
return input;
}
int pclose_rw(RWPIPE *rwp)
{
int status, ret = 0;
if (rwp)
{
if (rwp->cpid > 0)
{
kill(rwp->cpid, SIGTERM);
do {
ret = waitpid(rwp->cpid, &status, WUNTRACED|WCONTINUED);
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
}
close(rwp->pipe_r[0]);
close(rwp->pipe_w[1]);
free(rwp);
}
return ret;
}
RWPIPE *popen_rw(const char *command)
{
RWPIPE *rwp = (RWPIPE *)malloc(sizeof(*rwp));
if (rwp == NULL)
return NULL;
memset(rwp, 0x00, sizeof(*rwp));
if (pipe(rwp->pipe_r) != 0 || pipe(rwp->pipe_w) != 0)
{
free(rwp);
return NULL;
}
rwp->cpid = fork();
if (rwp->cpid == -1)
{
free(rwp);
return NULL;
}
if (rwp->cpid == 0)
{
dup2(rwp->pipe_w[0], STDIN_FILENO);
dup2(rwp->pipe_r[1], STDOUT_FILENO);
close(rwp->pipe_r[0]);
close(rwp->pipe_r[1]);
close(rwp->pipe_w[0]);
close(rwp->pipe_w[1]);
execl(command, command, NULL);
printf("Error: fail to exec command - %s ..\n", command);
exit (1);
}
else
{
close(rwp->pipe_r[1]);
close(rwp->pipe_w[0]);
}
return rwp;
}
ssize_t read_p(RWPIPE *rwp, void *buf, size_t count)
{
return read(rwp->pipe_r[0], buf, count);
}
ssize_t write_p(RWPIPE *rwp, const void *buf, size_t count)
{
return write(rwp->pipe_w[1], buf, count);
}
int main(void)
{
char rbuf[BUFSIZ], wbuf[BUFSIZ];
int ret, len, n = 0;
char *string;
RWPIPE *rwp = popen_rw("./read_write");
if (rwp == NULL)
{
printf("Error: fail to open command ..\n");
return EXIT_FAILURE;
}
while (1)
{
memset(rbuf, 0x00, sizeof(rbuf));
if (read_p(rwp, rbuf, sizeof(rbuf)) < 1)
{
printf("No more input..\n");
break;
}
printf("%s", rbuf);
string = get_user_input();
len = strlen(string);
ret = write_p(rwp, string, len);
if (ret != len)
{
printf("Write %d bytes (expected %d) ..\n", ret, len);
break;
}
printf("end");
}
pclose_rw(rwp);
return EXIT_SUCCESS;
}
If run the program2 reads output of program1 successfully.
And it gets user input but it failed to give the string entered from user to program1.
[root#localhost test_code]# ./rw_pipe
Input string : 1234
^C
Please give me some ideas why it works like this.
Your primary problem is that the data written to the child does not end with a newline, so the child is not aware that the message is complete (it isn't complete) and the child is still busy reading while the parent is waiting for a response — a deadlock.
This code adds some instrumentation and fixes the problem by including the newline in the string read by get_input().
The original program expects two lots of input (one in response to the prompt from read_write, the other in response to the echoed output), but dies from a SIGPIPE when it tries to send the second input to the now-exited child. The code below circumvents that by ignoring SIGPIPE signals, which means that the parent gets a write error instead of being killed by the signal.
There's an unusual control flow between the two programs, and if you made read_write into an iterative program, you'd see that it generates two outputs for a single input. That's not the way it's usually done, of course. Fixing that is outside of the scope of the immediate exercise, though.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
typedef struct pipe_rw
{
pid_t cpid;
int pipe_r[2];
int pipe_w[2];
} RWPIPE;
static char *get_user_input(void)
{
char buf[128];
char *input;
char ch;
size_t len = 0;
while((ch = fgetc(stdin)) != '\n' && ch != EOF && len < sizeof(buf) - 2)
buf[len++] = ch;
buf[len++] = '\n';
buf[len] = '\0';
input = malloc(sizeof(char) * (len + 1));
strncpy(input, buf, (len + 1));
printf("Got: [%s]\n", input);
return input;
}
static int pclose_rw(RWPIPE *rwp)
{
int status, ret = 0;
if (rwp)
{
if (rwp->cpid > 0)
{
kill(rwp->cpid, SIGTERM);
do {
ret = waitpid(rwp->cpid, &status, WUNTRACED|WCONTINUED);
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
}
close(rwp->pipe_r[0]);
close(rwp->pipe_w[1]);
free(rwp);
}
return ret;
}
static RWPIPE *popen_rw(const char *command)
{
RWPIPE *rwp = (RWPIPE *)malloc(sizeof(*rwp));
if (rwp == NULL)
return NULL;
memset(rwp, 0x00, sizeof(*rwp));
if (pipe(rwp->pipe_r) != 0 || pipe(rwp->pipe_w) != 0)
{
free(rwp);
return NULL;
}
rwp->cpid = fork();
if (rwp->cpid == -1)
{
free(rwp);
return NULL;
}
if (rwp->cpid == 0)
{
dup2(rwp->pipe_w[0], STDIN_FILENO);
dup2(rwp->pipe_r[1], STDOUT_FILENO);
close(rwp->pipe_r[0]);
close(rwp->pipe_r[1]);
close(rwp->pipe_w[0]);
close(rwp->pipe_w[1]);
execl(command, command, NULL);
fprintf(stderr, "Error: fail to exec command '%s'.\n", command);
exit (1);
}
else
{
close(rwp->pipe_r[1]);
close(rwp->pipe_w[0]);
}
return rwp;
}
static ssize_t read_p(RWPIPE *rwp, void *buf, size_t count)
{
return read(rwp->pipe_r[0], buf, count);
}
static ssize_t write_p(RWPIPE *rwp, const void *buf, size_t count)
{
return write(rwp->pipe_w[1], buf, count);
}
int main(void)
{
char rbuf[BUFSIZ];
int ret, len;
char *string;
signal(SIGPIPE, SIG_IGN);
RWPIPE *rwp = popen_rw("./read_write");
if (rwp == NULL)
{
printf("Error: fail to open command ..\n");
return EXIT_FAILURE;
}
while (1)
{
memset(rbuf, 0x00, sizeof(rbuf));
if (read_p(rwp, rbuf, sizeof(rbuf)) <= 0)
{
printf("No more input..\n");
break;
}
printf("From child: [%s]\n", rbuf);
string = get_user_input();
len = strlen(string);
printf("Length %d: [%s]\n", len, string);
ret = write_p(rwp, string, len);
if (ret != len)
{
fprintf(stderr, "Write %d bytes (expected %d) ..\n", ret, len);
break;
}
printf("end cycle\n");
}
printf("End of loop\n");
pclose_rw(rwp);
return EXIT_SUCCESS;
}
Sample run
The program is rwpipe53; the input I typed was Ocelot and Grumble.
$ ./rwpipe53
From child: [Input string : ]
Ocelot
Got: [Ocelot
]
Length 7: [Ocelot
]
end cycle
From child: [entered string is Ocelot
]
Grumble
Got: [Grumble
]
Length 8: [Grumble
]
Write -1 bytes (expected 8) ..
End of loop
$
Note how the square brackets (any pair of marker symbols can be used if you prefer) shows where the data starts and ends. I find that a valuable technique when debugging code.
C beginner here. The function send_chars_to_reducers does not appeared to be getting called inside the forked processes created in fork_mappers function.
C Code
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#define BUFFER_SIZE 1024
#define ALPHA_OFFSET 97
#define LETTERS 26
const int NUM_OF_MAPPERS = 4;
const int NUM_OF_REDUCERS = 26;
const int PIPE_READ_END = 0;
const int PIPE_WRITE_END = 1;
const int PIPE_BUFFER_SIZE = 1000;
int mapper_pipes[4][2];
int reducer_pipes[26][2];
void pipe_wrapper(int pipefd[]) {
int ret = pipe(pipefd);
if (ret == -1) {
perror("Error. Failed when trying to create pipes.");
exit(EXIT_FAILURE);
}
}
void create_mapper_pipes(void) {
int i;
for (i = 0; i < NUM_OF_MAPPERS; i++) {
pipe_wrapper(mapper_pipes[i]);
}
}
void create_reducer_pipes(void) {
int i;
for (i=0; i < NUM_OF_REDUCERS; i++) {
pipe_wrapper(reducer_pipes[i]);
}
}
// Prints an error msg and exits if one occurs. Else, returns the system call value.
int print_if_err(int syscall_val, const char* syscall_name) {
if (syscall_val < 0) {
perror(syscall_name);
exit(errno);
} else {
//No syscall error we can return
return syscall_val;
}
}
void send_chars_to_reducers(void) {
printf("hello from send_chars_to_reducers\n");
}
void fork_mappers(void) {
/* Constants useful to all children */
char ibuf[PIPE_BUFFER_SIZE]; // input pipe buffer
int rlen = 0;
int i;
for (i=0; i<NUM_OF_MAPPERS; i++) {
pid_t mapper_pid = print_if_err(fork(), "fork");
close(mapper_pipes[i][PIPE_WRITE_END]);
if (mapper_pid == 0) {
rlen = print_if_err(read(mapper_pipes[i][PIPE_READ_END], ibuf, 1000), "read");
while(rlen > 0) {
send_chars_to_reducers();
printf("read line from forked_mappers, p%d: %s\n", i, ibuf);
rlen = print_if_err(read(mapper_pipes[i][PIPE_READ_END], ibuf, 1000), "read");
}
_exit(0);
}
}
}
void fork_reducers(void) {
printf("hello from fork_reducers\n");
int i;
for (i = 0; i < NUM_OF_REDUCERS; i++) {
pid_t reducer_pid = print_if_err(fork(), "fork");
if (reducer_pid == 0) {
while (1 == 1) {
}
}
}
}
void send_lines_to_mappers(void) {
int wlen = 0;
char obuf[PIPE_BUFFER_SIZE];
int ob_size;
int count = 0;
char buff[BUFFER_SIZE]; // a buffer for each line of the file
FILE *input_file = fopen("input.txt", "r");
// read the input file line by line
while(fgets(buff, BUFFER_SIZE, input_file) > 0) {
printf("read line from send_lin_to_mappers: %s\n", buff);
ob_size = sizeof buff;
switch(count) {
case 0 :
write(mapper_pipes[0][PIPE_WRITE_END], buff, ob_size);
close(mapper_pipes[0][PIPE_WRITE_END]);
break;
case 1 :
write(mapper_pipes[1][PIPE_WRITE_END], buff, ob_size);
close(mapper_pipes[1][PIPE_WRITE_END]);
break;
case 2 :
write(mapper_pipes[2][PIPE_WRITE_END], buff, ob_size);
close(mapper_pipes[2][PIPE_WRITE_END]);
break;
case 3 :
write(mapper_pipes[3][PIPE_WRITE_END], buff, ob_size);
close(mapper_pipes[3][PIPE_WRITE_END]);
break;
default :
printf("you did something wrong in send_lines_to_mappers loop");
}
count++;
}
fclose(input_file);
}
int main(void) {
// Setup the mapper pipes
create_mapper_pipes();
create_reducer_pipes();
fork_reducers();
fork_mappers();
send_lines_to_mappers();
return 0;
}
Output
hello from fork_reducers
read line from send_lin_to_mappers: I like coding in C.
read line from send_lin_to_mappers: I like manually allocating memory, and opening the registers window in Visual Studio to see the values of the eax register and blitting graphics to the screen and all the stuff that Dr. Dobbs wrote about in the 90s.
read line from send_lin_to_mappers: My programming friends seem to believe that understanding this level of programming is good in a hand-wavy, theoretical sense, but when you consider all the web development, Java frameworks, and existing libraries most programmers today rely on, it's hard to really pin down a solid answer to the question "Why learn C?"
read line from send_lin_to_mappers: This is my attempt to answer that question, and I believe it comes down to the basic programming concept of abstraction.
The problem is the number of processes which share handles.
Each reducer has the pipes open
Each mapper has the pipes open, so the pipes are never properly closed ( the reducers are in a busy loop.
if (mapper_pid == 0) {
int j;
for( j = 0; j < NUM_OF_MAPPERS; j++ ){
close( mapper_pipes[j][PIPE_WRITE_END]);
if( j != i ){
close(mapper_pipes[j][PIPE_READ_END]);
}
}
I commented out the reducer fork's and then fixed the mapper forks to close all the pipe handles in the child except the one we want to read from.
This started to make the program work.
From pipe documentation, you should close the unused bits of a pipe. But you have pre-opened all the pipes (probably better localizing them), and have therefore got to close all the unused handles in the sub-processes to have the OS tidy up the resource correctly
Trying to give a Minimal Viable Example of the problem. Basically the method send_chars_to_reducers sends a character to the proper reducer_pipe. The fork_reducers function remains in its while loop until it receives EOF but it never does even though I close all reducer pipes in send_chars_to_reducers. I know it doesnt exit the while loop because it never prints exiting reducers.
C Code
void send_chars_to_reducers(char * line) {
printf("SEND_CHARS_TO_REDUCERS read: %s\n\n", line);
int i;
int ob_size = 1;
int wlen = 0;
for (i = 0; i < strlen(line); i++) {
if (line[i] >= ALPHA_OFFSET && line[i] < ALPHA_OFFSET + LETTERS) {
int pipe_num = line[i] - ALPHA_OFFSET;
printf("SENDING %c TO REDUCER PIPE %d\n", line[i], pipe_num);
wlen = print_if_err(write(reducer_pipes[pipe_num][PIPE_WRITE_END], &line[i], ob_size), "write");
printf("WROTE %s to REDUCER %d\n", line[i], i);
}
}
close_reducer_pipes();
}
void close_reducer_pipes(void) {
int i;
for (i = 0; i < NUM_OF_REDUCERS; i++) {
close(reducer_pipes[i][PIPE_WRITE_END]);
close(reducer_pipes[i][PIPE_READ_END]);
}
}
void fork_mappers(void) {
/* Constants useful to all children */
char ibuf[PIPE_BUFFER_SIZE]; // input pipe buffer
int rlen = 0;
int i;
for (i=0; i<NUM_OF_MAPPERS; i++) {
pid_t mapper_pid = print_if_err(fork(), "fork");
if (mapper_pid == 0) {
int j;
for (j=0; j < NUM_OF_MAPPERS; j++) {
close(mapper_pipes[i][PIPE_WRITE_END]);
if (j != i) {
close(mapper_pipes[j][PIPE_READ_END]);
}
}
rlen = print_if_err(read(mapper_pipes[i][PIPE_READ_END], ibuf, 1000), "read");
send_chars_to_reducers(ibuf);
close_reducer_pipes();
//printf("forked mapper%d read: %s\n\n", i, ibuf);
close(mapper_pipes[i][PIPE_READ_END]);
_exit(0);
}
}
}
void fork_reducers(void) {
printf("HELLLOOOO FROM REDUCER\n");
char ibuf[PIPE_BUFFER_SIZE]; // input pipe buffer
int rlen = 0;
int i;
for (i = 0; i < NUM_OF_REDUCERS; i++) {
pid_t reducer_pid = print_if_err(fork(), "fork");
if (reducer_pid == 0) {
while (1) {
rlen = print_if_err(read(reducer_pipes[i][PIPE_READ_END], ibuf, 1), "read");
if (rlen > 0) {
printf("REDUCER #%d, read %s\n", i, ibuf);
} else {
break;
}
}
printf("exiting reducer\n");
_exit(0);
}
}
}
Entire C Code
#include <sys/wait.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#define BUFFER_SIZE 1024
#define ALPHA_OFFSET 97
#define LETTERS 26
const int NUM_OF_MAPPERS = 4;
const int NUM_OF_REDUCERS = 26;
const int PIPE_READ_END = 0;
const int PIPE_WRITE_END = 1;
const int PIPE_BUFFER_SIZE = 1000;
int mapper_pipes[4][2];
int reducer_pipes[26][2];
int letter_count[26];
void init_letter_count(void) {
int i;
for (i =0; i < 26; i++) {
letter_count[i] = 0;
}
}
void pipe_wrapper(int pipefd[]) {
int ret = pipe(pipefd);
if (ret == -1) {
perror("Error. Failed when trying to create pipes.");
exit(EXIT_FAILURE);
}
}
void create_mapper_pipes(void) {
int i;
for (i = 0; i < NUM_OF_MAPPERS; i++) {
pipe_wrapper(mapper_pipes[i]);
}
}
void create_reducer_pipes(void) {
int i;
for (i=0; i < NUM_OF_REDUCERS; i++) {
pipe_wrapper(reducer_pipes[i]);
}
}
// Prints an error msg and exits if one occurs. Else, returns the system call value.
int print_if_err(int syscall_val, const char* syscall_name) {
if (syscall_val < 0) {
perror(syscall_name);
exit(errno);
} else {
//No syscall error we can return
return syscall_val;
}
}
void send_chars_to_reducers(char * line) {
printf("SEND_CHARS_TO_REDUCERS read: %s\n\n", line);
int i;
int ob_size = 1;
int wlen = 0;
for (i = 0; i < strlen(line); i++) {
if (line[i] >= ALPHA_OFFSET && line[i] < ALPHA_OFFSET + LETTERS) {
int pipe_num = line[i] - ALPHA_OFFSET;
printf("SENDING %c TO REDUCER PIPE %d\n", line[i], pipe_num);
wlen = print_if_err(write(reducer_pipes[pipe_num][PIPE_WRITE_END], &line[i], ob_size), "write");
printf("WROTE %c to REDUCER %d\n", line[i], pipe_num);
}
}
printf("END OF SEND CHAR FOR LOOP");
close_reducer_pipes();
}
void close_reducer_pipes(void) {
int i;
for (i = 0; i < NUM_OF_REDUCERS; i++) {
print_if_err(close(reducer_pipes[i][PIPE_WRITE_END]), "close");
print_if_err(close(reducer_pipes[i][PIPE_READ_END]), "close");
}
}
void fork_mappers(void) {
/* Constants useful to all children */
char ibuf[PIPE_BUFFER_SIZE]; // input pipe buffer
int rlen = 0;
int i;
for (i=0; i<NUM_OF_MAPPERS; i++) {
pid_t mapper_pid = print_if_err(fork(), "fork");
if (mapper_pid == 0) {
int j;
for (j=0; j < NUM_OF_MAPPERS; j++) {
close(mapper_pipes[i][PIPE_WRITE_END]);
if (j != i) {
close(mapper_pipes[j][PIPE_READ_END]);
}
}
rlen = print_if_err(read(mapper_pipes[i][PIPE_READ_END], ibuf, 1000), "read");
send_chars_to_reducers(ibuf);
//printf("forked mapper%d read: %s\n\n", i, ibuf);
close(mapper_pipes[i][PIPE_READ_END]);
_exit(0);
}
}
}
void fork_reducers(void) {
printf("HELLLOOOO FROM REDUCER\n");
char ibuf[PIPE_BUFFER_SIZE]; // input pipe buffer
int rlen = 0;
int i;
for (i = 0; i < NUM_OF_REDUCERS; i++) {
pid_t reducer_pid = print_if_err(fork(), "fork");
if (reducer_pid == 0) {
while (1) {
rlen = print_if_err(read(reducer_pipes[i][PIPE_READ_END], ibuf, 1), "read");
printf("RLEN = %d\n", rlen);
if (rlen > 0) {
int letter_count_i = ibuf[0] - ALPHA_OFFSET;
printf("REDUCER #%d, read %s, letter_count_i = %d\n", i, ibuf, letter_count_i);
letter_count[letter_count_i]++;
} else {
break;
}
}
printf("REDUCER EXITING\n");
_exit(0);
}
}
}
void send_lines_to_mappers(void) {
int wlen = 0;
char obuf[PIPE_BUFFER_SIZE];
int ob_size;
int count = 0;
char buff[BUFFER_SIZE]; // a buffer for each line of the file
FILE *input_file = fopen("input.txt", "r");
// read the input file line by line
while(fgets(buff, BUFFER_SIZE, input_file) > 0) {
//printf("send_lines_to_mappers read: %s\n\n", buff);
ob_size = sizeof buff;
switch(count) {
case 0 :
write(mapper_pipes[0][PIPE_WRITE_END], buff, ob_size);
close(mapper_pipes[0][PIPE_WRITE_END]);
close(mapper_pipes[0][PIPE_READ_END]);
break;
case 1 :
write(mapper_pipes[1][PIPE_WRITE_END], buff, ob_size);
close(mapper_pipes[1][PIPE_WRITE_END]);
close(mapper_pipes[1][PIPE_READ_END]);
break;
case 2 :
write(mapper_pipes[2][PIPE_WRITE_END], buff, ob_size);
close(mapper_pipes[2][PIPE_WRITE_END]);
close(mapper_pipes[2][PIPE_READ_END]);
break;
case 3 :
write(mapper_pipes[3][PIPE_WRITE_END], buff, ob_size);
close(mapper_pipes[3][PIPE_WRITE_END]);
close(mapper_pipes[3][PIPE_READ_END]);
break;
default :
printf("you did something wrong in send_lines_to_mappers loop");
}
count++;
}
fclose(input_file);
}
int main(void) {
init_letter_count();
// Setup the mapper pipes
create_mapper_pipes();
create_reducer_pipes();
fork_reducers();
fork_mappers();
send_lines_to_mappers();
return 0;
}
When you create a pipe, there are two ends ("file descriptors"), the reading end and the writing end.
When you fork(), the child process inherits ALL the open file descriptors, including both ends of ANY open pipe.
So, if you want:
[child1] >===pipe1===> [parent] >===pipe2===> [child2]
for example, then you have a several file descriptors to close.
In child1, you need to close the READ end of pipe1 AND BOTH ends of
pipe2.
In child2, you need to close the WRITE end of pipe2 and BOTH ends of
pipe1.
In the parent, after you are done forking, you need to close the
WRITE end of pipe1 and the READ end of pipe2.
As you have lots of pipes, you will have even more closing to do.
If you are not sure you have everything closed, the program lsof ("list open file-descriptors") can be helpful.
I'm trying to get 2 way communication between a main file and a helper file.
The main file forks, and the child does some pipe work and then runs an exec.
My problem is that I can send information from the child exec to the parent exec, but not the other way around.
Below Is my entire code from the two files, so you should be able to run it.
Any help in getting the 2 way communication working will be extremely helpful. i'm been at this for almost 8 hours straight now.
When you run it, you'll see it print out "yo 0". This was me testing that it takes an integer from the main file, sends it to the helper, adds yo in front of it and sends it back. The first slab of code is the main file, second is the helper, third is the map file needed to run it. make sure there isn't a blank line underneath the last line, and the fourth is the agent file needed to run it.
the running is [./handler mapfile 20 agentfile.]
the int 20 doesn't do anything yet, but you need it in there to run the file.
If anyone actually goes to the effort to do all this and help me, i am eternally grateful
main file (handler.c)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/wait.h>
enum ErrorCode {
SHOW_USAGE = 1, BAD_STEPS, OPEN_MAP_ERROR, CORRUPT_MAP,
OPEN_AGENT_ERROR, CORRUPT_AGENTS, AGENT_ERROR,
AGENT_WALLED, AGENT_COLLIDED, TOO_MANY_STEPS, INVALID_AGENT_RESPONSE,
AGENT_CLOSED, AGENT_DIED, SIGINT_REC
};
typedef struct {
int valid;
int row, col;
} Point;
typedef struct {
Point point;
int number;
char name;
char param[20];
char type[20];
} Agent;
typedef struct {
int rows, cols;
char **grid;
} Map;
Map map;
Map agentMap;
int listSize = 0;
void error(enum ErrorCode e) {
switch(e) {
case SHOW_USAGE:
fprintf(stderr, "Usage: handler mapfile maxsteps agentfile\n");
break;
case BAD_STEPS:
fprintf(stderr, "Invalid maxsteps.\n");
break;
case OPEN_MAP_ERROR:
fprintf(stderr, "Unable to open map file.\n");
break;
case CORRUPT_MAP:
fprintf(stderr, "Corrupt map.\n");
break;
case OPEN_AGENT_ERROR:
fprintf(stderr, "Unable to open agent file.\n");
break;
case CORRUPT_AGENTS:
fprintf(stderr, "Corrupt agents.\n");
break;
case AGENT_ERROR:
fprintf(stderr, "Error running agent.\n");
break;
case AGENT_WALLED:
fprintf(stderr, "Agent walled.\n"); // needs fixing, check spec sheet
break;
case AGENT_COLLIDED:
fprintf(stderr, "Agent collided.\n"); // same as AGENT_WALLED
break;
case TOO_MANY_STEPS:
fprintf(stderr, "Too many steps.\n");
break;
case INVALID_AGENT_RESPONSE:
fprintf(stderr, "Agent sent invalid response.\n"); // fixiing
break;
case AGENT_CLOSED:
fprintf(stderr, "Agent exited with status.\n"); // fixiing
break;
case AGENT_DIED:
fprintf(stderr, "Agent exited due to signal.\n"); // fixing
break;
case SIGINT_REC:
fprintf(stderr, "Exiting due to INT signal.\n");
break;
}
exit(e);
}
void print_map(Map map)
{
int r;
for (r = 0; r < map.rows; ++r) {
printf("%s", map.grid[r]);
}
puts("");
}
void print_agents(Agent *agents, int size)
{
int i;
for (i = 0; i < size; i++) {
Agent temp = agents[i];
printf("%d %d %c %d %s %s %i\n", temp.point.row, temp.point.col, temp.name, temp.number, temp.type, temp.param, i);
}
puts("");
}
void readMap(char *file)
{
int r;
FILE *fd = fopen(file, "r");
char buffer[20];
char d;
if (!fd) {
error(OPEN_MAP_ERROR);
}
if (fgets(buffer, 20, fd) == NULL) {
error(CORRUPT_MAP);
}
if (sscanf(buffer, "%d %d%1[^\n]\n", &map.rows, &map.cols, &d) != 2 ||
map.rows < 1 || map.rows > 999 || map.cols < 1 || map.cols > 999) {
error(CORRUPT_MAP);
}
map.grid = malloc(map.rows * sizeof(char *));
for (r = 0; r < map.rows; ++r) {
map.grid[r] = calloc(map.cols + 2, sizeof(char));
if (fgets(map.grid[r], map.cols + 2, fd) == NULL ||
map.grid[r][map.cols] != '\n') {
error(CORRUPT_MAP);
}
}
fclose(fd);
}
void checkAgent(char *file)
{
FILE *fd = fopen(file, "r");
if (!fd) {
error(AGENT_ERROR);
}
fclose(fd);
}
int growList (Agent **agentList, int curSize, int increaseNum)
{
const int newSize = curSize + increaseNum;
Agent *temp = (Agent*) realloc(*agentList, (newSize * sizeof(Agent)));
if (temp == NULL) {
exit(20);
}
else {
*agentList = temp;
return newSize;
}
}
Agent* readAgentFile(char *file, Agent *agentList)
{
int readCount = 0;
FILE *fp = fopen(file, "r");
char buffer[80];
listSize = 0;
if (!fp) {
error(OPEN_AGENT_ERROR);
}
if (fgets(buffer, 80, fp) == NULL) {
error(CORRUPT_AGENTS);
}
rewind(fp);
while (fgets(buffer, 80, fp) != NULL) {
if (buffer[0] != '#') {
Agent agent;
sscanf( buffer, "%d %d %c %s %s" ,&agent.point.row, &agent.point.col, &agent.name, agent.type, agent.param);
checkAgent(agent.type);
agent.number = readCount+1;
listSize = growList(&agentList, listSize, 1);
agentList[readCount] = agent;
readCount++;
}
}
if (readCount == 0) {
error(CORRUPT_AGENTS);
}
fclose(fp);
return agentList;
}
void createAgentMap()
{
int i,j;
agentMap = map;
for (i=0; i < map.rows; i++) {
for (j=0; j < map.cols; j++) {
char c = map.grid[i][j];
if (c == '.') {
agentMap.grid[i][j] = ' ';
}
}
}
}
int main(int argc, char **argv)
{
int steps;
int pid;
int returnStatus;
int i;
int out_pipe[2];
int in_pipe[2];
char ch[20];
Agent firstAgent;
Agent *agentList =(Agent *) calloc(1, sizeof(Agent));
if (argc != 4) {
error(SHOW_USAGE);
}
sscanf(argv[2], "%d", &steps);
if ((steps < 1)) {
error(BAD_STEPS);
}
readMap(argv[1]);
agentList = readAgentFile(argv[3], agentList);
firstAgent = agentList[0];
createAgentMap();
for (i=0; i < listSize; i++) {
if (pipe(out_pipe) < 0) {
perror("Pipe Error");
}
if (pipe(in_pipe) < 0) {
perror("Child pipe error");
}
Agent temp;
temp = agentList[i];
switch ( pid = fork() )
{
case -1:
perror("Can't fork.\n");
exit(20);
case 0:
/* Child */
/*close(1);
dup(in_pipe[1]);
close(0);
dup(out_pipe[0]);
close(in_pipe[0]);
close(out_pipe[1]);*/
dup2(out_pipe[0], 0);
dup2(in_pipe[1], 1);
execlp(temp.type, temp.type, temp.param, (char *)0);
perror("No exec");
default:
//close(1);
//dup(handlerChild[1]);
//fprintf(stdout, "%d", listSize);
write(out_pipe[1], "%d", listSize);
close(in_pipe[1]);
close(0);
dup(in_pipe[0]);
if (fgets(ch, 20, stdin) == NULL) {
break;
}
printf("%s\n", ch);
}
}
while (steps > 0) {
steps -= 1;
}
return 0;
}
helper file (simple.c)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
typedef struct {
int valid;
int row, col;
} Point;
typedef struct {
int numAgents;
char agentNames[80];
int agentNumber;
} Info;
typedef struct {
int rows, cols;
char **grid;
} Map;
Map agent_map;
int main(int argc, char **argv)
{
int steps = 10;
int simple_pipe[2];
int dir;
char inputDir;
char input_stream[20];
int in = dup(0);
Info info;
if (argc == 2) {
sscanf(argv[1], "%c1", &inputDir);
switch (inputDir) {
case 'N': dir = 0; break;
case 'E': dir = 1; break;
case 'S': dir = 2; break;
case 'W': dir = 3; break;
default : fprintf(stdout, "Invalid params.\n"); exit(2);
}
}
else {
fprintf(stdout, "Incorrect number of params.\n");
exit(1);
}
close(0);
dup(simple_pipe[0]);
fgets(input_stream, 20, stdin);
sscanf(input_stream, "%d", &info.numAgents);
//printf("%d", info.numAgents);
//printf("this is the input: %s\n", input_stream); // This is successfully printing to stdout in the pipe
fprintf(stderr, "yo %d \n", info.numAgents);
while (steps > 0) {
steps -= 1;
}
exit(0);
}
map file
6 6
##..##
#....#
#.##.#
#....#
##....
######
agent file
1 1 A ./simple E
2 2 B ./simple N
5 2 C ./simple S
A pipe is a unidrectional connection across processes. Before you fork, you open the pipe and it will reserve two file descriptors, where fd[0] can be read from and fd[1] can be written to.
So when you want to have a two way commumincation you need to create two pipes, and then use one for reading in the parent writing in the child and the second pipe the other way around.
A more detailed explanation along with some sample code can be foun dhere: http://linux.die.net/man/2/pipe
I am trying to make a simple shell program with the C language which have the options of redirecting stdin and stdout and making a pipe but it's giving me a segmentation fault error. Maybe the problem is in the getline but I'm not sure. Here is the code:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#define R 0
#define W 1
#define LINE_LEN 25
struct Job {
char* command;
char** argv;
int stdin;
int stdout;
} typedef Job;
int tokens_number = 0;
int sign_place = 0;
int contain_left = 0;
int contain_right = 0;
int contain_line = 0;
char** parse_cmdline (char * cmdline ){
char** arg = calloc(15, sizeof(char*));
char temp_cmd[LINE_LEN*10];
strcpy(temp_cmd, cmdline);
char * tmp;
tmp = strtok(temp_cmd, " ");
while(tmp != NULL) {
arg[tokens_number] = (char*) malloc(LINE_LEN * sizeof(char*));
strcpy(arg[tokens_number],tmp);
tmp = strtok(NULL, " ");
tokens_number++;
}
//LAST ELEMENT IS NULL
arg[tokens_number+1] = NULL;
return arg;
}
void check_for_special_signs(char** argv){
int i;
for(i=0; i<tokens_number; i++){
if(strcmp(argv[i], "<") == 0){
contain_left = 1;
sign_place = i;
return;
}else if(strcmp(argv[i], ">") == 0){
contain_right = 1;
sign_place = i;
return;
}else if(strcmp(argv[i], "|") == 0){
contain_line = 1;
sign_place = i;
return;
}
}
}
void fork_child(Job* my_job) {
pid_t pid = fork();
if (pid == 0) {
execv(my_job -> command, my_job -> argv);
perror(my_job -> command);
} else if (pid > 0) {
int status;
wait(&status);
} else
perror("fork");
}
char** create_argv(char** argv){
int i;
int j = 0;
char** argvs = calloc(sign_place,sizeof(char*));
if(sign_place!=0){
for(i=0; i < sign_place ; i++){
argvs[i] = (char*) malloc(sizeof(char*));
strcpy(argvs[i],argv[i]);
}
return argvs;
}else{
return argv;
}
}
void close_job(Job* my_job) {
if (my_job -> stdin != STDIN_FILENO)
close(my_job -> stdin);
if (my_job -> stdout != STDOUT_FILENO)
close(my_job -> stdout);
free(my_job);
}
int main() {
size_t s = 512;
char* buffer = malloc(s * sizeof(char));
char** sep_cmd = malloc(s * sizeof(char));
while (getline(&buffer, &s, stdin) != EOF) {
Job* my_job;
int my_pipe[2];
int in = 0;
int out = 1;
sep_cmd = parse_cmdline(buffer);
my_job->command = sep_cmd[0];
my_job->argv = sep_cmd;
my_job->stdin = in;
my_job->stdout = out;
check_for_special_signs(my_job->argv);
pid_t pid = fork();
if (pid == 0) {
if(contain_left == 1){
in = open(my_job->argv[sign_place + 1], O_RDONLY);
if(in < 0){
perror("open()");
}
my_job->argv = create_argv(my_job->argv);
my_job->stdin = in;
}else if(contain_right == 1){
out = open(my_job->argv[sign_place + 1], O_WRONLY | O_CREAT,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (out < 0)
perror("open()");
my_job->argv = create_argv(my_job->argv);
my_job->stdout = out;
}else if(contain_line == 1){
pipe(my_pipe);
if (my_job -> stdin == my_pipe[R])
close(my_pipe[W]);
else
close(my_pipe[R]);
}
execv(my_job -> command, my_job -> argv);
perror(my_job -> command);
} else if (pid > 0) {
int status;
wait(&status);
} else{
perror("fork");
}
close_job(my_job);
free(buffer);
buffer = (char*) malloc(s * sizeof(char));
}
free(buffer);
return 0;
}
That way I can't see if there are more mistakes in the code. Please if you see more mistakes list them too.
Thank you.
You forgot to allocate memory for my_job in main function