working with named pipes and semaphores in linux - c

I've been trying to get my program to work for several hours now and I just can't fgure out what's wrong with my code. It's about passing a variable between processess using pipes. Each process increments it M times. The program works perfectly when I use shared memory, but when I change it to using pipes it's a disaster. Creating or using named pipes doesn't seem to work at all, or I guess I'm just doing it the wrong way. Here's the source code:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/mman.h>
#include <unistd.h>
#include <memory.h>
#include <fcntl.h>
#include <sys/stat.h>
#define PIPE_NAME "MY_PIPE"
#define N 5
#define M 10
struct sembuf operations;
int semid;
key_t key;
int marker;
void semWait(int semid, int sempos) {
operations.sem_num = sempos;
operations.sem_op = -1;
operations.sem_flg = 0;
if (semop(semid, &operations, 1) < 0) {
perror("ERROR: semop wait\n");
exit(-1);
}
}
void semPost(int semid, int sempos) {
operations.sem_num = sempos;
operations.sem_op = 1;
operations.sem_flg = IPC_NOWAIT;
if (semop(semid, &operations, 1) < 0) {
perror("ERROR: semop post\n");
exit(-1);
}
}
void worker(int id) {
int j, nmarker;
int fd = open(PIPE_NAME, O_RDWR);
read(fd, &nmarker, sizeof(int));
for (j = 0 ; j < M; j++) {
semWait(semid, id);
nmarker = nmarker + 1 ;
printf("%d ", marker);
semPost(semid, N);
}
write(fd, &nmarker, sizeof(nmarker));
close(fd);
}
main() {
int i, tempPID;
int sarray[N+1] = {0};
key = 23;
marker = 0;
if ((semid = semget(key , N+1, 0666 | IPC_CREAT)) == -1) {
perror("ERROR: semget\n");
exit(-1);
}
if ((semctl(semid, N+1, SETALL, sarray)) < 0) {
perror("ERROR: semctl - val\n");
exit(-1);
}
if(mkfifo(PIPE_NAME, S_IFIFO | 0666) < 0) {
perror("ERROR:pipe\n");
exit(-1);
}
int fd;
if( fd = open(PIPE_NAME, O_WRONLY) < 0 ){
perror("ERROR:open\n");
exit(-1);
}
write(fd, &marker, sizeof(marker));
close(fd);
for(i = 0; i < N; i++) {
tempPID = fork();
if (tempPID < 0) {
perror("ERROR: fork\n");
exit(-1);
}
else if (tempPID == 0) { // if child
worker(i);
exit(0);
}
}
for (i = 0 ; i < (M*N); i++) {
semPost(semid, i%N);
semWait(semid, N);
}
printf("Marker = %d\n", marker);
if (semctl( semid, 1, IPC_RMID ) == -1) {
perror("ERROR: semctl free\n");
exit(-1);
}
unlinc(PIPE_NAME);
}
I create N worker processes and each one has to increment the marker value M times. I have to create a pool of 'sleeping' processes and waken them one by one using semaphores but it's all a blur so the current source code is all I came up with... :\
This is a version of the same program but with shared memory instead of pipes:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/mman.h>
#define N 5
#define M 10
struct sembuf operations;
int semid;
key_t key;
int *sharedmem;
void semWait(int semid, int sempos) {
operations.sem_num = sempos;
operations.sem_op = -1;
operations.sem_flg = 0;
if (semop(semid, &operations, 1) < 0) {
perror("ERROR: semop wait\n");
exit(-1);
}
}
void semPost(int semid, int sempos) {
operations.sem_num = sempos;
operations.sem_op = 1;
operations.sem_flg = IPC_NOWAIT;
if (semop(semid, &operations, 1) < 0) {
perror("ERROR: semop post\n");
exit(-1);
}
}
void worker(int id) {
int j;
for (j = 0 ; j < M; j++) {
semWait(semid, id);
(*sharedmem)++;
semPost(semid, N);
}
}
main() {
int i, tempPID;
int sarray[N+1] = {0};
int protect = PROT_READ | PROT_WRITE;
int flags = MAP_SHARED | MAP_ANONYMOUS;
if ((key = ftok("/dev/null", 4343)) == -1) {
perror("ERROR: ftok\n");
exit(-1);
}
if ((semid = semget(key , N+1, 0666 | IPC_CREAT)) == -1) {
perror("ERROR: semget\n");
exit(-1);
}
if ((semctl(semid, N+1, SETALL, sarray)) < 0) {
perror("ERROR: semctl - val\n");
exit(-1);
}
sharedmem = (int*)mmap(NULL, sizeof(int), protect, flags, 0, 0);
*(sharedmem) = 0;
for(i = 0; i < N; i++) {
tempPID = fork();
if (tempPID < 0) {
perror("ERROR: fork\n");
exit(-1);
}
else if (tempPID == 0) { // if child
worker(i);
exit(0);
}
}
for (i = 0 ; i < (M*N); i++) {
semPost(semid, i%N);
semWait(semid, N);
}
printf("Marker = %d\n", *sharedmem);
if (semctl( semid, 1, IPC_RMID ) == -1) {
perror("ERROR: semctl free\n");
exit(-1);
}
munmap(sharedmem, sizeof(int));
}

Some of your problems are in the worker code - these two lines:
int fd = open(PIPE_NAME, O_RDWR);
read(fd, &nmarker, sizeof(int));
If you open the pipe for reading and writing, you are asking for trouble (IMNSHO). Open it for reading only, read it, close it. Then open it for writing only, write to it, close it. Now you have to consider where the semaphore operation should occur. You actually need to wake the next process before you try to open the pipe for writing, because the open for writing will block until there is a process available to read from it. Similarly, the process that opens for reading will block until there is a process available to write to it. So, the kernel will coordinate the processes.
You don't check the return value from open(), so you've no idea whether you got a valid file descriptor. Always check the return status of open().
You don't check the return value from read(), so you've no idea whether you got anything valid off the pipe. Always check the return status of read().
(You can decide to ignore the return status of write() if there is no meaningful error recovery possible for a failed write, but it is not a bad idea to check that it did work. You can decide to ignore the return status of close() for similar reasons, though you might not get to know about problems until you do the close().)
Continuing in the worker code:
for (j = 0 ; j < M; j++) {
semWait(semid, id);
nmarker = nmarker + 1 ;
printf("%d ", marker);
semPost(semid, N);
}
It is surprising to see you printing marker rather than nmarker; and surely, basic diagnostic technique prints the value of nmarker when it is read. You might or might not print j and nmarker on each iteration. Note that since nothing in this code increments marker, the value printed won't change.
The logic sequence here is interesting...it combines with the loop in main() most oddly. The parent process writes one value to the FIFO. Only one child gets to read that value - the rest get EOF immediately, or hang indefinitely (depending on whether you use O_RDONLY or O_RDWR in the children). Each child gets signalled to increment its value, does so, and then goes back to sleep until woken again. There is nothing that sends the incremented value to the next child. So each child is independently incrementing whatever value it chooses - which is probably garbage. With shared memory, if you had a pointer to the shared value, then the increments were seen by all processes at once - that's why it is called shared memory. But here there is no shared memory, so you have to communicate explicitly to get it to work. (I wonder if your FIFO plus shared memory implementation worked because the communication was via shared memory - by accident, in other words?)
So, if the child is to increment the variable it reads each time, it must both read the current value and write the new value each time around the loop. This would be an error-checked read, of course. You might be OK with O_RDWR because of the semaphores, but I'd personally be happier with the separate opens for read and write - on each iteration if need so be. But I haven't implemented this to check that it really does run into problems; it is simply aconventional to use O_RDWR on a FIFO.
After your child has incremented its value N times, it writes the result to the pipe.
write(fd, &nmarker, sizeof(nmarker));
close(fd);
The main program then does:
printf("Marker = %d\n", marker);
if (semctl( semid, 1, IPC_RMID ) == -1) {
perror("ERROR: semctl free\n");
exit(-1);
}
unlinc(PIPE_NAME);
Since it has not modified marker, the value printed will be 0. You should be having the main process read the replies from each of the children.
The correct function for unlinking a FIFO is unlink() or remove().
Discussion
As noted in a comment, one problem was that opening the FIFO was blocking - no readers. However, that was far from the only problem.
The code below runs. I haven't verified that the number is being incremented as it should (but it is being incremented). I've not checked that every process is getting its turn. I've revised the error handling (one line per call instead of 3 or 4), and added a printing function that includes the PID in the output. I've error checked every system call (but none of the printing statements). I fixed a problem if (fd = open(...) < 0). As far as I could tell, closing the FIFO in the master process discarded the content written to it - so the parent no longer closes the FIFO immediately. But mainly I moved the read and write of the FIFO into the worker loop - leaving open and close outside. The code is also laced with diagnostic printing so I can see where it is going wrong when it is going wrong. I haven't done header minimization or any of a number of other cleanups that should occur. However, everything except main() is static so it doesn't have to be pre-declared. It compiles clean under:
/usr/bin/gcc -O3 -g -std=c99 -Wall -Wextra fifocircle.c -o fifocircle
Code
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/mman.h>
#include <unistd.h>
#include <memory.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdarg.h>
#include <errno.h>
#include <string.h>
static const char *arg0 = "undefined";
static void err_error(const char *fmt, ...)
{
int errnum = errno;
va_list args;
fflush(0);
fprintf(stderr, "%s: pid %d:", arg0, (int)getpid());
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
if (errnum != 0)
fprintf(stderr, "(%d: %s)", errnum, strerror(errnum));
fputc('\n', stderr);
exit(1);
}
static void print(const char *fmt, ...)
{
va_list args;
printf("pid %d: ", (int)getpid());
va_start(args, fmt);
vfprintf(stdout, fmt, args);
va_end(args);
fflush(0);
}
#define PIPE_NAME "MY_PIPE"
#define N 5
#define M 10
static struct sembuf operations;
static int semid;
static key_t key;
static int marker;
static void semWait(int semid, int sempos)
{
operations.sem_num = sempos;
operations.sem_op = -1;
operations.sem_flg = 0;
if (semop(semid, &operations, 1) < 0)
err_error("semop wait");
}
static void semPost(int semid, int sempos)
{
operations.sem_num = sempos;
operations.sem_op = 1;
operations.sem_flg = IPC_NOWAIT;
if (semop(semid, &operations, 1) < 0)
err_error("semop post");
}
static void worker(int id)
{
int j;
int fd = open(PIPE_NAME, O_RDWR);
if (fd < 0)
err_error("failed to open FIFO %s for read & write", PIPE_NAME);
print("Worker %d: fd %d\n", id, fd);
for (j = 0 ; j < M; j++)
{
int nmarker;
print("waiting for %d\n", id);
semWait(semid, id);
if (read(fd, &nmarker, sizeof(int)) != sizeof(int))
err_error("short read from FIFO");
print("Got %d from FIFO\n", nmarker);
nmarker = nmarker + 1 ;
if (write(fd, &nmarker, sizeof(nmarker)) != sizeof(nmarker))
err_error("short write to FIFO");
print("Wrote %d to FIFO\n", nmarker);
print("posting %d\n", id);
semPost(semid, N);
}
if (close(fd) != 0)
err_error("failed to close FIFO");
print("done\n");
}
int main(int argc, char **argv)
{
int i;
int sarray[N+1] = {0};
key = 23;
marker = 0;
arg0 = argv[0];
if (argc != 1)
err_error("Usage: %s\n", arg0);
if ((semid = semget(key , N+1, 0666 | IPC_CREAT)) == -1)
err_error("semget");
if ((semctl(semid, N+1, SETALL, sarray)) < 0)
{
perror("ERROR: semctl - val\n");
exit(-1);
}
if (mkfifo(PIPE_NAME, S_IFIFO | 0666) < 0)
err_error("failed to create FIFO %s\n", PIPE_NAME);
print("FIFO created\n");
int fd;
if ((fd = open(PIPE_NAME, O_RDWR)) < 0 )
err_error("failed to open FIFO %s\n", PIPE_NAME);
print("FIFO opened\n");
if (write(fd, &marker, sizeof(marker)) != sizeof(marker))
err_error("short write to FIFO");
print("FIFO loaded\n");
print("Master: about to fork\n");
for (i = 0; i < N; i++)
{
pid_t pid = fork();
if (pid < 0)
err_error("failed to fork");
else if (pid == 0)
{
worker(i);
exit(0);
}
}
print("Master: about to loop\n");
for (i = 0 ; i < (M*N); i++)
{
print("posting to %d\n", i%N);
semPost(semid, i%N);
print("waiting for %d\n", N);
semWait(semid, N);
}
if (close(fd) != 0)
err_error("failed to close FIFO");
print("Marker = %d\n", marker);
if (semctl( semid, 1, IPC_RMID ) == -1)
err_error("semctl remove");
if (unlink(PIPE_NAME) != 0)
err_error("failed to remove FIFO %s", PIPE_NAME);
return(0);
}

Related

How do I print stored data from the shared memory?

I have the following program:
#include <stdio.h>
#include <sys/types.h>
#define MAX_COUNT 100
void ChildProcess(void);
void ParentProcess(void);
void main(void)
{
pid_t pid;
pid = fork();
if (pid == 0)
ChildProcess();
else
ParentProcess();
}
void ChildProcess(void)
{
int i;
for (i = 1; i <= MAX_COUNT; i++)
printf(" This line is from child, value = %d\n", i);
printf(" *** Child process is done ***\n");
}
void ParentProcess(void)
{
int i;
for (i = 1; i <= MAX_COUNT; i++)
printf("This line is from parent, value = %d\n", i);
printf("*** Parent is done ***\n");
}
I have to modify it in a way that both the parent and the child print stored data from the shared memory in the following way:
Create and initialize the shared memory in the parent.
Fill the shared memory with 5 integer numbers. (I should allocate enough shared memory to store the 5 ints.)
Fork from the parent to the child.
If fork is successful, then the child process must print the values stored in the shared memory as shown in the expected output where N1, N2, N3, N4, N5 are the numbers found in the shared memory.
Expected output
What I did in the ParentProcess function is the following:
void ParentProcess(void)
{
int i;
for (i = 1; i <= MAX_COUNT; i++)
printf("This line is from parent, value = %d\n", i);
printf("*** Parent is done ***\n");
int localVar = 0;
int* p = (int*) malloc(2);
pid_t childPID = fork();
*p = 0;
if (childPID >= 0)
{
printf("\nChild process has started\n");
if (childPID == 0)
{
localVar++;
globalVar++;
printf("Child process has found the following data %d,", *p);
*p = 70;
printf( " %d,", *p);
*p = 66;
printf(" %d,", *p);
*p = 51;
printf(" %d,", *p);
*p = 90;
printf(" %d in shared memory\n",*p);
printf("Child is existing\n\n");
}
}
}
And now I realize that I did it completely wrong but I have no idea how to fix that. I suppose I have to use shmget to create the shared memory, but then what? How do I store values in it?
If you find that you cannot help me with this or it is too long, please share sources where I can learn more about C programming in Linux, particularly regarding the usage of shared memory. Thank you in advance
It may be better to make it clear what you want to do first because as far as I read your code you call fork() twice in your code (once in main() function and once in ParentProcess() function)
So I write general solution for parent/child shared memory. There are several ways to achieve shared memory but this is one example which is modified version of the code here
How to use shared memory with Linux in C
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/wait.h>
void *create_shared_memory(size_t size)
{
int protection = PROT_READ | PROT_WRITE;
int visibility = MAP_SHARED | MAP_ANONYMOUS;
return mmap(NULL, size, protection, visibility, -1, 0);
}
int main()
{
// Allocate 4 ints
void *shmem = create_shared_memory(sizeof(int)*4);
if( shmem == NULL ){
fprintf(stderr, "Failed to create shared memory\n");
return -1;
}
// Initialize 4 ints
((int*)shmem)[0] = 10;
((int*)shmem)[1] = 100;
((int*)shmem)[2] = 1000;
((int*)shmem)[3] = 10000;
int pid = fork();
if (pid == 0)
{
// Print 4 ints in child
printf("Child reading int 0: %d\n", ((int*)shmem)[0]);
printf("Child reading int 1: %d\n", ((int*)shmem)[1]);
printf("Child reading int 2: %d\n", ((int*)shmem)[2]);
printf("Child reading int 3: %d\n", ((int*)shmem)[3]);
printf("Child end\n");
}
else
{
printf("Parent waiting for child ends...\n");
waitpid(pid, NULL, 0);
printf("Parent ends\n");
}
int ret = munmap(shmem, sizeof(int)*4);
if( ret != 0 ){
fprintf(stderr, "Failed to unmap shared memory\n");
return -1;
}
return 0;
}
I've written a small piece of c code which you might find helpful:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#define NUM_INTS 5
int main(int argc, char *argv[])
{
key_t key = (key_t) 123456;
int shmgetrc, semgetrc;
struct shmid_ds ds;
int *shared_values;
int i;
struct sembuf sops[2];
int semid;
sops[0].sem_num = 0; /* Operate on semaphore 0 */
sops[0].sem_op = 0; /* Wait for value to equal 0 */
sops[0].sem_flg = 0;
sops[1].sem_num = 0; /* Operate on semaphore 0 */
sops[1].sem_op = 1; /* Increment value by one */
sops[1].sem_flg = 0;
/* create SHM segment */
shmgetrc = shmget(key, NUM_INTS * sizeof(int), IPC_CREAT | IPC_EXCL | 0x180);
if (shmgetrc < 0) {
perror("shmget failed...");
exit(1);
}
/* retrieve the address of the segment */
shared_values = (int *) shmat(shmgetrc, NULL, 0);
/* create a semaphore */
semgetrc = semget(key, 1, IPC_CREAT | IPC_EXCL | 0x180);
if (semgetrc < 0) {
perror("semget failed...");
exit(1);
}
/* lock the semaphore */
if (semop(semgetrc, sops, 2) == -1) {
perror("semop lock failed ...");
exit(1);
}
/* fill it with values */
for (i = 0; i < NUM_INTS; ++i) {
shared_values[i] = i;
}
/* unlock the semaphore */
sops[0].sem_op = -1;
if (semop(semgetrc, sops, 1) == -1) {
perror("semop release failed ...");
exit(1);
}
/* here something else could happen */
sleep(60);
/* lock the semaphore */
sops[0].sem_op = 0;
if (semop(semgetrc, sops, 2) == -1) {
perror("semop lock failed ...");
exit(1);
}
/* print values */
for (i = 0; i < NUM_INTS; ++i) {
printf("%d ", shared_values[i]);
}
printf("\n");
/* unlock the semaphore */
sops[0].sem_op = -1;
if (semop(semgetrc, sops, 1) == -1) {
perror("semop release failed ...");
exit(1);
}
/* remove the semaphore */
if (semctl(semgetrc, semgetrc, IPC_RMID) < 0) {
perror("semctl failed ...");
exit(1);
}
/* remove shm segment again */
if (shmctl(shmgetrc, IPC_RMID, &ds) < 0) {
perror("shmctl failed ...");
exit(1);
}
exit(0);
}
It was not my intention to write the most beautiful code ever written, just an example that shows:
how to create a shm segment
how to retrieve the address and to use it
how to remove it
Additionally, I've used a semaphore to protect the access.
Contrary to the other answer, I've used the ipc interface, not mmap().

Reading and Writing to a Named Pipe

I'm working with two programs. The customer.c program writes an int to a named pipe and the bank.c reads the pipe and prints the int. The customer will choose one of two named pipes, "atm1" and "atm2".
Ultimately I would like to run two customer.c programs, one for each pipe at the same time, but I have some issues with the writing and reading to the named pipe.
If I just run bank.c and one customer.c I don't get any output.
If I run bank.c and two customer.c the output doesn't always print or is out of order when it does.
I tried to use fsync() to flush but that didn't work either.
customer.c
int main(int argc, char *argv[]){
int fd, num =0;
if((fd = open(argv[1], O_WRONLY)) == -1){
...
}
while(1){
printf("Enter a integer:\n");
scanf("%d", &num);
if(num < 0){
break;
}
if(write(fd, &num, sizeof(num)) <= 0){...}
fsync(fd);
}
close(fd);
return 0;
}
bank.c
int main(){
int fd, sd, num=0, sret, fret, maxfd;
//fd_set readfds;
//struct timeval timeout;
if(mkfifo("atm1", 0666) == -1){...}
if(mkfifo("atm2", 0666) == -1){...}
if((fd = open("atm1", O_RDONLY)) == -1){...}
if((sd = open("atm2", O_RDONLY)) == -1){...}
while(1){
if((sret = read(sd, &num, sizeof(num))) > 0){
printf("%d\n", num);
}
if((fret = read(fd, &num, sizeof(num))) > 0){
printf("%d\n", num);
}
if(sret <= 0 && fret <= 0){
break;
}
}
close(fd);
close(sd);
return 0;
}
Any pointer?
you need to make multi-threads server for multiple same time recv. and require to check pipe name is already exist.
blank.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <pthread.h>
void *client(void *data)
{
char *pname = (char*) data ;
int sd =0 ;
int num ;
int sret=0 ;
if((sd = open(pname, O_RDONLY)) == -1) {
printf("open failed:%s\n", pname) ;
return NULL ;
}
printf("client[%d] start\n", sd) ;
while(1){
num=0 ;
if((sret = read(sd, &num, sizeof(num))) > 0){
printf("[%d] recv:%d\n", sd, num);
}
if(sret <= 0) {
break;
}
}
close(sd);
printf("client[%d] end\n", sd) ;
return NULL;
}
int main(){
int status ;
pthread_t t1, t2 ;
if(mkfifo("atm1", 0666) == -1) {
printf("mkfifio failed : %d\n", errno) ;
if (errno==EEXIST ) {
printf("already exist. ignore\n") ;
}
else
return -1;
}
if(mkfifo("atm2", 0666) == -1) {
printf("mkfifio failed2 : %d\n", errno) ;
if (errno==EEXIST ) {
printf("already exist. ignore\n") ;
}
else
return -1;
}
pthread_create(&t1, NULL, client, (void*)"atm1") ;
pthread_create(&t2, NULL, client, (void*)"atm2") ;
pthread_join(t1, (void**)&status) ;
pthread_join(t2, (void**)&status) ;
return 0;
}
you can access the atm1 and atm2 at the same time.
./customer atm1
./customer atm2

bad file descriptor in c program with forks

this program is supposed to simulate a posix shell in regards to commands with pipes. The example I've tried to simulate and wanna make work is "ls | nl", but it doesn't and I can't figure out why. I've debugged this code for many hours with no success.
I get the error: "nl: input error: Bad file descriptor", and when I've tried not closing any of the file descriptors or closing only some (or in only one of the forks, or only the parent, etc...), and the errors change, or it works but then nl keeps waiting for input. Anyways, I'm pretty sure the errors are in fork_cmd or fork_cmds and has to do with close.
I've included all the code. I know there's nothing wrong with parser.h. I know this is pretty shitty code but it should still work I think.
I'm probably blind, but I would really appreciate it if someone could help me figure it out. Hopefully it's something that I and maybe others can learn something from.
#include "parser.h"
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <stdbool.h>
#define READ 0
#define WRITE 1
void fork_error() {
perror("fork() failed)");
exit(EXIT_FAILURE);
}
void close_error() {
perror("Couldn't close file descriptor");
exit(EXIT_FAILURE);
}
void fork_cmd(char* argv[], int n, int read_pipe[2], int write_pipe[2], int (*all_fds)[2]) {
pid_t pid;
switch (pid = fork()) {
case -1:
fork_error();
case 0:
if (read_pipe != NULL) {
if (dup2(read_pipe[READ], STDIN_FILENO) < 0) {
perror("Failed to redirect STDIN to pipe");
exit(EXIT_FAILURE);
}
}
if (write_pipe != NULL) {
if (dup2(write_pipe[WRITE], STDOUT_FILENO) < 0) {
perror("Failed to redirect STDOUT to pipe");
exit(EXIT_FAILURE);
}
}
for (int i = 0; i < n - 1; i++) {
if (close(all_fds[i][READ]) == -1 || close(all_fds[i][WRITE] == -1)) {
close_error();
}
}
execvp(argv[0], argv);
perror("execvp");
exit(EXIT_FAILURE);
default:
printf("Pid of %s: %d\n", argv[0], pid);
break;
}
}
void fork_cmds(char* argvs[MAX_COMMANDS][MAX_ARGV], int n, int (*fds)[2]) {
for (int i = 0; i < n; i++) {
if (n == 1) {
fork_cmd(argvs[i], n, NULL, NULL, fds);
}
// n > 1
else if (i == 0) {
fork_cmd(argvs[i], n, NULL, fds[i], fds);
}
else if (i == n - 1) {
fork_cmd(argvs[i], n, fds[i - 1], NULL, fds);
}
else {
fork_cmd(argvs[i], n, fds[i - 1], fds[i], fds);
}
}
for (int i = 0; i < n - 1; i++) {
if (close(fds[i][READ]) == -1 || close(fds[i][WRITE] == -1)) {
close_error();
}
}
}
void get_line(char* buffer, size_t size) {
getline(&buffer, &size, stdin);
buffer[strlen(buffer)-1] = '\0';
}
void wait_for_all_cmds(int n) {
// Not implemented yet!
for (int i = 0; i < n; i++) {
int status;
int pid;
if ((pid = wait(&status)) == -1) {
printf("Wait error");
} else {
printf("PARENT <%ld>: Child with PID = %ld and exit status = %d terminated.\n",
(long) getpid(), (long) pid, WEXITSTATUS(status));
}
}
}
int main() {
int n;
char* argvs[MAX_COMMANDS][MAX_ARGV];
size_t size = 128;
char line[size];
printf(" >> ");
get_line(line, size);
n = parse(line, argvs);
// Debug printouts.
printf("%d commands parsed.\n", n);
print_argvs(argvs);
int (*fds)[2] = malloc(sizeof(int) * 2 * (n - 1)); // should be pointer to arrays of size 2
for (int i = 0; i < n - 1; i++) {
if (pipe(fds[i]) == -1) {
perror("Creating pipe error"); // Creating pipe error: ...
exit(EXIT_FAILURE);
}
printf("pipe %d: read: %d, write: %d\n", i, fds[i][READ], fds[i][WRITE]);
}
fork_cmds(argvs, n, fds);
wait_for_all_cmds(n);
exit(EXIT_SUCCESS);
}
The problem was that one of the parenthesis was at the wrong place in both fork_cmd and fork_cmds, it should be like this of course: close(fds[i][WRITE]). This was the original code:
for (int i = 0; i < n - 1; i++) {
if (close(fds[i][READ]) == -1 || close(fds[i][WRITE] == -1))<--
{
close_error();
}
}

named pipe won't open in C program

I have user read/write permissions on a pipe. Group has read. Other has read. But program gets "stuck" when I run it. Program 1 is the "parent". Program 2 is the "child".
Program 1:
int main(int argc, char * argv[])
{
FILE *fptr; //for opening and closing input file
int fdw;// write to pipe;
int fdr; //read to pipe;
pid_t pid;
int inputarray[500];
int arraylength = 0; int j =0;
char *mypipe = "mypipe";
if (argc < 2)
{
printf("Need to provide the file's name. \n");
return EXIT_FAILURE;
}
//open input file
fptr = fopen(argv[1], "r");
if (fptr==NULL)
{
printf("fopen fail.\n");
return EXIT_FAILURE;
}
//read input file and fill array with integers
while (!feof(fptr))
{
fscanf(fptr,"%d",&inputarray[arraylength]);
arraylength = arraylength + 1;
}
fclose(fptr); //close input file
pid = fork();
mkfifo(mypipe, 0666);
fdw = open("mypipe",O_WRONLY);
if (fdw < 0)
{
perror("File can't open to write.");
return;
}
int b;
b=3;
write(fdw,&b,sizeof(b));
close(fdw);
if ( pid ==-1)
{
perror("fork");
exit(1);
}
int status; //exit status of child
if(pid==0)//if child process
{
execl("program2", (char*) NULL);
}
else //if parent process
{
wait(&status);}
if((WIFEXITED(status)))
{
printf("Child's exit code %d", WEXITSTATUS(status));
}
else{
printf("Child did not terminate with exit");}
}
Program 2:
int fdl;
int data;
fdl = open("mypipe",O_RDONLY);
if ( fdl < 0)
{
perror("File can't open to read.");
return;
}
read(fdl,&data,sizeof(data));
close(fdl);
The program will block on writing to the fifo until what it's writing is being read. The reading in the child process won't happen since the execl() doesn't happen until after the writing.
Also, it looks like both processes will actually attempt to write to the fifo since you fork() and then immediately start writing.
You should fork(), then test on the returned PID. The parent should then write to the fifo while the child should call execl(). The fifo should be created by the parent before the fork() call.
You should also consider using indent or clang-format to properly format your code, which eases reading it and may expose bugs (forgotten curly braces etc.).
A simple complete example program. The parent writes a string to the child and the child reads it character by character and outputs it to standard output:
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
void parent(void);
void child(void);
int main(void) {
pid_t pid;
mkfifo("myfifo", 0666); /* fails if exists, but we don't care here */
if ((pid = fork()) < 0)
abort();
if (pid == 0)
child(); /* will not return */
else
parent();
return EXIT_SUCCESS;
}
void parent(void) {
int fd;
int len;
int ret;
int stat;
char *ptr;
char *msg = "Hello World!";
if ((fd = open("myfifo", O_WRONLY)) < 0)
abort();
len = strlen(msg) + 1;
ptr = msg;
puts("Parent: About to write to child");
while ((ret = write(fd, ptr, len)) != 0) {
if (ret > 0) {
len -= ret;
ptr += ret;
} else
abort();
}
close(fd);
puts("Parent: Waiting for child to exit");
wait(&stat);
printf("Parent: Child exited with status %d\n", stat);
}
void child(void) {
int fd;
int ret;
char ch;
if ((fd = open("myfifo", O_RDONLY)) < 0)
abort();
puts("Child: About to read from parent");
while ((ret = read(fd, &ch, 1)) != 0) {
if (ret > 0)
putchar(ch);
else
abort();
}
putchar('\n');
close(fd);
puts("Child: I'm done here");
exit(EXIT_SUCCESS);
}
In this case, since both child and parent processes are in the same context, I could have used an anonymous pipe pair created with pipe(), but this illustrates the flow, including the creation of the named pipe.

how the system call read and write behave and why the threads cannot work?

fifo.3 source code:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pthread.h>
#include <time.h>
#define FIFO_NAME "/tmp/my_fifo"
#define BUFFER_SIZE PIPE_BUF //4096
#define TEN_MEG (1024 * 1024 * 1)
void* thread_tick(void* arg)
{
int count =0;
while(count < 4){
printf("hello, world!\n");
sleep(1);
count++;
}
}
void* thread_write(void* arg)
{
int pipe_fd;
int res;
int bytes_sent = 0;
char buffer[BUFFER_SIZE ];
int count=0;
if (access(FIFO_NAME, F_OK) == -1) {
res = mkfifo(FIFO_NAME, 0777);
if (res != 0) {
fprintf(stderr, "Could not create fifo %s\n", FIFO_NAME);
exit(EXIT_FAILURE);
}
}
while(count < 10){
printf("write: Process %d opening FIFO O_WRONLY\n", getpid());
pipe_fd = open(FIFO_NAME, O_WRONLY);
printf("write: Process %d result %d \n", getpid(), pipe_fd);
if (pipe_fd != -1) {
while(bytes_sent < TEN_MEG) {
res = write(pipe_fd, buffer, BUFFER_SIZE);
if (res == -1) {
fprintf(stderr, "Write error on pipe\n");
exit(EXIT_FAILURE);
}
bytes_sent += res;
}
(void)close(pipe_fd);
}
else {
exit(EXIT_FAILURE);
}
printf("write: Process %d finished , count =%d\n", getpid(),count);
count++;
}
}
void CreateThread(void* (*start_routine)(void*), void* arg,int stacksize, int priority)
{
pthread_t app_thread;
pthread_attr_t thread_attr;
int res;
int max_priority;
int min_priority;
struct sched_param scheduling_value;
res = pthread_attr_init(&thread_attr);
if (res != 0) {
perror("Attribute creation failed\n");
exit(EXIT_FAILURE);
}
res = pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
if (res != 0) {
perror("Setting detached attribute failed");
exit(EXIT_FAILURE);
}
res = pthread_attr_setstacksize(&thread_attr, stacksize);
if (res != 0) {
perror("Set stack size failed\n");
exit(EXIT_FAILURE);
}
res = pthread_attr_setschedpolicy(&thread_attr, SCHED_RR);
if (res != 0) {
perror("Setting schedpolicy failed");
exit(EXIT_FAILURE);
}
max_priority = sched_get_priority_max(SCHED_RR);
min_priority = sched_get_priority_min(SCHED_RR);
scheduling_value.sched_priority = priority;
res = pthread_attr_setschedparam(&thread_attr, &scheduling_value);
if (res != 0) {
perror("Setting schedpolicy failed");
exit(EXIT_FAILURE);
}
res = pthread_create(&app_thread, &thread_attr, (*start_routine), arg);
if(res != 0){
perror("Thread creation failed\n");
exit(EXIT_FAILURE);
}
pthread_attr_destroy(&thread_attr);
//res = pthread_join(app_thread ,0 );
//return app_thread;
}
int main()
{
CreateThread(thread_write, 0, 50000, 99);
CreateThread(thread_tick, 0, 50000, 98);
// pthread_join(w,0 );
// pthread_join(t ,0 );
return 0;
}
fifo.4 source code :
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#define FIFO_NAME "/tmp/my_fifo"
#define BUFFER_SIZE PIPE_BUF //4096
int main()
{
int pipe_fd;
int res;
char buffer[BUFFER_SIZE ];
int bytes_read = 0;
int count = 0;
memset(buffer, '\0', sizeof(buffer));
while(count < 10){
printf("read: Process %d opening FIFO O_RDONLY\n", getpid());
pipe_fd = open(FIFO_NAME, O_RDONLY);
printf("read: Process %d result %d\n", getpid(), pipe_fd);
if (pipe_fd != -1) {
do {
res = read(pipe_fd, buffer, BUFFER_SIZE);
bytes_read += res;
} while (res > 0);
(void)close(pipe_fd);
}
else {
exit(EXIT_FAILURE);
}
printf("read: Process %d finished, %d bytes read , count =%d\n", getpid(), bytes_read,count);
count++;
}
return 0;
}
this is the first time I post code on Stack overflow, so it is in a mess.
Above are two C source code. fifo3.c has two thread and thread_write is to write data to named fifo.
fifo4.c is to read data from named fifo.
my question:
1) how does the read(pipe_fd, buffer, BUFFER_SIZE) behave when write() is writing data to fifo? If read() can not read data, SHOULD not read() return 0 and then exit, why read() would wait write() to finish write data??? of course, how does write() behave when read() is reading?
2) in fifo3.c , I create two threads, when I create them detached , the program can not run !!!
but joinable, they could run correctly !!I do not know why!
In theory, they both could function right.
Answer for Question-1:
If read cannot read data it will 'block' till data arrives, this is called blocking mode read. Incase of a blocking mode read, the read call blocks till a data arrives. If you wish to change it to non-blocking mode, you can use fcntl functionality, if the same is supported.
For other queries, it is best that you read about it through man pages as a concise answer will be difficult.
Answer for Question-2:
When you create a thread detached, it means the created threads are not bound to the parent thread which creates it. So, the parent thread will just exit, if it completes it's work. If the parent happens to be the main thread, then when it exits the process also will exit, which will cause program not to run.

Resources