Difference in kqueue handling of fifos between Mac OS and FreeBSD? - c

I'm working on an application that uses fifos for IPC and uses an event-notification API (such as epoll or kqueue) to monitor the fifos for data to be read.
The application expects that if the writer for a fifo terminates that the reader will receive an event via the event notification API, allowing the reader to notice that the writer terminated.
I'm currently porting this application to macos and I'm running into some odd behavior with kqueue. I've been able to create a reproducer of this behavior:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
#include <sys/errno.h>
static int child() {
char child_fifo_path[64];
char parent_fifo_path[64];
printf("Child %d\n", getpid());
sprintf(child_fifo_path, "/tmp/child-%d", getpid());
sprintf(parent_fifo_path, "/tmp/parent-%d", getpid());
mkfifo(child_fifo_path, 0644);
mkfifo(parent_fifo_path, 0644);
int parent_fd = open(parent_fifo_path, O_RDONLY);
if (parent_fd == -1) {
perror("open");
return EXIT_FAILURE;
}
unsigned char parent_val;
read(parent_fd, &parent_val, 1);
printf("Received %hhx from parent\n", parent_val);
int child_fd = open(child_fifo_path, O_WRONLY);
if (child_fd == -1) {
perror("open");
return EXIT_FAILURE;
}
write(child_fd, &parent_val, 1);
printf("Wrote %hhx to parent\n", parent_val);
close(parent_fd);
close(child_fd);
return EXIT_SUCCESS;
}
static int parent(pid_t child_pid) {
char child_fifo_path[64];
char parent_fifo_path[64];
printf("Parent %d\n", getpid());
sprintf(child_fifo_path, "/tmp/child-%d", child_pid);
sprintf(parent_fifo_path, "/tmp/parent-%d", child_pid);
int result = -1;
while (result == -1) {
struct stat buf;
result = stat(child_fifo_path, &buf);
if (result == -1) {
if (errno != ENOENT) {
perror("open");
return EXIT_FAILURE;
}
}
}
unsigned char val = 20;
int parent_fd = open(parent_fifo_path, O_WRONLY);
if (parent_fd == -1) {
perror("open");
return EXIT_FAILURE;
}
write(parent_fd, &val, 1);
printf("Wrote %hhx to child\n", val);
int child_fd = open(child_fifo_path, O_RDONLY);
if (child_fd == -1) {
perror("open");
close(parent_fd);
return EXIT_FAILURE;
}
int kq = kqueue();
struct kevent event;
EV_SET(&event, child_fd, EVFILT_READ, EV_ADD, 0, 0, 0);
result = kevent(kq, &event, 1, NULL, 0, NULL);
if (result == -1) {
perror("kevent");
close(child_fd);
close(parent_fd);
return EXIT_FAILURE;
}
int done = 0;
while (!done) {
memset(&event, 0, sizeof(event));
printf("Waiting for events\n");
result = kevent(kq, NULL, 0, &event, 1, NULL);
if (result == -1) {
perror("kevent");
close(child_fd);
close(parent_fd);
return EXIT_FAILURE;
}
if (event.ident == child_fd) {
if (event.flags & EV_EOF) {
printf("Child exited\n");
done = 1;
}else if ( event.data > 0 ) {
unsigned char child_val;
result = read(child_fd, &child_val, 1);
if (result == -1) {
perror("read");
return EXIT_FAILURE;
}
printf("Received %hhx from child\n", child_val);
}
}
}
return EXIT_SUCCESS;
}
int main(int argc, char *argv[]) {
pid_t child_pid = fork();
if (child_pid == -1) {
perror("fork");
return EXIT_FAILURE;
}
if (child_pid) {
return parent(child_pid);
} else {
return child();
}
}
This reproducer forks a child process, which creates 2 fifos: /tmp/parent-$CHILD_PID and /tmp/child-$CHILD_PID. The parent waits until /tmp/parent-$CHILD_PID is created and then writes a byte to it. The child opens /tmp/parent-$CHILD_PID and blocks to read the byte written by the parent. Once complete, the child goes to write that same byte to the parent via /tmp/child-$CHILD_PID. The parent uses kqueue to observe the write to /tmp/child-$CHILD_PID.
This sequence of events works fine.
The issue occurs when the child closes its file referring to /tmp/child-$CHILD_PID. I'm seeing that this event is not reported to the parent via kqueue.
The most interesting part: this code works as I would expect on FreeBSD.
Version info:
Mac OS X: 10.11.6
FreeBSD 10.4-RELEASE-p3
Is there a difference between kqueue on macos and FreeBSD in this context? If so, is there some documentation that documents this difference?

This is not the best answer to your question but I hope can help you find other differences that may influence your code behavior when using kqueue between macOS and FreeBSD
In my case I use kqueue EVFILT_VNODE to check for changes, but based on the operating system I need to define different flags openModeDir when using the syscall.Open
For macOS (openmode_darwin.go) I use this:
openModeDir = syscall.O_EVTONLY | syscall.O_DIRECTORY
openModeFile = syscall.O_EVTONLY
And for FreeBSD (openmode.go) I use:
openModeDir = syscall.O_NONBLOCK | syscall.O_RDONLY | syscall.O_DIRECTORY
openModeFile = syscall.O_NONBLOCK | syscall.O_RDONLY
From macOS docs open(2), this is the flag description:
O_EVTONLY descriptor requested for event notifications only
And from FreeBSD open(2), there is no O_EVTONLY.
Putting all together this is how I call kqueue:
...
watchfd, err := syscall.Open(dir, openModeDir, 0700)
if err != nil {
return err
}
kq, err := syscall.Kqueue()
if err != nil {
syscall.Close(watchfd)
return err
}
ev1 := syscall.Kevent_t{
Ident: uint64(watchfd),
Filter: syscall.EVFILT_VNODE,
Flags: syscall.EV_ADD | syscall.EV_ENABLE | syscall.EV_CLEAR,
Fflags: syscall.NOTE_WRITE | syscall.NOTE_ATTRIB,
Data: 0,
}
...
I am using go, but as mentioned before hope can give you an idea while dealing with Kqueue, In my case this simple change of flags made a difference.

Related

Named pipe, read fail with bad file descriptor

I'm beginning with pipes under Linux and I have a problem with my code.
I wanted to test sending an integer through the fifo so I code a small program to test it.
First I open the read only descriptor and then the write only one as precised in the documentation.
I send the number to the pipe and close the write descriptor.
However, when I'm trying to read from the pipe, it says that I have a bad file descriptor, I don't understand why it's not working because as the descriptor is the good one and is oppened with good option (O_RDONLY | O_NONBLOCK).
Here is my code :
void main(void)
{
int modePipeWrite, modePipeRead;
if(mkfifo("test.fifo", 0777) == -1)
{
perror("mkfifo");
exit(EXIT_FAILURE);
}
if(modePipeRead = open("test.fifo", O_RDONLY | O_NONBLOCK) == -1)
{
perror("openRead");
exit(EXIT_FAILURE);
}
if(modePipeWrite = open("test.fifo", O_WRONLY | O_NONBLOCK) == -1)
{
perror("openWrite");
exit(EXIT_FAILURE);
}
int n = 0;
if(write(modePipeWrite, &n, sizeof(n)) == -1)
{
perror("write");
}
printf("Send value: %d\n", n);
close(modePipeWrite);
int mode;
while(1)
{
if(read(modePipeRead, &mode, sizeof(mode)) == -1)
{
perror("read");
}
printf("Received value: %d\n", getpid(), mode);
sleep(1);
}
}
And the output :
./a.out
Send value: 0
read: Bad file descriptor
Received value: -1877110288
read: Bad file descriptor
Received value: -1877110288
read: Bad file descriptor
Received value: -1877110288
I don't understand what could be wrong here. If someone have some advices I would be glad to hear it.
Your code has a couple of issues, which you would've found if you compiled it with all warnings enabled. If you use gcc, the following options are nice/mandatory:
-Wall -Wextra -Werror -pedantic
One issue is that you don't assign the return value of open() to the file descriptors. You need to add braces around the assignment, or move it out of the if-statements.
Here's a working example, using a thread to read:
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <pthread.h>
static void *reader(void *arg)
{
int fd, val;
if ((fd = open("test.fifo", O_RDONLY)) == -1) {
perror("openRead");
exit(EXIT_FAILURE);
}
if (read(fd, &val, sizeof val) == -1)
perror("read");
else
printf("Received value: %d\n", val);
close(fd);
return NULL;
}
int main(void)
{
int modePipeWrite, modePipeRead;
pthread_t readerid;
if (mkfifo("test.fifo", 0777) == -1) {
perror("mkfifo");
exit(EXIT_FAILURE);
}
pthread_create(&readerid, NULL, reader, NULL);
sleep(1);
if ((modePipeWrite = open("test.fifo", O_WRONLY)) == -1) {
perror("openWrite");
exit(EXIT_FAILURE);
}
int n = 1234;
if (write(modePipeWrite, &n, sizeof n) == -1)
perror("write");
else
printf("Sent value: %d\n", n);
sleep(1);
close(modePipeWrite);
return 0;
}

Program with mkfifo() and open() cannot exit

I am trying to use FIFO for interprocessing. But when trying to create a FIFO and then open it, my program hangs (cannot exit).
if (mkfifo("./fifo.txt", S_IRUSR | S_IWUSE) < 0) {
fprint("Can not create fifo");
return 1;
}
if ((readfd = open("./fifo.txt", O_RDONLY)) < 0) {
return 1;
}
What am I doing wrong here?
Thank you very much.
Read fifo(7), notably:
Normally, opening the FIFO blocks until the other end is opened also.
So I guess that your call to open(2) is blocked. Perhaps you want to pass the O_NONBLOCK flag.
You should use strace(1) to debug your program (and perhaps also strace the other program on the other end of the fifo). And call perror(3) on error.
Perhaps using unix(7) sockets could be more relevant in your case. You can then poll(2) before accept(2)
You should read Advanced Linux Programming.
Here is an example code:
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void child(void)
{
int fd = 0;
if ((fd = open("./fifo.txt", O_WRONLY)) < 0) {
return;
}
write(fd, "hello world!", 12);
}
void parent(void)
{
int fd = 0;
if ((fd = open("./fifo.txt", O_RDONLY)) < 0) {
return;
}
char buf[36] = {0};
read(fd, buf, 36);
printf("%s\n", buf);
}
int main(void)
{
pid_t pid = 0;
if (mkfifo("./fifo.txt", S_IRUSR | S_IWUSR) < 0) {
printf("Can not create fifo\n");
return 1;
}
pid = fork();
if (pid == 0) {
printf("child process\n");
child();
} else if (pid < 0) {
printf("fork error\n");
return -1;
}
parent();
}

Communication with child process using pipes and epoll

I'm writing an application which will start some processes (fork and exec) depending on users input and should inform user about every error in those processes (Print some internal ID + message written to stderr by the process). I'd also like to detect exiting processes. I'm facing the problem, that I cannot receive data after executing execl() command. Test data is received in epoll_wait loop, but process called by exec seems to not write anything to stderr nor even end (I don't know if I'm correct, but I expect pipe to be readable after it's writing end has been closed, when process exited). Some code reproducing my problem:
#include <unistd.h>
#include <sys/epoll.h>
#include <stdio.h>
#include <thread>
#include <stdlib.h>
#include <sys/wait.h>
thread_local int epoll;
int do_fork()
{
int pip[2];
pipe(pip);
int pid = fork();
if (pid == 0)
{
dup2(pip[1], STDERR_FILENO);
close(pip[0]);
close(pip[1]);
write(STDERR_FILENO, "test data", 10);
sleep(3);
//exit(1);
execl("tr", "tr", NULL); //this process will end immediately and write error on stderr
}
else
{
printf("PID %d\n", pid);
close(pip[1]);
epoll_event ev;
ev.data.fd = pip[0];
ev.events = EPOLLIN | EPOLLRDHUP | EPOLLHUP;
printf("pipe %d\n", pip[0]);
epoll_ctl(epoll, EPOLL_CTL_ADD, pip[0], &ev);
}
}
int thread()
{
epoll_event events[10];
epoll = epoll_create1(0);
epoll_event ev;
ev.data.fd = STDIN_FILENO;
ev.events = EPOLLIN;
epoll_ctl(epoll, EPOLL_CTL_ADD, STDIN_FILENO, &ev);
char buf[1000];
while(true)
{
int r = epoll_wait(epoll, events, 10, -1);
printf("r = %d\n", r);
for (int i = 0; i < r; ++i)
{
if(events[i].data.fd == STDIN_FILENO)
{
int t = read(events[i].data.fd, buf, 1000);
do_fork();
}
else
{
printf("event? %d\n", events[i].events);
printf("pipe %d\n", events[i].data.fd);
int t = read(events[i].data.fd, buf, 1000);
printf("t == %d\n", t);
if(t == -1)
printf("errno: %d\n", errno);
if(events[i].events & EPOLLHUP || t == 0)
{
epoll_ctl(epoll, EPOLL_CTL_DEL, events[i].data.fd, NULL);
if(close(events[i].data.fd) == -1)
printf("cannot close fd\n");
}
}
}
}
return 0;
}
int main()
{
std::thread t{thread};
t.detach();
while(true);
}

Linux inter-processes communication

I have to implement a testing program(quiz), which besides displaying the question and reading the answer, it has to display the time left at each one minute past. After finishing the examination time, by finishing the questions or by running out of time,the program has to get back from the beginning, when before the start, we enter the name of the candidate. This implementation has to be done using processes. Below is the code that i have written so far. The problem is that i am not sure that i am making a good communication between the process and the subprocesses, especially because i am not using a pipe. Some opinions?
#include<stdio.h>
#include<sys/types.h>
#include<signal.h>
#include<unistd.h>
#include<sys/wait.h>
#define T 180
void firstChildAction(){
static const char filename[] = "/home/osystems01/laura/text";
char question[100];
char answer[100];
FILE *file = fopen(filename,"r");
if(file != NULL){
while(fgets(question,sizeof question,file) != NULL){
fputs(question, stdout);
scanf("%s",&answer);
}
fclose(file);
}
else{
perror(filename);
}
}
void secondChildAction(){
int i;
for(i = T; i >= 0; i-=60){
if( i/60 != 0){
printf("You have %d %s left.\n", i/60,(i/60 > 1)?"minutes":"minute");
sleep(60);
}
else{
printf("The time is over\n");
break;
}
}
}
int main() {
pid_t pidA;
pid_t pidB;
pid_t wPid;
char name[20];
while(1){
printf("Enter the candidate name or Quit to exit: \n");
scanf("%s",&name);
if(strcmp(name,"Quit") == 0 || strcmp(name,"quit") == 0){
printf("The program is terminating.....\n");
break;
}
else{
pidA = fork();
if(pidA == 0){
firstChildAction();
exit(0);
}
else{
pidB = fork();
if(pidB == 0){
secondChildAction();
exit(0);
}
}
int status;
while(wPid = wait(&status)) > 0 ){
if(WIFEXITED(status)){
int result = WEXITSTATUS(status);
printf("Exit status of %d is %d\n", wPid, result);
if(wPid == pidA){
kill(pidB,SIGTERM);
kill(pidA,SIGTERM);
}
else if(wPid == pidB){
kill(pidA,SIGTERM);
kill(pidB,SIGTERM);
}
}
}
}
}
return 0;
}
Pipes as such don't require you to provide a regular file, but they can have a unique, globally visible name, which is provided by a (unused) filename you have to specify. The contents of the file, if any, is handled by the library.
There are (simple) pipes for communication among related processes (such as a child and a parent process in the same process hierarchy) where the pipe handle can easily be passed to other processes.
The other flavor is called 'named pipes' for processes with any relation, where one can lookup the pipe handle using the global name (as explained in the answer of the question I linked). You can think of a pipe as of a directly connected speaking tube, allowing two processes to chitchat about whatever they like, using read and write functions. On Linux, a pipe is a simplex (at a time, one talks, the other one listens). One would nee two pipes for bidirectional async IO in this case (https://unix.stackexchange.com/questions/53641/how-to-make-bidirectional-pipe-between-two-programs). The immediate buffer for input and output is abstracted. Its just like with network sockets.
I'd suggest to compile this nice example in the accepted answer to play around with: https://stackoverflow.com/a/2789967/1175253
Edit
Example code with error handling. Treat pipe.h & pipe.c as a library (link NamedPipeReader and NamedPipeWriter against it).
This code would need further testing, however, the code is able to (re)open named pipes in any order.
pipe.h
#ifndef PIPE_H_
#define PIPE_H_
//C headers
#include <errno.h>
#include <assert.h>
//Linux headers
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#ifdef __cplusplus
extern "C"
{
#endif
int open_named_pipe(const char* const name, const int permissions, const int mode, int* pipe_created);
#ifdef __cplusplus
}
#endif
#endif /* PIPE_H_ */
pipe.c
#include "pipe.h"
#include <stdio.h>
int open_named_pipe(const char* const name, const int permissions, const int mode, int* pipe_created)
{
int fd;
assert(name);
assert(permissions);
assert(pipe_created);
//Create or use an existing pipe special file
if (0 == mkfifo(name, permissions))
{
*pipe_created = 1;
printf("Successfully created named pipe '%s'\n", name);
}
else
{
switch (errno)
{
case EEXIST:
//this is OK, as the other process might already has created the special file
printf("Opened existing named pipe '%s'\n", name);
break;
default:
fprintf(stderr, "Failed to create or access named pipe '%s'\n", name);
perror(" ");
return -1;
};
}
fd = open(name, mode);
if (fd < 0)
{
perror("Could not open pipe for writing");
if (*pipe_created)
{
if (0 == unlink(name))
{
*pipe_created = 0;
}
else
{
perror("Failed to unlink named pipe");
}
}
}
return fd;
}
NamedPipeReader.c
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include "pipe.h"
//Globals
const char* const pipe_name = "/tmp/myfifo";
const int pipe_permissions = 0600;
const size_t read_buffer_size = 1024; //[bytes]
const size_t read_retry_delay = 25000; //[us]
int fd = -1;
int pipe_created = 0;
char* read_buffer = NULL;
//Handles EPIPE signal
void signal_handler(int signal)
{
fprintf(stderr, "cought signal %d\n", signal);
}
//Handles cleanup on exit
void exit_handler(void)
{
if (read_buffer)
free(read_buffer);
if (fd >= 0)
close(fd);
//if this process created the FIFO, we unlink it
if (pipe_created == 0)
unlink(pipe_name);
}
int main()
{
//Locals
int run = 1;
int received = 0;
//Install the exit handler
atexit(&exit_handler);
signal(EPIPE, signal_handler);
signal(EACCES, signal_handler);
//Allocate the buffer
read_buffer = (char*) malloc(read_buffer_size);
if (!read_buffer)
{
perror("Failed to allocate buffer");
return EXIT_FAILURE;
}
restart: ;
//Close if already open
if(fd >= 0)
close(fd);
//Create or use an existing pipe special file
fd = open_named_pipe(pipe_name, pipe_permissions, O_RDONLY, &pipe_created);
if (fd < 0)
{
return EXIT_FAILURE;
}
while (run)
{
assert(fd >= 0);
assert(read_buffer_size > 1);
received = read(fd, read_buffer, read_buffer_size - 1);
if (received > 0)
{
//add a NUL char for string termination
read_buffer[received] = '0';
printf("local process %llu received: %s\n", (unsigned long long) getpid(), read_buffer);
}
else if (received == 0)
{
//EOF reached, this happens in case the writer has closed its handle.
//Perform a delayed restart and recreate the named pipe
usleep(read_retry_delay);
printf("Restarting...\n");
goto restart;
}
else
{
switch (errno)
{
case EAGAIN:
//Wait, if the pipe is empty,
//happens when opened with the O_NONBLOCK flag
usleep(read_retry_delay);
break;
case EPIPE:
case EBADF:
case EBADFD:
perror("Pipe error");
printf("Restarting...\n");
goto restart;
default:
perror("Pipe error");
return EXIT_FAILURE;
};
}
}
return EXIT_SUCCESS;
}
NamedPipeWriter.c
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include "pipe.h"
//Globals
const char* const pipe_name = "/tmp/myfifo";
const int pipe_permissions = 0600;
const size_t write_buffer_size = 1024; //[bytes]
const size_t write_retry_delay = 25000; //[us]
const size_t write_interval = 1000000;
int fd = -1;
int pipe_created = 0;
char* write_buffer = NULL;
//Handles EPIPE signal
void signal_handler(int signal)
{
fprintf(stderr, "cought signal %d\n", signal);
}
//Handles cleanup on exit
void exit_handler(void)
{
if (write_buffer)
free(write_buffer);
if (fd >= 0)
close(fd);
//if this process created the FIFO, we unlink it
if (pipe_created == 0)
unlink(pipe_name);
}
//Main Function
int main()
{
//Locals
int run = 1;
int sent = 0;
int msg_len = 0;
//Install the exit handler
atexit(&exit_handler);
signal(EPIPE, signal_handler);
signal(EACCES, signal_handler);
//Allocate the buffer
write_buffer = (char*) malloc(write_buffer_size);
if (!write_buffer)
{
perror("Failed to allocate buffer");
return EXIT_FAILURE;
}
restart: ;
//Close if already open
if(fd >= 0)
close(fd);
//Create or use an existing pipe special file
fd = open_named_pipe(pipe_name, pipe_permissions, O_WRONLY, &pipe_created);
if (fd < 0)
{
return EXIT_FAILURE;
}
while (run)
{
//Print message into the buffer
msg_len = snprintf(write_buffer, write_buffer_size, "Greetings from process %llu\n", (unsigned long long) getpid());
{
char* msg_ptr = write_buffer;
char* msg_end = write_buffer + msg_len;
while (msg_ptr != msg_end)
{
assert(fd >= 0);
assert(msg_ptr < msg_end);
sent = write(fd, msg_ptr, msg_end - msg_ptr);
if (sent > 0)
{
msg_ptr += sent;
}
else if (sent == 0)
{
//retry delay for nonblocking writes
usleep(write_retry_delay);
}
else
{
switch (errno)
{
case EAGAIN:
//Wait, if the pipe is full,
//happens when opened with the O_NONBLOCK flag
usleep(write_retry_delay);
break;
case EPIPE:
case EBADF:
case EBADFD:
perror("Pipe error");
printf("Restarting...\n");
goto restart;
default:
perror("Pipe error");
return EXIT_FAILURE;
};
}
}
printf("Written: %s\n", write_buffer);
usleep(write_interval);
}
}
return EXIT_SUCCESS;
}

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