I use a device on serial port /dev/ttyUSB0 (uses FTDI) and I don't want to leak any file descriptors to other spawned processes so I set the close-on-exec flag on the descriptor. Could you tell me what is the difference between setting O_CLOEXEC while opening:
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
int main()
{
int fd, rc;
fd = open("/dev/ttyUSB1", O_RDWR | O_NOCTTY | O_CLOEXEC);
if(fd < 0)
{
perror("error open:");
exit(-1);
}
rc = close(fd);
if(rc != 0)
{
perror("error close:");
exit(-1);
}
return 0;
}
And setting close-on-exec with ioctl(fd, TIOCEXCL):
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <stdio.h>
int main()
{
int fd, rc;
fd = open("/dev/ttyUSB1", O_RDWR | O_NOCTTY);
if(fd < 0)
{
perror("error open:");
exit(-1);
}
rc = ioctl(fd, TIOCEXCL);
if(rc != 0)
{
perror("error ioctl:");
exit(-1);
}
rc = close(fd);
if(rc != 0)
{
perror("error close:");
exit(-1);
}
return 0;
}
The TIOCEXCL does not set the close-on-exec flag (that would be FIOCLEX, or, equivalently, fcntl(fd, F_SETFD, FD_CLOEXEC)).
To answer the question you thought you were asking:
Specifying O_CLOEXEC when you open() a file will set the close-on-exec flag before it returns, saving you another call and, importantly, ensuring that there is no race condition where another thread might call exec() after open() but before the subsequent fcntl().
If you really need to set or unset the flag at any other time (I've never needed to), you can do so with fcntl F_SETFD, passing FD_CLOEXEC or 0 respectively.
Related
Can one do non-blocking I/O on a pipe? fcntl fails to set O_NONBLOCK. Page 918 of The Linux Programming Interface includes a table 'Semantics of reading n bytes from pipe or FIFO (p)'. This table lists the behaviour of pipes and FIFO's with one column titled O_NONBLOCK enabled? This would imply that you can set the O_NONBLOCK flag on a pipe. Is this correct? The following code fails to set the flag, fcntl(2) does not report an error though.
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
int main()
{
int fds[2];
pid_t pid;
char wr_buf[100];
char rd_buf[100];
pipe(fds);
pid = fork();
if ( pid )
{
while (1 )
{
memcpy( wr_buf, "abcdefghi\0",10);
write( fds[1], wr_buf, 10);
sleep(2);
}
}
else
{
int retval = fcntl( fds[0], F_SETFL, fcntl(fds[0], F_GETFL) | O_NONBLOCK);
printf("Ret from fcntl: %d\n", retval);
while (1)
{
ssize_t r=read( fds[0], rd_buf, 10 );
printf("read: %d\n", r);
if ( r > 0 )
{
printf("Buffer: %s\n", rd_buf);
}
else
{
printf("Read nothing\n");
perror("Error was");
sleep(1);
}
}
}
}
There is nothing special to pipe and O_NONBLOCK. The following example work as expected. I did not check every retval from every call to make the example a bit more readable. A real world application must do the checks.
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
int main()
{
int fds[2];
pid_t pid;
char buf[100];
pipe(fds);
pid = fork();
if ( pid )
{
while (1 )
{
memcpy( buf, "abcdefghi\0",10);
write( fds[1], buf, 10);
sleep(2);
}
}
else
{
int retval = fcntl( fds[0], F_SETFL, fcntl(fds[0], F_GETFL) | O_NONBLOCK);
printf("Ret from fcntl: %d\n", retval);
while (1)
{
ssize_t r=read( fds[0], buf, 10 );
printf("read: %d\n", r);
if ( r > 0 )
{
printf("Buffer: %s\n", buf);
}
else
{
printf("Read nothing\n");
perror("Error was");
sleep(1);
}
}
}
}
After writing my example I inspect your code and found:
flags = fcntl(pfd[0], F_GETFD);
flags |= O_NONBLOCK;
if (fcntl(pfd[0], F_SETFD, flags))
Please change F_SETFD to F_SETFL and also for the get operation. You would not change the file descriptor flags but the file status flags :-)
From man 3 fcntl:
File descriptor flags
The following commands manipulate the flags associated with a file
descriptor. Currently, only one such flag is defined: FD_CLOEXEC, the
close-on-exec flag. If the FD_CLOEXEC bit is 0, the file descriptor
will remain open across an execve(2), otherwise it will be closed.
File status flags
Each open file description has certain associated status flags, ini‐
tialized by open(2) and possibly modified by fcntl(). Duplicated file
descriptors (made with dup(2), fcntl(F_DUPFD), fork(2), etc.) refer to
the same open file description, and thus share the same file status
flags.
F_SETFL (int)
Set the file status flags to the value specified by arg. File
access mode (O_RDONLY, O_WRONLY, O_RDWR) and file creation flags
(i.e., O_CREAT, O_EXCL, O_NOCTTY, O_TRUNC) in arg are ignored.
On Linux this command can change only the O_APPEND, O_ASYNC,
O_DIRECT, O_NOATIME, and O_NONBLOCK flags. It is not possible
to change the O_DSYNC and O_SYNC flags; see BUGS, below.
I write a test for FIFO. Server writes string "hello" to the client through FIFO. But it seems that the two processes are blocked.I think the FIFO are opened for writing and reading by server and client. But the two processes output nothing.
/* FIFO test */
#include <stdio.h>
#include <sys/types.h>
#include <sys.stat.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#define FIFOPATH "/home/hel/fifo" // define file path
int client(void);
int server(void);
int main(void)
{
pid_t pid;
/* create FIFO */
if (mkfifo(FIFOPATH, S_IRUSR | S_IWUSR) < 0) {
if (errno == EEXIST) { // already exists, ok
}
/* error */
else {
exit(-1);
}
}
/* create process */
pid = fork();
if (pid < 0) { // error, process exits.
exit(-1);
} else if (pid == 0) { // child, server
server();
return 0; // exit
}
/* parent, client */
client();
return 0;
}
/* server */
int server(void)
{
int ret;
int fd;
/* open fifo for writing */
if ((fd = open(FIFOPATH, 0200)) < 0) {
printf("%s\n", strerror(errno));
return -1; // error
}
ret = write(fd, "hello", 5);
close(fd);
return 0;
}
/* client */
int client(void)
{
char recvBuf[100];
int fd;
int ret;
/* open fifo for reading */
if ((fd = open(FIFOPATH, 0400)) < 0) {
printf("%s\n", strerror(errno));
return -1; // error
}
ret = read(fd, recvBuf, 5);
printf("ret: %d %d\n", ret, fd);
printf("client receive %s\n", recvBuf);
close(fd);
return 0;
}
Your code has two problems. The first one is the main problem.
The flags parameters passed to open are incorrect. They are not supposed to be unix file permission flags as it appears you have provided. The server should use O_WRONLY and the client should use O_RDONLY.
write(fd, "hello", 5); and read(fd, recvBuf, 5); are not writing and reading the terminating NUL character of the string. But then it is printed as a string: printf("client receive %s\n", recvBuf);. This invokes Undefined Behaviour (even though there is a good chance the program may appear to "work"). Change 5 to 6.
open() uses following flags:-
O_RDONLY open for reading only
O_WRONLY open for writing only
O_RDWR open for reading and writing
O_NONBLOCK do not block on open or for data to become available
O_APPEND append on each write
O_CREAT create file if it does not exist
O_TRUNC truncate size to 0
O_EXCL error if O_CREAT and the file exists
O_SHLOCK atomically obtain a shared lock
O_EXLOCK atomically obtain an exclusive lock
O_NOFOLLOW do not follow symlinks
O_SYMLINK allow open of symlinks
O_EVTONLY descriptor requested for event notifications only
O_CLOEXEC mark as close-on-exec
for FIFO you must use O_RDONLY in client and O_WRONLY in server in your program.
0200 and 0400 permissions are not working for open(). you can check the the flag value in as
#define O_RDONLY 0x0000 / open for reading only */
#define O_WRONLY 0x0001 / open for writing only */
thats why open blocks in your case as it doesn't get correct flag.
Can one do non-blocking I/O on a pipe? fcntl fails to set O_NONBLOCK. Page 918 of The Linux Programming Interface includes a table 'Semantics of reading n bytes from pipe or FIFO (p)'. This table lists the behaviour of pipes and FIFO's with one column titled O_NONBLOCK enabled? This would imply that you can set the O_NONBLOCK flag on a pipe. Is this correct? The following code fails to set the flag, fcntl(2) does not report an error though.
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
int main()
{
int fds[2];
pid_t pid;
char wr_buf[100];
char rd_buf[100];
pipe(fds);
pid = fork();
if ( pid )
{
while (1 )
{
memcpy( wr_buf, "abcdefghi\0",10);
write( fds[1], wr_buf, 10);
sleep(2);
}
}
else
{
int retval = fcntl( fds[0], F_SETFL, fcntl(fds[0], F_GETFL) | O_NONBLOCK);
printf("Ret from fcntl: %d\n", retval);
while (1)
{
ssize_t r=read( fds[0], rd_buf, 10 );
printf("read: %d\n", r);
if ( r > 0 )
{
printf("Buffer: %s\n", rd_buf);
}
else
{
printf("Read nothing\n");
perror("Error was");
sleep(1);
}
}
}
}
There is nothing special to pipe and O_NONBLOCK. The following example work as expected. I did not check every retval from every call to make the example a bit more readable. A real world application must do the checks.
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
int main()
{
int fds[2];
pid_t pid;
char buf[100];
pipe(fds);
pid = fork();
if ( pid )
{
while (1 )
{
memcpy( buf, "abcdefghi\0",10);
write( fds[1], buf, 10);
sleep(2);
}
}
else
{
int retval = fcntl( fds[0], F_SETFL, fcntl(fds[0], F_GETFL) | O_NONBLOCK);
printf("Ret from fcntl: %d\n", retval);
while (1)
{
ssize_t r=read( fds[0], buf, 10 );
printf("read: %d\n", r);
if ( r > 0 )
{
printf("Buffer: %s\n", buf);
}
else
{
printf("Read nothing\n");
perror("Error was");
sleep(1);
}
}
}
}
After writing my example I inspect your code and found:
flags = fcntl(pfd[0], F_GETFD);
flags |= O_NONBLOCK;
if (fcntl(pfd[0], F_SETFD, flags))
Please change F_SETFD to F_SETFL and also for the get operation. You would not change the file descriptor flags but the file status flags :-)
From man 3 fcntl:
File descriptor flags
The following commands manipulate the flags associated with a file
descriptor. Currently, only one such flag is defined: FD_CLOEXEC, the
close-on-exec flag. If the FD_CLOEXEC bit is 0, the file descriptor
will remain open across an execve(2), otherwise it will be closed.
File status flags
Each open file description has certain associated status flags, ini‐
tialized by open(2) and possibly modified by fcntl(). Duplicated file
descriptors (made with dup(2), fcntl(F_DUPFD), fork(2), etc.) refer to
the same open file description, and thus share the same file status
flags.
F_SETFL (int)
Set the file status flags to the value specified by arg. File
access mode (O_RDONLY, O_WRONLY, O_RDWR) and file creation flags
(i.e., O_CREAT, O_EXCL, O_NOCTTY, O_TRUNC) in arg are ignored.
On Linux this command can change only the O_APPEND, O_ASYNC,
O_DIRECT, O_NOATIME, and O_NONBLOCK flags. It is not possible
to change the O_DSYNC and O_SYNC flags; see BUGS, below.
I am trying to share a socket descriptor with another local process on the same Linux machine. These processes are "unrelated" i.e., they are not parent/child related and are not forked. They are independent. Ultimately, the flow I want is something like:
| [Process1]
| -> master_socket = socket()
| -> setsockopt(master_socket...)
| -> fcntl(master_socket...)
| -> bind(master_socket...)
| -> listen(master_socket...)
| -> Share master_socket with Process2
|
| [Process2]
| -> Receive and store master_socket
| -> Use select() on master_socket
| -> Use accept() on master_socket to receive connections...
Based on a few related threads, it seems as though this is possible using a Unix domain socket which will track that the socket handle was sent from Process1 to Process2 in the kernel, giving it permission (e.g., here, here, and here).
What I am trying to determine is whether or not the descriptor is possible to share over a POSIX message queue. Oddly enough, if I create the socket BEFORE opening the queue, it seems to work OK. However, if I create the socket AFTER opening the queue, the descriptor read on Process2 comes up as "invalid."
Example Programs
Here is a sample program that sends a socket descriptor via a message queue. If init_socket() is called before open_queue(), then the descriptor received is valid. If visa-versa then it comes across invalid.
send.c: gcc -o send send.c -lrt
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <syslog.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/fcntl.h>
#include <string.h>
#include <errno.h>
#include <mqueue.h>
int init_socket();
int open_queue();
mqd_t msgq_id;
int main() {
int socket;
char msg_str[16];
// HERE: ordering matters. If open_queue() is done before init_socket(), then
// the descriptor is received invalid. If init_socket() is called BEFORE open_queue()
// then the descriptor received is valid.
open_queue();
socket = init_socket();
// Put the socket on the queue
memset(msg_str, '\0', sizeof(msg_str));
snprintf(msg_str, sizeof(msg_str), "%d", socket);
if(mq_send(msgq_id, msg_str, strlen(msg_str)+1, 1) == -1) {
printf("Unable to send the message on the queue: %s\n", strerror(errno));
return -1;
}
}
int open_queue() {
// Create a queue to share the socket
if(msgq_id = mq_open("/share_socket", O_RDWR | O_CREAT | O_EXCL, S_IRWXU | S_IRWXG, NULL)==-1) {
if(errno != EEXIST) {
printf("Failed to create IPC queue: %s\n", strerror(errno));
return -1;
}
// Re-open the already existing queue.
if((msgq_id = mq_open("/share_socket", O_RDWR)) != -1) {
printf("Re-opened the IPC queue: %s\n", "/share_socket");
} else {
printf("Failed to re-open IPC queue %s: %s\n", "/share_socket", strerror(errno));
return -1;
}
}
return 1;
}
int init_socket() {
int master_socket;
int opt=1;
struct sockaddr_in loc_addr = { 0 };
// Create the high level master socket
if( (master_socket = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
printf("Unable to create master_socket\n");
return EXIT_FAILURE;
}
// Set socket to accept multiple connections
if( setsockopt(master_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) < 0 ) {
printf("Error setting socket to accept multiple connections\n");
return EXIT_FAILURE;
}
// Set the socket type
bzero(&loc_addr, sizeof(struct sockaddr_in));
loc_addr.sin_family = AF_INET;
loc_addr.sin_addr.s_addr = htonl(INADDR_ANY);
loc_addr.sin_port=htons(1200);
// Set the socket to nonblocking
if (fcntl(master_socket, F_SETFL, O_NDELAY) < 0) {
printf("Can't set socket to non-blocking\n");
return EXIT_FAILURE;
}
// Bind to the socket
if (bind(master_socket, (struct sockaddr *)&loc_addr, sizeof(loc_addr)) < 0) {
return EXIT_FAILURE;
}
// Now, listen for a maximum of 6 pending clients
if(listen(master_socket, 6) < 0) {
printf("Could not set the socket to listen\n");
close(master_socket);
return EXIT_FAILURE;
}
return master_socket;
}
read.c: gcc -o read read.c -lrt
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <syslog.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/fcntl.h>
#include <string.h>
#include <errno.h>
#include <mqueue.h>
#define MAX_MSG_LEN 10000
int main() {
mqd_t msgq_id;
int master_socket;
unsigned int sender;
int bytes;
char msgcontent[MAX_MSG_LEN];
// Re-open the already existing queue.
if((msgq_id = mq_open("/share_socket", O_RDWR)) != -1) {
printf("Re-opened the IPC queue: %s\n", "/share_socket");
} else {
printf("Failed to re-open IPC queue %s: %s\n", "/share_socket", strerror(errno));
return -1;
}
// Try to read from the queue.
if((bytes = mq_receive(msgq_id, msgcontent, MAX_MSG_LEN, &sender)) == -1)
{
// If the failure was due to there being no messages, just return 0.
if(errno==EAGAIN)
return 0;
printf("Unable to read from the queue\n");
return -1;
}
sscanf(msgcontent, "%d", &master_socket);
printf("Got master socket value: %d\n", master_socket);
if(master_socket != 0 && (fcntl(master_socket, F_GETFD) != -1 || errno != EBADF))
printf("... socket is valid\n");
else
printf("... SOCKET IS INVALID\n");
return 1;
}
How to run: Just go ahead and run ./read and it will wait on the message queue and then run ./send. If you compiled send.c with init_socket() before open_queue(), you will see:
$ ./read
Re-opened the IPC queue: /share_socket
Got master socket value: 3
... socket is valid
Otherwise:
$ ./read
Re-opened the IPC queue: /share_socket
Got master socket value: 4
... SOCKET IS INVALID
What would cause this behavior where the ordering is important?
Problem One:
Both programs will start with file descriptors 0, 1, & 2 open and valid.
When you run send with init_socket first the returned file descriptor for the socket witl be 3. Then you open the queue which will be 4. You send 3 to read process.
In read you open the queue which will be file descriptor 3. You read the queue and find you were sent a 3, which is actually the fd of the queue. So you are "testing" the queue file descriptor.
Conversely when in send you open the queue first and then the socket you are sending the file descriptor 4 to read. There is no file descriptor 4 open in read (the queue will still be opened as 3) so it naturally fails the "test".
Problem 2:
The test is wrong.
if (master_socket != 0 && (fcntl(master_socket, F_GETFD) != -1 || errno != EBADF))
printf("... socket is valid\n");
else
printf("... SOCKET IS INVALID\n");
All this is telling you is that it isn't file descriptor 0 and you can read the file descriptor flags. A test that the message queue file descriptor will pass.
A better test is something like this:
void isSocket(int fd)
{
struct stat statbuf;
if (fstat(fd, &statbuf) == -1)
{
perror("fstat");
exit(1);
}
if (S_ISSOCK(statbuf.st_mode))
printf("%d is a socket\n", fd);
else
printf("%d is NOT a socket\n", fd);
}
I haven't tested your pgms but if you print out all your file descriptors from both programs as you open them and try the above test it should bare out what I am saying.
I need to some little help to understand how to use condition variables in C to resolve an exercise. Here is a little example:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#define OKTOWRITE "/oktowrite"
#define MESSAGE "/message"
#define MUTEX "/lock"
int main(int argc, char** argv)
{
pthread_cond_t* condition;
pthread_mutex_t *mutex;
char* message;
int des_cond, des_msg, des_mutex;
int mode = S_IRWXU | S_IRWXG;
des_mutex = shm_open(MUTEX, O_CREAT | O_RDWR | O_TRUNC, mode);
if (des_mutex < 0)
{
perror("failure on shm_open on des_mutex");
exit(1);
}
if (ftruncate(des_mutex, sizeof(pthread_mutex_t)) == -1)
{
perror("Error on ftruncate to sizeof pthread_cond_t\n");
exit(-1);
}
mutex = (pthread_mutex_t*) mmap(NULL, sizeof(pthread_mutex_t),
PROT_READ | PROT_WRITE, MAP_SHARED, des_mutex, 0);
if (mutex == MAP_FAILED )
{
perror("Error on mmap on mutex\n");
exit(1);
}
pthread_mutex_init(mutex, NULL );
des_cond = shm_open(OKTOWRITE, O_CREAT | O_RDWR | O_TRUNC, mode);
if (des_cond < 0)
{
perror("failure on shm_open on des_cond");
exit(1);
}
if (ftruncate(des_cond, sizeof(pthread_cond_t)) == -1)
{
perror("Error on ftruncate to sizeof pthread_cond_t\n");
exit(-1);
}
condition = (pthread_cond_t*) mmap(NULL, sizeof(pthread_cond_t),
PROT_READ | PROT_WRITE, MAP_SHARED, des_cond, 0);
if (condition == MAP_FAILED )
{
perror("Error on mmap on condition\n");
exit(1);
}
pthread_cond_init(condition, NULL );
if (!fork())
{
sleep(3);
pthread_mutex_lock(mutex);
pthread_cond_signal(condition);
pthread_mutex_unlock(mutex);
printf("son signaled\n");
exit(0);
}
else
{
printf("wait on condition\n");
pthread_mutex_lock(mutex);
pthread_cond_wait(condition, mutex);
pthread_mutex_unlock(mutex);
printf("Signaled by son process, wake up\n");
pthread_mutex_destroy(mutex);
pthread_cond_destroy(condition);
shm_unlink(OKTOWRITE);
shm_unlink(MESSAGE);
shm_unlink(MUTEX);
return 0;
}
}
The problem is that the father of the process keeps on being locked, even after son's signaling. Everything is in shared memory (using shm_open and mmap) so the condition should be the same for both the processes.
Am I maybe making a mistake by locking the mutex before calling wait or signal?
EDIT:
Thanks to all who helped me. Here's the right code with the CRITICAL parts marked:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#define OKTOWRITE "/condwrite"
#define MESSAGE "/msg"
#define MUTEX "/mutex_lock"
int main(int argc, char** argv) {
pthread_cond_t* condition;
pthread_mutex_t* mutex;
char* message;
int des_cond, des_msg, des_mutex;
int mode = S_IRWXU | S_IRWXG;
des_mutex = shm_open(MUTEX, O_CREAT | O_RDWR | O_TRUNC, mode);
if (des_mutex < 0) {
perror("failure on shm_open on des_mutex");
exit(1);
}
if (ftruncate(des_mutex, sizeof(pthread_mutex_t)) == -1) {
perror("Error on ftruncate to sizeof pthread_cond_t\n");
exit(-1);
}
mutex = (pthread_mutex_t*) mmap(NULL, sizeof(pthread_mutex_t),
PROT_READ | PROT_WRITE, MAP_SHARED, des_mutex, 0);
if (mutex == MAP_FAILED ) {
perror("Error on mmap on mutex\n");
exit(1);
}
des_cond = shm_open(OKTOWRITE, O_CREAT | O_RDWR | O_TRUNC, mode);
if (des_cond < 0) {
perror("failure on shm_open on des_cond");
exit(1);
}
if (ftruncate(des_cond, sizeof(pthread_cond_t)) == -1) {
perror("Error on ftruncate to sizeof pthread_cond_t\n");
exit(-1);
}
condition = (pthread_cond_t*) mmap(NULL, sizeof(pthread_cond_t),
PROT_READ | PROT_WRITE, MAP_SHARED, des_cond, 0);
if (condition == MAP_FAILED ) {
perror("Error on mmap on condition\n");
exit(1);
}
/* HERE WE GO */
/**************************************/
/* set mutex shared between processes */
pthread_mutexattr_t mutexAttr;
pthread_mutexattr_setpshared(&mutexAttr, PTHREAD_PROCESS_SHARED);
pthread_mutex_init(mutex, &mutexAttr);
/* set condition shared between processes */
pthread_condattr_t condAttr;
pthread_condattr_setpshared(&condAttr, PTHREAD_PROCESS_SHARED);
pthread_cond_init(condition, &condAttr);
/*************************************/
if (!fork()) {
sleep(10);
pthread_mutex_lock(mutex);
pthread_cond_signal(condition);
printf("son signaled\n");
pthread_mutex_unlock(mutex);
exit(0);
}
else {
printf("father waits on condition\n");
pthread_mutex_lock(mutex);
pthread_cond_wait(condition, mutex);
pthread_mutex_unlock(mutex);
printf("Signaled by son process, wake up!!!!!!!!\n");
pthread_condattr_destroy(&condAttr);
pthread_mutexattr_destroy(&mutexAttr);
pthread_mutex_destroy(mutex);
pthread_cond_destroy(condition);
shm_unlink(OKTOWRITE);
shm_unlink(MESSAGE);
shm_unlink(MUTEX);
}
return 0;
}
To be shareable between processes a mutex needs to be initialised accordingly via a properly initialised attribute: http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutexattr_setpshared.html
#include <pthread.h>
...
pthread_mutex_t * pmutex = NULL;
pthread_mutexattr_t attrmutex;
/* Initialise attribute to mutex. */
pthread_mutexattr_init(&attrmutex);
pthread_mutexattr_setpshared(&attrmutex, PTHREAD_PROCESS_SHARED);
/* Allocate memory to pmutex here. */
/* Initialise mutex. */
pthread_mutex_init(pmutex, &attrmutex);
/* Use the mutex. */
/* Clean up. */
pthread_mutex_destroy(pmutex);
pthread_mutexattr_destroy(&attrmutex);
(error checking left out for the sake of this example's readability)
The same applies to a condition variable which should be shared between processes: http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_condattr_setpshared.html
#include <pthread.h>
...
pthread_cond_t * pcond = NULL;
pthread_condattr_t attrcond;
/* Initialise attribute to condition. */
pthread_condattr_init(&attrcond);
pthread_condattr_setpshared(&attrcond, PTHREAD_PROCESS_SHARED);
/* Allocate memory to pcond here. */
/* Initialise condition. */
pthread_cond_init(pcond, &attrcond);
/* Use the condition. */
/* Clean up. */
pthread_cond_destroy(pcond);
pthread_condattr_destroy(&attrcond);
(error checking left out for the sake of this example's readability)
Also see this answer: https://stackoverflow.com/a/2390670/694576
Waiting for a condition should be preceded by a while statement, like this:
pthread_mutex_lock(mutex);
while(!conditionSatisfied)
pthread_cond_wait(condition, mutex);
pthread_mutex_unlock(mutex);
while signaling should be done in the following way:
pthread_mutex_lock(mutex);
conditionSatisfied = true;
pthread_cond_signal(condition);
pthread_mutex_unlock(mutex);
yes, mutex have to be locked before pthread_cond_wait, but doesn't have to for pthread_cond_signal. If you look back at your code, you see that the mutex will be unlocked twice, which is a sign of error... it is also possible for the child to call unlock on a mutex which has been destroyed by the parent...
Btw sleeping doesn't guarantee that the parent will execute first. To ensure this you will need ... a condition variable...