Problems with shared buffer and synchronized programming - c

I just started learning how to mess with threads and synchronized programming in C. I'm trying to code a reader which uses threads (POSIX interface) to read selected files from a shared buffer. The children threads will retrieve the file names from the buffer, while the father thread will be infinitly reading filenames from stdin and placing them in the buffer. What am I doing wrong?
pthread_mutex_t lock;
static char* files[NFILES];
int top = NFILES-1;
void putInBuffer(char* file){
pthread_mutex_lock(&lock);
if(top < NFILES-1){
files[top] = file;
top++;
}
pthread_mutex_unlock(&lock);
}
char* removeFromBuffer(){
char* file;
pthread_mutex_lock(&lock);
file = files[top];
top--;
pthread_mutex_unlock(&lock);
return file;
}
void* leitor(){
int op,i,r,cl;
char* file;
char buff[NCHARS];
char teste[NCHARS];
while(1){
pthread_mutex_lock(&lock);
file = removeFromBuffer();
printf("%s\n", file);
op = open(file, O_RDONLY);
if(op == -1) {
perror("Open unsuccessful");
pthread_exit((void*)-1);
}
r = read(op, teste, NBYTES);
if(r == -1){
perror("Read unsuccessful");
pthread_exit((void*)-1);
}
for(i=0; i<NLINES-1; i++){
r = read(op, buff, NBYTES);
if(r == -1){
perror("Read unsuccessful");
pthread_exit((void*)-1);
}
if(strcmp(buff,teste) != 0){
perror("Incorrect file");
pthread_exit((void*)-1);
}
}
cl = close (op);
if(cl == -1){
perror("Close unsuccessful");
pthread_exit((void*)-1);
}
printf("Correct file: %s\n", file);
pthread_mutex_unlock(&lock);
}
pthread_exit((void*)0);
return NULL;
}
int main(){
pthread_t threads[NTHREADS];
int i,*status;
char file[LENFILENAME];
if (pthread_mutex_init(&lock, NULL))
{
perror("\n mutex init failed\n");
exit(-1);
}
for(i=0;i<NTHREADS;i++){
if(pthread_create(&(threads[i]),NULL, leitor,NULL)){
perror("Failed to create thread");
exit(-1);
}
}
while(1){
read(STDIN_FILENO, file, LENFILENAME);
printf("%s\n", file);
putInBuffer(file);
printf("%s\n", removeFromBuffer());
}
for (i=0;i<NTHREADS;i++){
if(pthread_join(threads[i],(void**)&status)){
perror("Failed to join thread");
exit(-1);
}
printf("Thread returned %d\n", status);
}
pthread_mutex_destroy(&lock);
return 0;
}

Given what your program is doing, it seems that you should use a separate semaphore for notifying the child threads of new input instead of using the mutex that you have created.
Each child thread should wait on the semaphore at the top of the while loop, where you currently have pthread_mutex_lock(). After the parent has finished putInBuffer, it should release the semaphore once. When a child thread grabs the semaphore, it can call removeFromBuffer to get the next file, and read it (i.e. what you have already written). After the child finishes with the file, it should not release the semaphore, just go back to the top of the loop and wait on it again.
You have correctly used the mutex in putInBuffer and removeFromBuffer to protect accesses to the shared variables files and top.

Related

how to synchronize process using mutex with multithreading in c

HI I am writing a program which write the string in file, process 1 will write a small letters in file and process 2 write a capital letters in same file. i implemented a program using threading, process 1 must run first, after that run the process 2 . program as follows.
/******************
* Header Files
******************/
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
pthread_mutex_t lock;
void* write_p1()
{
if((pthread_mutex_lock(&lock)) != 0)
{
printf("pthread_mutex_lock() failed\n");
exit(1);
}
int index=0; /*used in for loop*/
int flag=0; /*flag used in capital letter and space detecion*/
FILE *fp=NULL; /*file pointer used to store open file*/
char str[100]; /*to store user string*/
printf("*****Hi, I am Process 1*****\n");
/*open the sample.txt file for write*/
fp=fopen("sample.txt","w");
if(fp==NULL)
{
printf("Not able to open file\n");
exit(1);
}
printf("Enter small letters\n");
fgets(str,100,stdin);
/*capital letter and space detection*/
if((strlen(str)>0)&&(str[strlen(str)-1]=='\n'))
{
str[strlen(str)-1]='\0';
}
for(index=0;str[index]!='\0';index++)
{
if(islower(str[index]) || str[index] == ' ')
{
flag=0;
}
else
{
printf("Enter Small Letters\n");
exit(1);
}
}
if(flag==0)
{
fprintf(fp,"%s",str);
}
/*close the file*/
fclose(fp);
printf("Entered string: %s\n",str);
if((pthread_mutex_unlock(&lock)) != 0)
{
printf("pthread_mutex_unlock() failed\n");
exit(1);
}
printf("\n\n");
}
void* write_p2()
{
if((pthread_mutex_lock(&lock)) != 0)
{
printf("pthread_mutex_lock() failed\n");
exit(1);
}
int index=0; /*used in for loop*/
int flag=0; /*flag used in small letter and space detecion*/
FILE *fp=NULL; /*file pointer used to store open file*/
char str[100]; /*to store user string*/
printf("*****Hi, I am Process 2*****\n");
/*open the sample.txt file for write*/
fp=fopen("sample.txt","a");
if(fp==NULL)
{
printf("Not able to open file\n");
exit(1);
}
printf("Enter Capital letters\n");
fgets(str,100,stdin);
/*capital letter and space detection*/
if((strlen(str)>0)&&(str[strlen(str)-1]=='\n'))
{
str[strlen(str)-1]='\0';
}
for(index=0;str[index]!='\0';index++)
{
if(isupper(str[index]) || str[index] == ' ')
{
flag=0;
}
else
{
printf("Enter capital Letters\n");
exit(1);
}
}
if(flag==0)
{
fprintf(fp,"%s",str);
}
/*close the file*/
fclose(fp);
printf("Entered string: %s\n",str);
if((pthread_mutex_unlock(&lock)) != 0)
{
printf("pthread_mutex_unlock() failed\n");
exit(1);
}
printf("\n\n");
}
int main(void)
{
/*initialized semaphore*/
if((pthread_mutex_init(&lock,NULL)) != 0)
{
printf("pthread_mutex_init() failed\n");
exit(1);
}
/*create a two thread*/
pthread_t t1=0,t2=0;
pthread_create(&t1,NULL,write_p1,NULL);
pthread_create(&t2,NULL,write_p2,NULL);
/*this function wait for thread to terminate*/
pthread_join(t1,NULL);
pthread_join(t2,NULL);
/*destroy the semaphore*/
if((pthread_mutex_destroy(&lock)) != 0)
{
printf("pthread_mutex_destroy() failed\n");
exit(1);
}
return 0;
}
Now This program is working but sometime it will run first thread 2(process 2) or some time thread 1(process 1) , i need a solution for that, it must run thread 1(process 1) first after that thread 2(process 2), so what changes should i do?
I am wring below program for you. You can do it by using conditional variable. Implement same procedure in your code. How will it work? To start the controlling process, we allowing thread1 first. In the main thread (i.e. main function; every program has one main thread, in C/C++ this main thread is created automatically by the operating system once the control passes to the main method/function via the kernel) we are calling pthread_cond_signal(&cond1);. Once this function is called from the main thread, thread1, which was waiting on cond1, will be released and it will start executing further. Once it finishes its final task, it will call pthread_cond_signal(&cond2);. Now, the thread which was waiting on condition cond2, i.e. thread2, will be released and it will start to execute its final stage
#include<pthread.h>
pthread_cond_t cond1 = PTHREAD_COND_INITIALIZER;
pthread_cond_t cond2 = PTHREAD_COND_INITIALIZER;
pthread_mutex_t lock1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t lock2 = PTHREAD_MUTEX_INITIALIZER;
int TRUE = 1;
void * threadMethod1(void *arg)
{
printf("In thread1\n");
do{
pthread_mutex_lock(&lock1);
//Add your business logic(parallel execution codes) here
pthread_cond_wait(&cond1, &lock1);
printf("I am thread1 generating the final report and inserting into file \n");
pthread_cond_signal(&cond2);/* Now allow 2nd thread to process */
pthread_mutex_unlock(&lock1);
}while(TRUE);
pthread_exit(NULL);
}
void * threadMethod2(void *arg)
{
printf("In thread2\n");
do
{
pthread_mutex_lock(&lock2);
//Add your business logic(parallel execution codes) here
pthread_cond_wait(&cond2, &lock2);
printf("I am thread2 generating the final report and inserting into a file \n");
pthread_cond_signal(&cond1);
pthread_mutex_unlock(&lock2);
}while(TRUE);
pthread_exit(NULL);
}
int main(void)
{
pthread_t tid1, tid2;
int i = 0;
printf("Before creating the threads\n");
if( pthread_create(&tid1, NULL, threadMethod1, NULL) != 0 )
printf("Failed to create thread1\n");
if( pthread_create(&tid2, NULL, threadMethod2, NULL) != 0 )
printf("Failed to create thread2\n");
pthread_cond_signal(&cond1);/* Now allow first thread to process first */
sleep(1);
TRUE = 0;/* Stop all the thread */
//sleep(3);
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
exit(0);
}

C pipe: Bad file descriptor

I have the simple producer consumer program in C try to solve it with fork
I get error exactly when producer tries to write on pipe:
I have wrote another program with the same logic but this one does not give me any clue to know why?
Producer failed to write item on pipe: Bad file descriptor
Anyone have idea why I get this error?
Thanks
#define READ 0
#define WRITE 1
int mutex = 1, full = 0, empty = BUFFER_SIZE, x = 0;
void consumer();
void producer();
int wait_(int);
int signal_(int);
int pipefd[2];
int main() {
printf("Starting producer-consumer problem!\n");
//We intend to run the producer in parent process and the consumer in the child process
if (pipe(pipefd) == -1) { /* An error has occurred. */
fprintf(stderr, "%s", "The call to pipe() has failed.\n");
exit(EXIT_FAILURE);
}
for (int j = 0; j < sizeof(pipefd); j++) {
if (pipe(&pipefd[j]) < 0) { //Initialize each pipe appropriately
perror("Error in making pipe...");
}
}
pid_t pid = fork();
if (pid < 0) {
perror("**********Error in creating fork()!**************\n");
exit(STDERR_FILENO);
} else if (pid == 0) {
consumer();//We intend to run the consumer in child
} else {
producer();//We intend to run the producer in parent
}
return 0;
}
int wait_(int s) {
return (--s);
}
int signal_(int s) {
return (++s);
}
void producer() {
printf("Starting Producer\n");
//while (1) {
//sleep(1);
if (close(pipefd[READ]) != 0) {
perror("Error in closing reading pipe");
}
if (write(pipefd[WRITE], &full, 1) < 0) {
perror("Producer failed to write item on pipe");
}
if ((mutex == 1) && (empty != 0)) {
mutex = wait_(mutex);
full = signal_(full);
empty = wait_(empty);
x++;
printf("Producer produces the item %d\n", x);
mutex = signal_(mutex);
}
if (close(pipefd[WRITE]) != 0) {
perror("Error in closing writing pipe");
}
//}
}
void consumer() {
printf("Starting Consumer\n");
//while (1) {
//sleep(1);
int status = 0;
wait(&status); /* wait for all children to return back the result */
if (close(pipefd[WRITE]) != 0) {
perror("Error in closing reading pipe");
}
if (read(pipefd[READ], &full, 1) > 0) {
printf("Consumer\t%d\n", full);
}
if ((mutex == 1) && (full != 0)) {
mutex = wait_(mutex);
full = wait_(full);
empty = signal_(empty);
printf("Consumer consumes item %d\n", x);
x--;
mutex = signal_(mutex);
}
if (close(pipefd[READ]) != 0) {
perror("Error in closing reading pipe");
}
//}
}
The sizeof operator returns the size in bytes. So on a typical system where an int is four bytes, sizeof(pipefd) would result in the value 8. Which is not the correct number of elements for your loop.
Also, pipe(&pipefd[j]) is not correct either. The two pipes in pipefd are already initialized "appropriately". There's no need for any more initialization. Especially since in both this and the previous case you will have undefined behavior.

Issue read write file using pipe fork shared memory

I am trying to use pipes in C. I have two create two pipes between parent and child process.I have to read a file in chunks of 4096 bytes (or smaller if there is less) and I have to send through the pipes the amount of data that was read and how many times there have been readings. For example, to copy a 6KB file, the parent writes the first 4KB data of the file to the shared memory and send two integers, 1 and 4096, to the child via the pipe. The child receives these two numbers, copies 4096 bytes from the shared memory to the output file, and sends back 1 to the parent via the other pipe. After receiving 1, the parent copies the left 2KB data to the shared memory and send 2 and 2048 to the child. The child receives them from the pipe, copies 2048 bytes to the output file, and replies with 2 to the parent. The parent then send 0, 0 to the child. The child receives 0 and replies with a 0 and then exit. The parent receives 0 and exits too.
Currently my program works for file less than one block but not for file greater then one block (4096 bytes)
697, thank you for pointing out, I have modified my program as following but still has issues, basically how to control the flow as parent-child-parent-child ...
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#define SIZE 4096
int file_exist (char *filename)
{
struct stat buffer;
return (stat (filename, &buffer) == 0);
}
int main(int argv, char *argc[]) {
/*Check if program is called correctly*/
if(argv != 3) {
printf("Please call program appropriately\n");
exit(EXIT_FAILURE);
}
FILE *r, *w;
void *sharedMem;
int datapipe[2];
int blockpipe[2];
int shm;
char userInput[5];
char *name = "fsuid_cop4610";
if (file_exist (argc[2]))
{
printf("Would you like to overwrite file (yes/no): ");
scanf("%s", userInput);
if(!strcmp(userInput, "yes")) {
printf("Overwriting file...\n");
//fclose(w);
w = fopen(argc[2], "wb");
if(w == NULL) {
perror("Error with write file");
exit(EXIT_FAILURE);
}
}
else if(!strcmp(userInput, "no")) {
printf("Will not overwrite\n");
exit(EXIT_FAILURE);
}
else {
printf("User input not accepted\n");
exit(EXIT_FAILURE);
}
}
/*Check if read file can open*/
r = fopen(argc[1], "rb");
if(r == NULL) {
perror("Error opening read file");
exit(EXIT_FAILURE);
}
fseek(r, 0, SEEK_END); // seek to end of file
int inputlength = ftell(r); // get current file pointer
printf("inputlength is %d\n",inputlength);
int numofblock = inputlength/SIZE + 1;
fseek(r, 0, SEEK_SET); // seek back to beginning of file
/*Check if write file can open*/
if (pipe(datapipe) < 0) {
perror("Pipe");
exit(EXIT_FAILURE);
}
if (pipe(blockpipe) < 0) {
perror("Pipe");
exit(EXIT_FAILURE);
}
/*Check if forking process is successful*/
pid_t pid = fork();
if(pid < 0) {
perror("Fork");
exit(EXIT_FAILURE);
}
shm = shm_open(name, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
if(shm == -1) {
perror("Shared memory");
exit(EXIT_FAILURE);
}
if(ftruncate(shm, SIZE) == -1) {
perror("Shared Memory");
exit(EXIT_FAILURE);
}
sharedMem = mmap(NULL, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm, 0);
if(sharedMem == MAP_FAILED) {
perror("Mapping shared memory");
exit(EXIT_FAILURE);
}
if (pid>0) { // parent
printf(" parent before close data0,block1\n");
close(datapipe[0]); // close read, will write data
close(blockpipe[1]); // close write, will read block number
printf(" parent close data0,block1\n");
for (int i=1; i<=numofblock; i++)
{
printf("... parent process\n");
int blocknumber=i;
printf("parent read from input file into shared memory\n");
int P2SHM = fread(sharedMem, 1, SIZE, r);
if(P2SHM < 0) {
perror("Could not store to shared memory");
exit(EXIT_FAILURE);
}
//printf("parent shared memory conent: %s\n",(char *)sharedMem);
printf("parent data read %d\n",P2SHM);
if (i==1)
{
printf("i=%d parent write to data pipe\n",i);
if(write(datapipe[1], &P2SHM, sizeof(int)) < 0) {
perror("parent failed to write to pipe bytes");
exit(EXIT_FAILURE);
}
if(write(datapipe[1], &blocknumber, sizeof(int)) < 0) {
perror("parent failed to write to pipe block number");
exit(EXIT_FAILURE);
}
}
printf("parent read from block pipe\n");
int C2P = read(blockpipe[0], &blocknumber, sizeof(int));
if(C2P < 0) {
perror("parent failed to read value from blockpipe");
exit(EXIT_FAILURE);
}
/*else if(C2P == 0) {
printf("End of file reached\n");
}*/
else {
printf("parent block %d Received succesfully\n", blocknumber);
if (i>= 2)
{
printf("i=%d parent write to data pipe\n",i);
//close(datapipe[0]); // close read, will write data
if(write(datapipe[1], &P2SHM, sizeof(int)) < 0) {
perror("parent failed to write to pipe bytes");
exit(EXIT_FAILURE);
}
if(write(datapipe[1], &blocknumber, sizeof(int)) < 0) {
perror("parent failed to write to pipe block number");
exit(EXIT_FAILURE);
}
//close(datapipe[1]);
}
if(C2P == 0) {
printf("parent End of file reached\n");
}
}
} // end for
printf(" parent before close data1,block0\n");
close(datapipe[1]);
close(blockpipe[0]);
printf(" parent close data1,block0\n");
printf(" ... existing parent process\n");
}
else { // pid=0 child
printf(" child before close data1,block0\n");
close(datapipe[1]); // close write, will read data
close(blockpipe[0]); // close read, will write block number
printf(" child close data1,block0\n");
for (int j=1; j<=numofblock; j++)
{
printf(".... child process\n");
int cBytes, len, len2;
int blocknumber = 1;
printf("child read from datapipe\n");
len = read(datapipe[0], &cBytes, sizeof(cBytes));
len2 = read(datapipe[0], &blocknumber, sizeof(blocknumber));
printf("child wrote to blockpipe blocknumber=%d\n", blocknumber);
write(blockpipe[1], &blocknumber, sizeof(blocknumber));
printf("child There are %i bytes\n", cBytes);
if(len >= 0)
{
printf("child writing to file\n");
//fwrite(sharedMem, 1, sizeof(sharedMem), w);
//printf("child shared memory conent: %s\n",(char *)sharedMem);
char* res = (char *)sharedMem;
//printf("res = %s\n",res);
//printf("errno before write=%d",errno);
shm_unlink(name);
//int writtenbyte = fwrite(res, sizeof(char), strlen(res), w);
int writtenbyte = fwrite(res, sizeof(char), cBytes, w);
if(errno == EINTR) {
printf("somewhting wrong");
}
//printf("errno after write=%d",errno);
printf("writtenbyte = %d\n",writtenbyte);
//fclose(w);
//fputs((char *)sharedMem, w);
//fwrite(s->name, sizeof(char), strlen(s->name) + 1, fp);
}
/*else if (len == 0) {
printf("End of fle reached\n");
}*/
else { // len < 0
perror("Failed to read from pipe");
exit(EXIT_FAILURE);
}
} // after for
printf(" child before close data0,block1\n");
close(datapipe[0]);
close(blockpipe[1]);
printf(" child close data0,block1\n");
printf("... exiting Child process\n");
}
//shm_unlink(name);
//fclose(r);
fclose(w);
return 0;
}
Result:
inputlength is 4177
parent before close data0,block1
parent close data0,block1
... parent process
parent read from input file into shared memory
parent data read 4096
i=1 parent write to data pipe
parent read from block pipe
child before close data1,block0
child close data1,block0
.... child process
child read from datapipe
child wrote to blockpipe blocknumber=1
child There are 4096 bytes
child writing to file
parent block 1 Received succesfully
... parent process
parent read from input file into shared memory
parent data read 81
parent read from block pipe
parent block 2 Received succesfully
i=2 parent write to data pipe
Note child write block number 1, but parent received block number 1, then received block number 2, and after the 2nd write from parent it stopped, without writing output file. Any thoughts for that?

How can I generate three child processes respecting a particular order?

I'm assigned to make a program that creates three child processes as follows: A, B and C.
Process A reads data from a file and sends 4KB-long fragments to a message queue.
Process B reads these fragments from the queue, converts them and writes the
new data on the queue.
Finally, process C reads the converted string from the message queue and writes it on another file.
I used the fork() function to create them, and I'm having trouble generating the three child processes in a sequential order. When I run the program, usually processes B or C are created before A, and they can't read properly from the message queue because process A hasn't been generated yet.
How could I solve this problem?
This is the code I've been working on:
(Note: Two parameters must be added before running the program: ./program.exe source.txt destination.txt)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/msg.h>
#include <unistd.h>
#define TAM 4096
#define FILEKEY "/bin/cat"
#define KEY 4242
#define N_PROCESSES 3
typedef struct msgbuf{
long type;
char text[TAM];
}message;
int main (int argc, char *argv[]){
key_t key;
pid_t pid;
int msqid;
message env, enda, endb;
message rcv;
message conv;
message msg;
int buf_length;
FILE *f1=NULL;
FILE *f2=NULL;
char string[TAM], *receive;
int rc;
int i;
int status;
int p;
if(argc < 2){
printf("Incorrect command line arguments");
exit(-1);
}
p = getpid();
/*Opens files*/
f1 = fopen(argv[1], "r");
if(f1 == NULL){
exit(-1);
}
f2 = fopen(argv[2], "w");
if(f2 == NULL){
fclose(f1);
exit(-1);
}
/*Obtains key for message queue*/
key = ftok(FILEKEY, KEY);
if(key == -1){
fclose(f1);
fclose(f2);
exit(-1);
}
/*Creates message queue*/
msqid = msgget(key, IPC_CREAT | 0600);
if(msqid == -1){
fclose(f1);
fclose(f2);
exit(-1);
}
/*Message types*/
env.type = 1; /*Message from process A to process B*/
enda.type = 2; /*Process A has finished reading data from f1*/
conv.type = 3; /*Message from process B to process C*/
endb.type = 4; /*Process B has finished converting the string*/
/*Message size (4KB)*/
buf_length = sizeof(message)-sizeof(long);
/*Creates processes A, B and C*/
for ( i = 0; i < N_PROCESSES; i++){
pid = fork();
if(pid == -1){ /*Error*/
msgctl(msqid, IPC_RMID, (struct msqid_ds *)NULL);
fclose(f1);
fclose(f2);
exit(-1);
}else if (pid == 0 && i == 0){/*Process A*/
/*Reads from f1 while end of file is not reached*/
while (fgets(string, TAM, f1) !=NULL){
/*Copies string to env.text*/
strcpy(env.text, cadena);
/*Sends text fragments (4KB) to message queue*/
if(msgsnd(msqid, &env, buf_length, IPC_NOWAIT)<0){
msgctl(msqid, IPC_RMID, (struct msqid_ds *)NULL);
fclose(f1);
fclose(f2);
exit(-1);
}
}
/*Process A sends this message when there's no more data to read*/
if(msgsnd(msqid, &enda, buf_length, IPC_NOWAIT)<0){
msgctl(msqid, IPC_RMID, (struct msqid_ds *)NULL);
fclose(f1);
fclose(f2);
exit(-1);
}
exit(EXIT_SUCCESS);
}else if(pid == 0 && i == 1){/*Process B*/
/*Reads text fragments (4KB) from message queue*/
while (msgrcv(msqid, &rcv, buf_length, 1, IPC_NOWAIT)>0) {
/*Converts string*/
strcpy(receive, rcv.text);
for(i = 0; i < TAM; i++){
receive[i] = toupper(receive[i]);
}
strcpy(conv.text, receive);
/*Sends fragments of converted string to message queue*/
if(msgsnd(msqid, &conv, buf_length, IPC_NOWAIT)<0){
msgctl(msqid, IPC_RMID, (struct msqid_ds *)NULL);
fclose(f1);
fclose(f2);
exit(-1);
}
}
/*The loop finishes when there's an error or when there are no more type 1 messages to read*/
/*Reads type 2 message from process A*/
rc = msgrcv(msqid, &rcv, buf_length, 2, 0);
if( rc == -1){
msgctl(msqid, IPC_RMID, (struct msqid_ds *)NULL);
fclose(f1);
fclose(f2);
exit(-1);
}
/*Process B sends this message indicating that it has finished sending string fragments*/
if(msgsnd(msqid, &endb, buf_length, IPC_NOWAIT)<0){
msgctl(msqid, IPC_RMID, (struct msqid_ds *)NULL);
fclose(f1);
fclose(f2);
exit(-1);
}
exit(EXIT_SUCCESS);
}else if(pid == 0 && i == 2){/*Process C*/
/*Reads converted string fragments from message queue*/
while (msgrcv(msqid, &msg, buf_length, 3, IPC_NOWAIT)>0) {
/*Writes fragments on another file*/
if(fputs(msg.text, f2)<0){
exit(-1);
}
}
/*The loop finishes when there are no more fragments to write on f2*/
/*Then process C reads the last message sent from B*/
rc = msgrcv(msqid, &rcv, buf_length, 4, 0);
if( rc == -1){
msgctl(msqid, IPC_RMID, (struct msqid_ds *)NULL);
fclose(f1);
fclose(f2);
exit(-1);
}
exit (EXIT_SUCCESS);
}
}
/*Parent process waits for processes A, B and C*/
for (i=0; i<N_PROCESSES; i++) {
pid=wait(&status);
printf("Process %d with PPID = %d terminated\n", pid, p);
}
msgctl(msqid, IPC_RMID, (struct msqid_ds *)NULL);
fclose(f1);
fclose(f2);
return (EXIT_SUCCESS);
}
The processes are created in the order you specified, but the scheduler can schedule them in any order it likes, so you find code from process B executing before process A has finished, etc.
What I'd do to set up the situation you want is have 2 queues, one from A to B, the other from B to C. I'd create them all in the parent, to keep things simple.
Then I'd have the reading children do blocking reads (msgrcv() without IPC_NOWAIT), or I'd loop until I got a failure other than no data received.
The sending processes (A and B) would both put "and that's all" indicators into the queue when done, so the receivers would know they had everything.

Pipes and Forks

The goal of this project is to use pipes and forks to execute a line-count utility already written in a multi-process manner (one process per argument). I'm currently working on getting a single process working before expanding to handle multiple args.
Given two executables, lc1 and lc2, I want lc2 to establish a pipe to the stdout file descriptor of lc1, so that when execlp("lc1", argv[1], NULL) is called, the output will be read in by
while ((c= read(pipefd[0], readin, SIZE)) > 0)
According to my Unix book, I should use the open, dup2, close method for redirecting stdout to stdin, and here's my code:
int pid, c, i;
char *readin= (char *)malloc(sizeof(SIZE));
if (pipe(pipefd)== -1)
perror("Can't open a pipe\n");
for (i=1; i< argc; i++){
if ((pid= fork())==-1)
perror("Can't fork\n");
run(argv[i]);
}
//close pipe
close(1);
if (dup2(pipefd[0], 0)==-1)
perror("Can't redirect stdin");
close(pipefd[1]);
for (i=1; i< argc; i++){
if ((wait(NULL))== -1)
perror("Wait error");
while ((c= read(pipefd[0], readin, SIZE)) > 0){;
//print buf count
total += atoi(readin);
}
}
The run function is
void run(char *f){
int fp;
if ((fp= open(f, O_RDONLY)) == -1)
perror("Can't open the file");
close(pipefd[0]);
dup2(pipefd[1], 1);
close(pipefd[1]);
execlp("ls1", f, NULL);
}
When I try to execute this code, I get a stdin redirect error saying bad file descriptor. Why is this happening, and would appreciate any hints to for fixing.
run(argv[i]) is executed by both parent and child because are not assigning the functionality based on the returned PID, so one close after the other may have closed.
See below code, can he handy, I will use the code sample for situations like this. :
int main()
{
int pipe_fd[2] = {0};
int pid = -1;
int status = -1;
int ret_value = INVALID_CMD;
int cmd_output_len = -1;
status = pipe(pipe_fd);
if(status<0)
{
perror("pipe create err");
}
else
{
pid = fork();
if(pid<0)
{
}
else if (pid == 0)
{
/*Child functionality*/
child_func(pipe_fd, cmd);
}
else
{
/*Parent functionality*/
cmd_output_len = parent_fun(pid, pipe_fd);
}
}
return ret_value;
}
int child_func(int pipe_fd[], const char * cmd)
{
int status = 5;
int read_fd = pipe_fd[0]; /*read file descriptor*/
int write_fd = pipe_fd[1]; /*write file descriptor*/
int exit_status = 0;
/*close read fd*/
close(read_fd);
/*dup2 stdout to write fd*/
//status = dup2(1, write_fd);
status = dup2(write_fd, 1);
if(status<0)
{
exit(-1);
}
else
{
system(cmd);
exit(0);
}
}
int parent_fun(int child_id, int pipe_fd[])
{
int status = -1;
int len = 0;
bool_e break_loop = FALSE;
int read_fd = pipe_fd[0]; /*read file descriptor*/
int write_fd = pipe_fd[1]; /*write file descriptor*/
/*close write fd*/
close(write_fd);
while(1)
{
sleep(1);
status = waitpid(child_id, &status, WNOHANG);
switch(status)
{
case 0:
/*Child is still active*/
printf("No process waiting to exit..\n");
len = do_ur_fun(read_fd);
write(1, output, len);
break;
/*case EINTR:
case ECHILD:
case EINVAL:
perror("waitpid error");
break_loop = TRUE;
break;*/
default:
if(status<0)
{
perror("waitpid error");
break_loop = TRUE;
len = -1;
}
else if(child_id == status)
{
/*Valid staus from child*/
len = read_output(read_fd, output);
//write(1, output, len);
break_loop = TRUE;
}
else
{
}
break;
}
if(TRUE == break_loop)
{
break;
}
}
return len;
}
int do_ur_fun (int read_fd)
{
/*Do your exec*/
}
MaheshGupta024 identified a very important problem in your code; I'm assuming you will fix that.
One of the other problem areas is:
close(1);
if (dup2(pipefd[0], 0)==-1)
perror("Can't redirect stdin");
close(pipefd[1]);
for (i=1; i< argc; i++){
if ((wait(NULL))== -1)
perror("Wait error");
while ((c= read(pipefd[0], readin, SIZE)) > 0){;
//print buf count
total += atoi(readin);
}
}
The first close closes the process's standard output; this is seldom a good idea. The next line duplicates the read end of the pipe to standard input - which is fine. As noted in a comment above, perror() does not exit. You then close the write end of the pipe - that's correct; but you should presumably close the read end of the pipe too since you have set it to come from the pipe.
Your loop starts OK; you have redundant parentheses in the wait() line. You read from pipefd[0] instead of standard input - so maybe you didn't want to close pipefd[0] but neither did you need to duplicate it to standard input. You then have a nested loop that reads on the pipe while there's more data to be read from a child - you don't absolutely need the wait() code with its loop since the inner while won't terminate until all the children are dead. On the other hand, there's no great harm in it - after the first child dies, you'll read the data from all the other children, then go into the outer loop and wait for each other child, with the inner loop terminating immediately since there is no data left to read.
So:
Don't close stdout.
Don't dup the pipe read to stdin.
Decide whether you want to clean up the loop - it will work, but could be cleaner.
The run() function is:
void run(char *f){
int fp;
if ((fp= open(f, O_RDONLY)) == -1)
perror("Can't open the file");
close(pipefd[0]);
dup2(pipefd[1], 1);
close(pipefd[1]);
execlp("ls1", f, NULL);
}
The argument should be const char *f (or use name or file instead of f). I would also pass the pipefd array to the function rather than use a global variable
.
Do not call a file descriptor fp; that name conventionally indicates a variable of type FILE *, not int.
However, you don't need to open the file in the first place - unless you want the calling program to do the error reporting instead of the invoked program. However, if you do want the calling program to do the error reporting, you should close the file descriptor before proceeding. (I've already commented on perror() returning).
It would be a good idea to print an error message after execlp(); the only time the function returns is when it fails, so there is no need to test its return value. You might want to exit too - rather than have the failed function go through the rest of the main program after the call to run().
Good points: you did close both the pipe file descriptors.
Hence:
void run(const char *file, int *pipefd)
{
close(pipefd[0]);
dup2(pipefd[1], 1);
close(pipefd[1]);
execlp("ls1", f, NULL);
perror("Failed to exec ls1");
exit(EXIT_FAILURE);
}

Resources