forking processes, reading a file but not showing its content - c

I have the following code where I have to enter 2 arguments as an input where: The 1st argument is a number. If this number is multiple of 2, then 15 child processes will be created with fork. If the number is multiple of 3, 10 processes will be created. If the number is multiple of 5, 7 processes will be created. The 2nd argument is a file where its size (in bytes) will be divided into the number of processes created and each child process is going to read one part of the file, they save that part into a variable and finally the parent shows all the text in that variable. For example, I run the program with ./p 5 /home/directoryFile.c. So I'm having 7 child processes and let's say the filesize is 700 bytes. That means every child process should read 100 bytes, they save it into a variable (appending the content) and finally the parent shows all the content together. The problem is that the variable textToSend that should show all the content in the parent doesn't show anything... I believe there should be a problem with the sprintf line in the child.
//gcc Test.c -o p
//./p 5 /home/directoryFile.c
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int studentId, children = 0, j, i, childNumber[15], fdFile, fdread;
float bytesToRead;
char directory[50];
char *buffer = malloc(256), *textToSend = malloc(256);
system("clear");
if(argc-1 < 1)
{
printf("\nSome arguments are missing\n");
return EXIT_FAILURE;
}
studentId = atoi(argv[1]);
strcpy(directory,argv[2]);
if((studentId%2) == 0)
{
children = 15;
}
else
{
if((studentId%3) == 0)
{
children = 10;
}
else
{
if((studentId%5) == 0)
{
children = 7;
}
else
{
printf("\nStudentId is not multiple of 2, 3 o 5\n");
return EXIT_FAILURE;
}
}
}
struct stat fileInfo;
stat(argv[2],&fileInfo);
bytesToRead = fileInfo.st_size / children;
printf("children: %d\n",children);
printf("File Size: %lld\n",(long long int) fileInfo.st_size);
printf("Bytes: %.2f\n",bytesToRead);
fdFile = open(directory,O_RDONLY);
if(fdFile == -1)
{
printf("\nError opening the fileo\n");
return EXIT_FAILURE;
}
for(i=0;i<children;i++)
{
childNumber[i] = fork();
if(childNumber[i] == -1)
{
printf("\nError creating the child process\n");
return EXIT_FAILURE;
}
if(childNumber[i] == 0)
{
fdread = read(fdFile,buffer,bytesToRead);
if(fdread == -1)
{
printf("\nError reading the file\n");
return EXIT_FAILURE;
}
printf("%s",buffer);
//printf("\n\n------------------------\n\n");
sprintf(textToSend,"%s%s",textToSend,buffer);
return EXIT_SUCCESS;
}
else
{
//waitpid(childNumber[i],NULL,WNOHANG);
}
}
printf("\nThis is the content of the file: %s\n",textToSend);
close(fdFile);
for(j=0;j<children;j++)
{
wait(NULL);
}
return EXIT_SUCCESS;
}
What could happen? The buffer is showing a part of the file correctly...
Could it be the sprintf function?

You are using sprintf to print to a local buffer which is not shared among your processes.
When you fork your child receives a full private copy of all of the parents memory which is private. All memory is private by default unless specifically allocated as shared.
If you allocate it as shared
char * textToSend = mmap(NULL, 256, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
Then you will have the same memory accessible from all processes.
This will not account for the fact that you have to tell all other processes that the start of the buffer has moved, so that would require an extra shared variable and synchronised access. At the moment you are not appending text with sprintf, you are overwriting any previous content.
In the end if you wish to pool together results from different processes you may want to use pipes (pipe(2)) rather then shared memory.

Related

is it possible to read and write with the same file descriptor in C

I am trying to write to a file and display the output of the thing i wrote with another process. The code i come up with:
void readLine (int fd, char *str) {
int n;
do {
n = read (fd, str, 1);
} while (*str++ != '\0');
}
int main(int argc,char ** argv){
int fd=open("sharedFile",O_CREAT|O_RDWR|O_TRUNC,0600);
if(fork()==0){
char buf[1000];
while(1) {
readLine(fd,buf);
printf("%s\n",buf);
}
}else{
while(1){
sleep(1);
write(fd,"abcd",strlen("abcd")+1);
}
}
}
the output i want (each result spaced from the other with a period of one second):
abcd
abcd
abcd
....
Unfortunately this code doesn't work, it seems that the child process (the reader of the file "sharedFile") reads junk from the file because somehow it reads values even when the file is empty.
When trying to debug the code, readLine function never reads the written file correctly,it always reads 0 bytes.
Can someone help?
First of all, when a file descriptor becomes shared after forking, both the parent and child are pointing to the same open file description, which means in particular that they share the same file position. This is explained in the fork() man page.
So whenever the parent writes, the position is updated to the end of the file, and thus the child is always attempting to read at the end of the file, where there's no data. That's why read() returns 0, just as normal when you hit the end of a file.
(When this happens, you should not attempt to do anything with the data in the buffer. It's not that you're "reading junk", it's that you're not reading anything but are then pretending that whatever junk was in the buffer is what you just read. In particular your code utterly disregards the return value from read(), which is how you're supposed to tell what you actually read.)
If you want the child to have an independent file position, then the child needs to open() the file separately for itself and get a new fd pointing to a new file description.
But still, when the child has read all the data that's currently in the file, read() will again return 0; it won't wait around for the parent to write some more. The fact that some other process has a file open for writing don't affect the semantics of read() on a regular file.
So what you'll need to do instead is that when read() returns 0, you manually sleep for a while and then try again. When there's more data in the file, read() will return a positive number, and you can then process the data you read. Or, there are more elegant but more complicated approaches using system-specific APIs like Linux's inotify, which can sleep until a file's contents change. You may be familiar with tail -f, which uses some combination of these approaches on different systems.
Another dangerous bug is that if someone else writes text to the file that doesn't contain a null byte where expected, your child will read more data than the buffer can fit, thus overrunning it. This can be an exploitable security vulnerability.
Here is a version of the code that fixes these bugs and works for me:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void readLine (int fd, char *str, size_t max) {
size_t pos = 0;
while (pos < max) {
ssize_t n = read(fd, str + pos, 1);
if (n == 0) {
sleep(1);
} else if (n == 1) {
if (str[pos] == '\0') {
return;
}
pos++;
} else {
perror("read() failure");
exit(2);
}
}
fprintf(stderr, "Didn't receive null terminator in time\n");
exit(2);
}
int main(int argc, char ** argv){
int fd=open("sharedFile", O_CREAT|O_RDWR|O_TRUNC, 0600);
if (fd < 0) {
perror("parent opening sharedFile");
exit(2);
}
pid_t pid = fork();
if (pid == 0){
int newfd = open("sharedFile", O_RDONLY);
if (newfd < 0) {
perror("child opening sharedFile");
exit(2);
}
char buf[1000];
while (1) {
readLine(newfd, buf, 1000);
printf("%s\n",buf);
}
} else if (pid > 0) {
while (1){
sleep(1);
write(fd,"abcd",strlen("abcd")+1);
}
} else {
perror("fork");
exit(2);
}
return 0;
}

Thread not hitting thread function every time

I am trying to make a program that takes several files, appends them all into one big file. Each append has to be done by a separate thread.
/*
This program creates appends several files together
*/
#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
pthread_t *tids;
void *threadout(void *num);
int main(int argc, char *argv[])
{
int numOfFiles = atoi(argv[2]);
int error;
int index;
sem_t sem;
//Used for output file
int outFile;
//Checking to make sure there is the correct number of arguments
if (argc != 4)
{
printf("%s \n", "Wrong number of arguments, exiting program.");
return 1;
}
//checking to make sure there are at least two files to append
if (numOfFiles < 2)
{
printf("%s \n", "Cannot append 1 file or less.");
return 1;
}
//opening/creating file
outFile = open(argv[3], O_WRONLY | O_CREAT, S_IRUSR);
///****************** Allocate space for thread ids ******************/
tids = (pthread_t *)calloc(numOfFiles, sizeof(pthread_t));
if (tids == NULL)
{
perror("Failed to allocate memory for thread IDs");
return 1;
}
if (sem_init(&sem, 0, 1) == -1)
{
perror("Failed to initialize semaphore");
return 1;
}
/****************** Create threads *********************************/
for (index = 0; index < numOfFiles; index++)
{
if (error = pthread_create(tids + index, NULL, threadout, &index))
{
fprintf(stderr, "Failed to create thread:%s\n", strerror(error));
return 1;
}
}
return 0;
}
void * threadout(void *num)
{
printf("Hello");
return NULL;
}
Near the bottom of the program I do the actual creating of the threads. The first thing the thread should do is hit the "threadout" function. However the only way I can get anything to print is if I say to create a large number of threads. So if I tell my program to create 5000 threads, "Hello" will be printed. Not 5000 times though. If I asked it to create 10 threads nothing is printed. Am I doing something wrong when I invoke "threadout"? Thanks
Returning from main causes your entire program to exit, even if other threads are running.
Your main function exits when all threads are started. If you're starting lots of threads, this leaves enough time for the first ones to print. If you're starting few threads, it returns before the first ones get to print anything.
You might want to use pthread_join (called once per thread) to wait for all threads to terminate.

Segmentation fault 11 printing string from struct

This is the first time I've run into Segmentation fault 11 in C and I can't seem to wrap my head around what is actually going wrong.
What I'm trying to do is write a few int values to a struct plus the file name from the command line (char *) from a child process and then write the struct to the pipe to read from from the parent process. It works fine when it's only the integers and I take out the code working with the string, but once I add in the string and try to print out the file name in the parent process I get the segmentation fault 11 when the program is run.
I've looked at various posts from all over, but have noticed that the common issue for this is when someone attempts to assign a string to a char array and prints, but I made sure to use only char * here. Here's the code where it locks up
if((read(pd[0], &pv, 2048)) == -1)
{
error_exit("read not working");
}
printf("words = %d\n", pv.words);
printf("lines = %d\n", pv.lines);
printf("bytes = %d\n", pv.bytes);
printf("file = %s\n", pv.file); //locks up here and gives segmentation fault 11 on the command line
Here is the read out of what the program does when I run it:
$ ./a testfile
Parent process... should be waiting on child...
In child process! pid = 21993
it worked? testfile
Done with child process!
words = 1
lines = 2
bytes = 3
Segmentation fault: 11
Also here is the full code
EDIT: I swapped out the code using sizeof for string and used strlen
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
void error_exit(char *);
typedef struct total {
int words, lines, bytes;
char *file;
} Vals;
int main(int argc, char *argv[])
{
int pd[2]; //pipe descriptor
pid_t pid;
Vals v, pv;
char *fname = "Not set";
if(argc > 1)
{
fname = malloc(strlen(argv[1]));
strcpy(fname, argv[1]);
}
if((pipe(pd)) == -1)
{
error_exit("pipe creation");
}
if((pid = fork()) == -1)
{
error_exit("the fork forked up!");
}
else if(pid == 0)
{
printf("In child process! pid = %d\n", getpid());
v.words = 1;
v.lines = 2;
v.bytes = 3;
v.file = malloc(strlen(fname));
strcpy(v.file, fname);
printf("it worked? %s\n", v.file);
close(pd[0]);
if((write(pd[1], &v, sizeof(v.words) + sizeof(v.lines) + sizeof(v.bytes) + strlen(v.file))) == -1)
{
error_exit("Write from child");
}
//return; //return from child
printf("Done with child process!\n");
close(pd[1]);
return 0;
}
else
{
printf("Parent process... should be waiting on child...\n");
}
//wait for child
while((pid = wait(NULL)) > 0);
close(pd[1]);
//Vals pv = {0, 0, 0, "pv.file not set"};
//just assign anything to file to see if it fixes
//pv.file = malloc(strlen(fname));
if((read(pd[0], &pv, 2048)) == -1)
{
error_exit("read not working");
}
printf("words = %d\n", pv.words);
printf("lines = %d\n", pv.lines);
printf("bytes = %d\n", pv.bytes);
printf("file = %s\n", pv.file); //locks up here and gives segmentation fault 11 on the command line
close(pd[0]);
//program ended normally
return 0;
}
void error_exit(char *err)
{
printf("exiting because of this section: %s\nerrno = %d", err, errno);
exit(1);
}
I really appreciate any insight on this!
Your main problem is that you don't quite have the right understanding of C strings. You cannot do sizeof(char_pointer). That will just give you the pointer size (4 in a 32 bit system) and not the size of the string it points to. Use strlen to get the length of a string.
The second related problem is that you are writing a pointer address, v.file, and not the full string contents through the pipe. That is not correct because each process has a seperate address space and hence a pointer in one process is not valid in another process.
There are several ways to fix your problem. I will give you the simplest (but not the best).
First declare file inside the struct as a char array rather than a char pointer. This essentially gives you a fixed sized buffer.
#define MAX_FILENAME_LEN 64
typedef struct total {
int words, lines, bytes;
char file[MAX_FILENAME_LEN];
} Vals;
Then remove the malloc call. You don't need it anymore as file is already a buffer that you can copy into.
Finally, make sure you don't overflow the buffer during string copy:
if (strlen(fname) >= MAX_FILENAME_LEN) {
error_exit("File name too long");
}
strcpy(v.file, fname);
You also don't need the +1 in the write as the sizeof gives you the full buffer size.
I'll leave it as an exercise for you to use dynamic memory for the file name in the struct. It's not hard but will require you to change your read and write logic a little as you will need to read/write the file name seperately (because writing the whole struct in that case will just write the pointer not the contents).
There's a few things wrong here. First, you aren't free()ing the space you allocate with malloc().
Second, you should be using strlen() in place of sizeof() in your calculations. This occurs twice in your code.
Third, the declaration char fname = "Not set"; is not safe, since it is actually a const char* to read-only memory (text segment), and it's later pointed to something allocated via malloc(). Don't do this.
Corrected Code Listing
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#define MAX_BUF_LEN (1024)
void error_exit(char *);
typedef struct total {
int words, lines, bytes;
char file[MAX_BUF_LEN];
} Vals;
int main(int argc, char *argv[])
{
int pd[2]; //pipe descriptor
pid_t pid;
Vals v, pv;
char fname[MAX_BUF_LEN] = "Not set";
if(argc > 1) {
//fname = malloc(sizeof(argv[1]) + 1);
//fname = argv[1];
strcpy(fname, argv[1]);
}
if((pipe(pd)) == -1) {
error_exit("pipe creation");
}
if((pid = fork()) == -1) {
error_exit("the fork forked up!");
} else if(pid == 0) {
printf("In child process! pid = %d\n", getpid());
v.words = 1;
v.lines = 2;
v.bytes = 3;
//v.file = malloc(strlen(fname) + 1);
strcpy(v.file, fname);
printf("it worked? %s\n", v.file);
close(pd[0]);
if((write(pd[1], &v, sizeof(v.words) + sizeof(v.lines) + sizeof(v.bytes) + sizeof(v.file) + 1)) == -1) {
error_exit("Write from child");
}
printf("Done with child process!\n");
close(pd[1]);
return 0; //return from child
}
else
{
printf("Parent process... should be waiting on child...\n");
}
//wait for child
while((pid = wait(NULL)) > 0);
close(pd[1]);
if((read(pd[0], &pv, 2048)) == -1) {
error_exit("read not working");
}
printf("words = %d\n", pv.words);
printf("lines = %d\n", pv.lines);
printf("bytes = %d\n", pv.bytes);
printf("file = %s\n", pv.file); //locks up here and gives segmentation fault 11 on the command line
close(pd[0]);
//program ended normally
return 0;
}
void error_exit(char *err)
{
printf("exiting because of this section: %s\nerrno = %d", err, errno);
exit(1);
}
Sample Run
Parent process... should be waiting on child...
In child process! pid = 7410
it worked? HelloWorld
Done with child process!
words = 1
lines = 2
bytes = 3
file = HelloWorld
This code has several issues which for some reason were not mentioned. Hence here goes my take.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
Well, gcc -Wall -Wextra tells me:
warning: implicit declaration of function ‘wait’
How are you compiling this? Did you see this error and ignored it? If so, no candy for a week.
void error_exit(char *);
typedef struct total {
int words, lines, bytes;
char *file;
} Vals;
Weird naming. 'total'? 'vals'?
int main(int argc, char *argv[])
{
int pd[2]; //pipe descriptor
Rather useless comment.
pid_t pid;
Vals v, pv;
char *fname = "Not set";
if(argc > 1)
Should test argc == 2 and throw insults if > 2.
{
fname = malloc(strlen(argv[1]));
strcpy(fname, argv[1]);
Incorrect. strlen return the length without the terminating null character. Consider using strdup instead (non-standard). Missing NULL check.
}
if((pipe(pd)) == -1)
{
error_exit("pipe creation");
}
if((pid = fork()) == -1)
{
error_exit("the fork forked up!");
}
else if(pid == 0)
{
printf("In child process! pid = %d\n", getpid());
v.words = 1;
v.lines = 2;
v.bytes = 3;
v.file = malloc(strlen(fname));
strcpy(v.file, fname);
printf("it worked? %s\n", v.file);
close(pd[0]);
You typically close earlier.
if((write(pd[1], &v, sizeof(v.words) + sizeof(v.lines) + sizeof(v.bytes) + strlen(v.file))) == -1)
{
error_exit("Write from child");
}
This code this does not work, but you may be tempted to use 'char file[BIGNUM];' mentioned in other comments, so let's steal a sample which was supposed to work:
if((write(pd[1], &v, sizeof(v.words) + sizeof(v.lines) + sizeof(v.bytes) + sizeof(v.file) + 1)) == -1) {
error_exit("Write from child");
}
Incorrect. Let's assume this adds up to the size of the structure - then '+1' found here causes reading 1 byte after the structure. But sizes of all struct elements are not guaranteed to add up to the size of the entire structure due to padding. If using 'char file[BIGNUM];' just sizeof(v). If playing with char *file, you have to make sure file is always last and for simplicity just use offsetof to the file pointer.
//return; //return from child
printf("Done with child process!\n");
close(pd[1]);
return 0;
Incorrect. Should use _Exit(2) instead.
}
else
{
printf("Parent process... should be waiting on child...\n");
}
What's up with the else clause which only prints something and passes execution below?
//wait for child
while((pid = wait(NULL)) > 0);
Incorrect. wait can return due to a signal.
close(pd[1]);
Should close before wait.
//Vals pv = {0, 0, 0, "pv.file not set"};
//just assign anything to file to see if it fixes
//pv.file = malloc(strlen(fname));
if((read(pd[0], &pv, 2048)) == -1)
{
error_exit("read not working");
}
pv does not have 2048 bytes, so this can happen to work only by accident.
printf("words = %d\n", pv.words);
printf("lines = %d\n", pv.lines);
printf("bytes = %d\n", pv.bytes);
printf("file = %s\n", pv.file); //locks up here and gives segmentation fault 11 on the command line
close(pd[0]);
//program ended normally
return 0;
}
void error_exit(char *err)
{
printf("exiting because of this section: %s\nerrno = %d", err, errno);
exit(1);
}
Consider using perror or err-family of functions (not portable).
Finally, I recommend finding less atrocious style (from linux or KNF).

C - creating process tree using pipes, select, fork, and execl

I'm trying to write the following, 2 part program. In one file ("root.c"), I read in a random string of 1's and 0's. I then split the resulting string in half, and send each half to its own process through fork(). Each child process uses execl() to run the second program ("bit_count.c").
Within bit_count.c, it:
a) checks if the length of the (half)string is 2 or less. If yes, it returns the number of
1's and 0's to it's parent process.
b) if not, it begins to recursively split the string in half, and sending each half to its own new process (replicating the procedure in root.c). This creates a binary process tree, until all pieces of the string are 2 characters long or less.
c) the count results of the left and right children are aggregated by the parent, and returned to its parent, until returning to the root process, which aggregates the highest two children, and outputs it to the user.
My problem with this project is returning the 2-character counts to the parent. My idea right now is to direct the parent's left and right read pipes to stdin with dup2(), and just to print to stdout with fprintf from the children. The select() function of the parent should catch the returning output, right?
My second problem is the format of the output. if the counts are in ints, what is the best way to return that using select() in this case? I've attached my code below, just be warned that it may be a mess - I'm rusty with C code and this is my first exposure to select() and execl().
root.c:
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char* argv[]) {
if (argc != 2) {
perror("input file name");
printf("%d", argc);
exit(1);
}
FILE* fp;
if((fp = fopen(argv[1], "r")) == NULL) {
perror("open file");
}
fseek(fp, 0, SEEK_END);
long fsize = ftell(fp);
fseek(fp, 0, SEEK_SET);
char *bits = malloc(fsize+1);
fread(bits, fsize, 1, fp);
fclose(fp);
char *left_half = malloc( fsize/2 + 1 );
char *right_half;
if (fsize%2) right_half = malloc( fsize/2 + 2 );
else right_half = malloc( fsize/2 + 1 );
if (!left_half || !right_half) perror("array split");
memcpy(left_half, bits, fsize/2);
if (fsize%2) memcpy(right_half, bits + fsize/2, fsize/2 + 1);
else memcpy(right_half, bits + fsize/2, fsize/2);
int fd_left[2], fd_right[2];
int zero, one;
int *left_res, *right_res;
pid_t left, right;
struct timeval tv;
fd_set readfds;
tv.tv_sec = 2;
tv.tv_usec = 500000;
if ((pipe(fd_left) == -1) || (pipe(fd_right) == -1)){
perror("Create pipe error");
exit(1);
}
FD_ZERO(&readfds);
FD_SET(fd_left[0], &readfds);
FD_SET(fd_right[0], &readfds);
if ((left=fork()) == 0) {
close(fd_left[0]);
execl("./bit_count", "bit_count", left_half, NULL);
perror("initiating recursion");
exit(1);
}
else if(left > 0) {
if ((right = fork())==0) {
close(fd_right[0]);
execl("./bit_count", "bit_count", right_half, NULL);
perror("initiating recursion");
exit(1);
}
else if (right > 0) {
close(fd_right[1]);
close(fd_left[1]);
char *left;
char *right;
dup2(fd_left[0], 0);
dup2(fd_right[0], 0);
int ret = select(2, &readfds, NULL, NULL, &tv);
read(fd_left[0], &left_res, 1);
read(fd_right[0], &right_res, 1);
printf("Back in root process!\n");
}
}
zero = (*right_res + *left_res);
one = (*(left_res+sizeof(int)) + *(right_res+sizeof(int)));
printf("%s had %d zeroes and %d ones\n", argv[1], zero, one);
return 0;
}
bit_count.c (only relevant part):
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char* argv[]) {
if (argc != 2) {
perror("sent bit string");
printf("%d", argc);
exit(1);
}
char *bit_string = argv[1];
int size = strlen(bit_string);
int counts[2];
counts[0] = 0;
counts[1] = 0;
if (!(size > 2)) {
int i=0;
for(; i < size; i++) {
if (bit_string[i]=='1') ++counts[1];
else ++counts[0];
}
fprintf(stdout, "%p", &counts);
fflush(stdout);
return 0;
}
}
My idea right now is to direct the parent's left and right read pipes to stdin with dup2(), and just to print to stdout with fprintf from the children. The select() function of the parent should catch the returning output, right?
No. You need to dup2(fd[1], STDOUT_FILENO) in the child before calling execl(). How else is bit_count supposed to know about the pipe? Then in the parent you can just read from fd[0]. To make things easier, you could just make bit_count a function, and call it directly in the child without using execl(). Then you could just write to fd[1] (if you made it global, or passed the value to the bit_count function) from the children.
My second problem is the format of the output. if the counts are in ints, what is the best way to return that using select() in this case?
You could use write(STDOUT_FILENO, &counts, 2*sizeof(int)) to write the ints directly to the pipe, rather than formatting them as a string. This way the parent does not need to convert them back to ints.

Named pipes without child process

I used a FIFO for a simple read/write programme where the input from user is written to standard output by the writer function. The question is however, am I able to run this program without creating a child process (with the fork() operation). From what I see from examples about FIFOs, most read/write programmes with a named pipe/FIFO are done with 2 files - one for reading and one for writing. Could I do these all in a file?
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
/* read from user */
void reader(char *namedpipe) {
char c;
int fd;
while (1) {
/* Read from keyboard */
c = getchar();
fd = open(namedpipe, O_WRONLY);
write(fd, &c, 1);
fflush(stdout);
}
}
/* writes to screen */
void writer(char *namedpipe) {
char c;
int fd;
while (1) {
fd = open(namedpipe, O_RDONLY);
read(fd, &c, 1);
putchar(c);
}
}
int main(int argc, char *argv[]) {
int child,res;
if (access("my_fifo", F_OK) == -1) {
res = mkfifo("my_fifo", 0777);
if (res < 0) {
return errno;
}
}
child = fork();
if (child == -1)
return errno;
if (child == 0) {
reader("my_fifo");
}
else {
writer("my_fifo");
}
return 0;
}
You'll need to put a lock on the file, or else you could attempt to be reading when someone else is writing. You'll also want to flush the write buffer, or your changes to the fifo might actually not be recorded until the kernel write buffer fills and then writes to the file (in linux, write doesn't guarantee a write happens at that exact moment. i see you're flushing stdout, but you should also fsync on the file descriptor. This will cause the file to lock during any write operation so that no one else can write. In order to lock the file for reading, you might have to use a semaphore.

Resources