I have something that I've been staring at for most of the night and can't figure out. I'm writing code in C that is supposed to use pipes to pass a byte back and forth, allowing me to switch between a parent and child process that will take turns writing a string to a file. Here's my code:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
int fd[2];
int fd2[2];
char token = 'a';
int file = open("output.txt", O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
if (pipe(fd) == -1 || pipe(fd) == -1)
{
printf("Pipe failed");
return(-1);
}
pipe(fd2);
int pid = fork();
int i;
int j;
write(fd[1], token, 1);
if (pid) // Parent enters here
{
for (i = 0; i < 100;)
{
if (read(fd[0], token, 1) != -1)
{
write(file, "ppppppp", 7);
i++;
write(fd2[1], token, 1);
}
//usleep(500000);
}
wait();
}
else if (pid == 0) // Child enters here
{
for (j = 0; j < 100;)
{
if (read(fd2[0], token, 1) != -1)
{
write(file, "ccccc", 5);
j++;
write(fd[1], token, 1);
}
//usleep(500000);
}
}
else // Error creating child
{
exit (-1);
}
close(file);
return 0;
}
I know the writing to a file works when I don't use the pipes, but now I'm getting an infinite loop and I don't know what the problem is.
I figured it out! Funny how small things make all the difference.
Related
I am having trouble creating a child process, and I'm not sure if I have the execvp argument right. Is there a way to fix it so it'll pass correctly?
int execute(char* input) {
int i = 0;
char* shell_argv[MAX_CMD_LINE_ARGS];
memset(shell_argv, 0, MAX_CMD_LINE_ARGS * sizeof(char));
//passing pointer of input and element list 128
int shell_argc = parse(input, shell_argv);
int status = 0;
pid_t pid = fork();
if (pid < 0) {
fprintf(stderr, "Fork() failed\n"); } // send to stderr
else if (pid == 0) { // child
// fill in code for execvp(...) <- this is what I'm having trouble with
if (execvp(shell_argv[0], shell_argv) == -1 && strcmp(input, "history") != 0) {
printf("Invalid command\n");
}
} else { // parent ----- don't wait if you are creating a daemon (background) process
while (wait(&status) != pid) { }
}
return 0;
}
There are some errors in your code:
shell_argv is an array of char*, memset length shoud be MAX_CMD_LINE_ARGS * sizeof(char*); or use a simple way char *shell_argv[MAX_CMD_LINE_ARGS] = {0};
I can't find a standard function parse(), maybe you implemented it by your self. I have made a workround to run the code.
memory for the element in shell_argv should be free at the end of founction.
The input argument of execvp is correct. Here is all the code for your reference.
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <stdlib.h>
#define MAX_CMD_LINE_ARGS 255
int parse(char *input, char** shell_argv){
shell_argv[0] = strdup("date");
shell_argv[1] = strdup("+%s");
}
int execute(char *input)
{
int i = 0;
char *shell_argv[MAX_CMD_LINE_ARGS];
memset(shell_argv, 0, MAX_CMD_LINE_ARGS * sizeof(char*));
// passing pointer of input and element list 128
int shell_argc = parse(input, shell_argv);
int status = 0;
pid_t pid = fork();
if (pid < 0)
{
fprintf(stderr, "Fork() failed\n");
} // send to stderr
else if (pid == 0)
{ // child
// fill in code for execvp(...) <- this is what I'm having trouble with
if (execvp(shell_argv[0], shell_argv) == -1 && strcmp(input, "history") != 0)
{
printf("Invalid command\n");
}
}
else
{ // parent ----- don't wait if you are creating a daemon (background) process
while (wait(&status) != pid)
{
}
}
for(int i=0;i<MAX_CMD_LINE_ARGS;i++){
if(shell_argv[i] != NULL){
free(shell_argv[i]);
}
}
return 0;
}
int main(){
execute("date +%s");
return 0;
}
Test result:
gxie#ubuntu20:~/test $ gcc main.c
gxie#ubuntu20:~/test $ ./a.out
1663837441
I have a program with 2 child processes which has to do the following:
use the parent to read data from a file 'data.txt' and write in a pipe
use a child to read the data from the pipe and filter the lowercase letters
use another child to write the filtered letters in a new file, each on a new line
I tried to do it and it works... kinda. The problem is, it writes the filtered letters in the desired file, but the program does not stop. What am I doing wrong?
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <string.h>
int parentChildpipeFileDescriptors[2], child1Child2FileDescriptors[2];
void parentProcess()
{
close(child1Child2FileDescriptors[0]);
close(child1Child2FileDescriptors[1]);
close(parentChildpipeFileDescriptors[0]);
int fileDescriptor = open("data.txt", O_RDONLY);
char buffer[8];
int store;
while ((store = read(fileDescriptor, buffer, 8)))
{
write(parentChildpipeFileDescriptors[1], buffer, store);
}
close(fileDescriptor);
close(parentChildpipeFileDescriptors[1]);
}
void child1Process()
{
close(parentChildpipeFileDescriptors[1]);
close(child1Child2FileDescriptors[0]);
char buffer[8];
int store, count = 0;
while ((store = read(parentChildpipeFileDescriptors[0], buffer, 8)))
{
for (int i = 0; i < store; i++)
{
if (buffer[i] >= 'a' && buffer[i] <= 'z')
{
count++;
write(child1Child2FileDescriptors[1], &buffer[i], sizeof(buffer[i]));
}
}
}
printf("CHILD 1 FINISHED FILTERING\n");
close(parentChildpipeFileDescriptors[0]);
close(child1Child2FileDescriptors[1]);
exit(count);
}
void child2Process()
{
close(parentChildpipeFileDescriptors[0]);
close(child1Child2FileDescriptors[1]);
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
char *fileName = "stat.txt";
int newFileDescriptor = creat(fileName, mode);
char buffer;
int store;
while ((store = read(child1Child2FileDescriptors[0], &buffer, 1)))
{
write(newFileDescriptor, &buffer, sizeof(buffer));
write(newFileDescriptor, "\n", 1);
}
close(newFileDescriptor);
printf("CHILD 2 FINISHED WRITING'\n");
close(child1Child2FileDescriptors[0]);
close(parentChildpipeFileDescriptors[1]);
exit(444);
}
int main(int argc, char const *argv[])
{
if (pipe(parentChildpipeFileDescriptors) < 0)
{
printf("ERROR CREATING PIPE\n");
exit(-100);
}
if (pipe(child1Child2FileDescriptors) < 0)
{
printf("ERROR CREATING PIPE\n");
exit(-101);
}
pid_t child1PID = fork();
if (child1PID < 0)
{
printf("ERROR CREATING CHILD\n");
exit(-200);
}
if (!child1PID)
{
child1Process();
}
pid_t child2PID = fork();
if (child2PID < 0)
{
printf("ERROR CREATING CHILD\n");
exit(-201);
}
if (!child2PID)
{
child2Process();
}
parentProcess();
int status1, status2;
waitpid(child1PID, &status1, 0);
waitpid(child2PID, &status2, 0);
printf("CHILD 1 TERMINATED WITH EXIT STATUS: %d\n", WEXITSTATUS(status1));
printf("CHILD 2 TERMINATED WITH EXIT STATUS: %d\n", WEXITSTATUS(status2));
return 0;
}
The read loop in child1process will never terminate, because child2 still has the write side of that pipe open. You need to execute:
close(parentChildpipeFileDescriptors[1]);
before you enter the read loop. The general rule is that if a process isn't going to use a file descriptor, it should close it immediately.
your while ((store = read(parentChildpipeFileDescriptors[0], buffer, 8))) loop is never gonna end.
The parent needs to say to the child that there is no more data coming and it shall not do another read.
You can do this by sending a special byte.
Example :
in the parent:
char endByte = 0x1;
write(parentChildpipeFileDescriptors[1], &endByte, 1);
//then close
in the while loop of the child :
if(buffer[i] == 0x1){
printf("CHILD 1 FINISHED FILTERING\n");
fflush(stdout);
close(parentChildpipeFileDescriptors[0]);
close(child1Child2FileDescriptors[1]);
exit(count);
};
I want to make NO_PROC processes, such that every process reads messages from its parent, and then writes those messages and one more message to its child, except in the case of the last process which writes its messages to stdout. So the i'th process will receive i-1 messages and will send to child i messages. I must use pipe to communication between processes. I wrote code but something is wrong and I can't find any bug :/. When NO_PROC = 5 I want the output to look like 4 lines with "my message", but in output I have one line: "my message" and 3 empty lines, like 3 messages are empty string :/. Note, err.h is my library which gives me function syserr() when something went wrong.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "err.h"
#define NO_PROC 5
#define BUF_SIZE 20
char message[] = "my message";
int parent;
char buf[BUF_SIZE];
int main()
{
for (int i = 0; i < NO_PROC; ++i) {
int pipe_dsc[2], buf_len;
if (pipe(pipe_dsc) == -1)
syserr("Error in pipe\n");
pid_t pid = fork();
if (pid == -1)
syserr("Error in fork\n");
else if (pid == 0)
parent = pipe_dsc[0];
else {
for (int j = 0; j < i; ++j) {
if ((buf_len = read(parent, buf, BUF_SIZE - 1)) == -1)
syserr("Error in read\n");
buf[buf_len < BUF_SIZE - 1 ? buf_len : BUF_SIZE - 1] = '\0';
if (i == NO_PROC - 1)
printf("%s\n", buf);
else if (write(pipe_dsc[1], buf, sizeof(buf)) != sizeof(buf))
syserr("Error in write\n");
}
if (i < NO_PROC - 1 && write(pipe_dsc[1], message, sizeof(message)) != sizeof(message))
syserr("Error in write\n");
if (wait(0) == -1)
syserr("Error in wait\n");
return 0;
}
}
}
I think your over complicating it and/or using the wrong approach. You don't have to send i messages to the ith process. Since the ith process is a copy (fork) of the i-1th process it has already received i-1 messages, and just needs one more. It's a rather symmetrical (and academic) problem.
Here is an example (robust error checking omitted). Note this relies on atomic pipe writes, which is fine as long as you not writing message greater that PIPE_BUF (see man pipe):
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
void syserr(char * msg) { printf("%s\n", msg); exit(1); }
#define NO_PROC 5
#define BUF_SIZE 100
char message[] = "my message ";
int main() {
int message_len = 0;
char buf[BUF_SIZE];
for (int i = 0; i < NO_PROC; ++i) {
int pipe_dsc[2], buf_len;
if (pipe(pipe_dsc) == -1) {
syserr("Error in pipe\n");
}
pid_t pid = fork();
if (pid == -1) {
syserr("Error in fork\n");
}
else if (pid == 0) {
close(pipe_dsc[1]);
int n = read(pipe_dsc[0], buf+message_len, sizeof(buf));
message_len = strlen(buf); // Assume message is null terminated string.
if(i == NO_PROC -1) {
printf("Process %i: received '%s'\n", i+1, buf);
}
}
else {
close(pipe_dsc[0]);
write(pipe_dsc[1], message, sizeof(message));
wait(0);
return 0;
}
}
}
Basically I have a parent process that forks a child and feeds it it's stdin through a pipe. The child process can terminate in one of two cases:
the write end of the pipe is closed by the parent, meaning it reached the end of stdin thus receiving an EOF,
or it receives a certain input through the pipe(-1 in this case) and exits
My parent code looks roughly like this:
close(pi[0]); // close input end
signal(SIGPIPE, SIG_IGN); // do not handle SIGPIPE
char buffer;
int ok = 1;
while(ok && read(STDIN_FILENO, &buffer, 1) > 0) {
int b_written = write(pi[1], &buffer, 1);
if(b_written == -1) {
if(errno == EPIPE) ok = 0;
else perror("pipe write"); // some other error
}
}
As you can see, I check whether the read end of a pipe is closed by checking for errno == EPIPE. However this means that the read loop does one extra iteration before closing. How could I possibly poll to see if the pipe is closed without necessarily writing something to it?
This snippet will check if the other end of a writable pipe is closed using poll(2). This works on Linux -- I'm not sure about other OSes or what POSIX says.
#include <poll.h>
#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>
bool is_pipe_closed(int fd) {
struct pollfd pfd = {
.fd = fd,
.events = POLLOUT,
};
if (poll(&pfd, 1, 1) < 0) {
return false;
}
return pfd.revents & POLLERR;
}
The child could send a signal, such as SIGUSR1 when it detects it has finished. Parent could set a flag to when it receives SIGUSR1 signal, and check this flag before trying to read input. But I am not absolutely sure SIGUSR1 could not be received after checking the flag ans before reading input from stdin). So I prefer to use a control pipe, each time child know it will be able to read one more data it write a 1 in this control pipe. The result could be something like that:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#define STOP_VALUE 100
#define SIZE_STDIN_BUFFER 1024
static char can_read_more = 1;
static int handle_child(int *p_child_input_stream, int *p_control_stream)
{
int pipefd[2][2];
pid_t fk;
if (pipe(pipefd[0]) < 0) // Pipe to read input from
{
perror("pipe");
return -1;
}
if (pipe(pipefd[1]) < 0) // Pipe to notifiate parent input can be processed
{
perror("pipe");
close(pipefd[0][0]);
close(pipefd[0][1]);
return -1;
}
if ((fk = fork()) < 0)
{
perror("fork");
close(pipefd[0][0]);
close(pipefd[0][1]);
close(pipefd[1][0]);
close(pipefd[1][1]);
return -1;
}
if (fk == 0)
{
close(pipefd[0][1]);
close(pipefd[1][0]);
write(pipefd[1][1], &can_read_more, sizeof(char)); // sizeof(char) == 1
ssize_t nb_read = 0;
char buffer;
while (nb_read >= 0)
{
nb_read = read(pipefd[0][0], &buffer, sizeof(char));
if (nb_read > 0)
{
printf("0x%02x\n", (unsigned int) buffer);
if (buffer == STOP_VALUE)
{
nb_read = -1;
}
else
{
write(pipefd[1][1], &can_read_more, sizeof(char));
}
}
}
close(pipefd[0][0]);
close(pipefd[1][1]);
exit(0);
}
close(pipefd[0][0]);
close(pipefd[1][1]);
*p_child_input_stream = pipefd[0][1];
*p_control_stream = pipefd[1][0];
return 0;
}
int main()
{
int child_input_stream;
int control_stream;
if (handle_child(&child_input_stream, &control_stream) < 0)
{
return 1;
}
char stdin_buffer[SIZE_STDIN_BUFFER];
char buffer;
int ok = 1;
int child_available_input = 0;
while(ok)
{
while (child_available_input <= 0 && ok)
{
ssize_t nb_control = read(control_stream, &buffer, sizeof(char));
if (nb_control > 0)
{
child_available_input += buffer;
}
else
{
fprintf(stderr, "End of child reading its input detected.\n");
ok = 0;
}
}
if (ok)
{
if (fgets(stdin_buffer, SIZE_STDIN_BUFFER, stdin) == NULL)
{
ok = 0;
}
else
{
if (stdin_buffer[strlen(stdin_buffer) - 1] == '\n')
{
stdin_buffer[strlen(stdin_buffer) - 1] = '\0';
}
char dummy;
int input;
if (sscanf(stdin_buffer, "%d%c", &input, &dummy) == 1)
{
buffer = (char) input;
write(child_input_stream, &buffer, sizeof(char));
child_available_input--;
}
}
}
}
return 0;
}
Can I use standard input for interprocess communication? I wrote the following gnu c code as an experiment, but the program hangs waiting for input after printing the character defined as val. Neither a newline nor fflush in the sending process seem to alleviate the problem.
#include <unistd.h>
#include <stdio.h>
int main(void) {
char val = '!';
int proc = fork();
if (proc < 0)
return -1;
if (proc == 0) {
write(0, &val, 1);
return 0;
}
else {
char ch[2] = { 0 };
read(0, ch, 1);
printf("%s\n", ch);
return 0;
}
return -2;
}
You can use pipe for IPC. Now if you want to use STDIN_FILENO and STDOUT_FILENO it would look like this:
#include <unistd.h>
#include <stdio.h>
int main(void) {
char val = '!';
int filedes[2];
pipe(filedes);
int proc = fork();
if (proc < 0)
return -1;
if (proc == 0) {
close(1);
dup(filedes[1]);
close(filedes[0]);
close(filedes[1]);
write(1, &val, 1);
return 0;
}
else {
char ch[2] = { 0 };
close(0);
dup(filedes[0]);
close(filedes[0]);
close(filedes[1]);
read(0, ch, 1);
printf("%s\n", ch);
return 0;
}
return -2;
}
Combination close(x) and dup(filedes[x]) closes STDOUT/STDIN makes copy of filedes[x] into first available descriptor, what you just closed. As suggested by Jonathan example is now closing both filedes ends and without any doubts is using STDIN/STDOUT.