how is select for reading being handled on Linux systems in case the process was forked after opening a udp socket?
Especially - is it possible that in this kind of program:
so = open socket
fork
for(;;) {
select() for reading on socket so
recv from so
}
two packets will wake up only one of the processes (in case they arrive before the waiting process is notified / exits select) and the second one of those packets will not be received?
Or can I assume that for UDP, every packet will always wake up a process or leave the flag set?
Each processes, the parent and child, has a fie descriptor for the same socket. The per file descriptor attributes are independent (e.g. blocking, being able to close the socket).
In your scenario it is indeed feasible legal for one of the processes, for example to be waken and read the data from the socket before the other one getting into select.
Your question is not actually affected by the fork() at all.
select() returns if one of the file descriptors in the read set is readable. If you don't read from it and call select() again, it will still be readable. It will be remain readable until there is no more data to read from it.
In other words, select() is level-triggered, not edge-triggered.
Related
I'm new to C and I/O Multiplexing, just a question on select().
according to the man page of select() : allows a program to monitor multiple file descriptors, waiting until at least or more of the file descriptors become "ready" for some class of I/O operation. and the return value is the count of ready descriptors. I'm a little bit confused with the return value.
So let's say a concurrent web server use select(), and there are 2 clients(connection file descriptors) connecting to the server. if client A is writing sth and client B hasn't write yet, the of course select() return 1 and modify the fdset.
But I saw some code that use select() in a way that it can return when more than one file descriptors are ready, which means select() can return 2,3,....etc.
here is my question:
from my understanding, select() should return asap when one fd is available to avoid blocking, so if select() can detect more than one fd, and consider that each fd won't be ready in exace the same time(e.g. the first fd is ready at 1.0s and the second is ready 1.1s, they cannot be both ready at 1.0s), so how does select() know that when it enconter when the first fd is ready, it should not return immediately but wait until next fd is ready? and since select() can not predict, how does it know there will be a second ready fd?
or
the underlying mechanism of select() is, it is blocked until the first fd is ready, then it wait a predefined time to make sure it can catch other read fd before the timeout, so that select() can return at least 1?
My program is going to use a single socket to read and write. Messages from multiple clients will be written to the same socket and the responses received will be multiplexed back to the respective clients.
When making select() call, can I set the same single fd in readfds and writefds sets in order to detect whether the socket is ready for read or write? I plan to read or write on the socket based upon the the status that select() would return.
Yes, you can and that's a very typical thing to do.
Two processes are communicating via sockets - Process A and Process B.
Process B is using select() call to check when the socket is the ready for I/O.
Process A is suddenly killed. What will happen to the B side socket. Will B side socket automatically detect that A's socket is no longer available and select() will return -1 with EABDF. OR select() call will remain blocked forever.
Select will unlock and either an error case or a read case will be returned.
select() returns and says that the socket is readable. When you read the socket, you will get -1 (and the corresponding error in errno) or 0 (EOF).
The tcp socket will remain half opened for some time if there's no heartbeat between two sides.
Finally tcp connection will time out, depends on the time out settings.
Refer to: http://en.wikipedia.org/wiki/Half-open_connection
I am implementing a web server where I need to make the parent process do the following:
fork() new worker processes (a pool) at the beginning.
Looping forever, listening for incoming requests (via socket communication).
Putting the socket descriptor (returned by accept() function) into a queue.
and the worker process will do the following:
Once created, loops forever watching the queue for any passed socket descriptors.
If he takes the socket descriptor, he handles the request and serves the client accordingly.
After looking around and searching the internet, I found that I can send a file descriptor between different processes via UNIX Domain Socket or Pipes. But unfortunately, I can do this synchronously only! (I can send one fd at a time, and I cannot put it in a waiting queue)
So, my question is:
How can I make the parent process puts the socket descriptor into a waiting queue, so that, the request is pending until one of the worker processes finishes a previous request?
File descriptors are just integers. They are used to index into a per-process table of file information, maintained by the kernel. You can't expect a file descriptor to be "portable" to other processes.
It works (somewhat) if you create the files before calling fork(), since the file descriptor table is part of the process and thus clone()d when the child is created. For file descriptors allocated after the processes have split, such as when using accept() to get a new socket, you can't do this.
UPDATE: It seems there is a way, using sendmsg() with AF_UNIX sockets, see here for details as mentioned in this question. I did not know that, sounds a bit "magical" but apparently it's a well-established mechanism so why not go ahead and implement that.
put the fd on an internal queue (lock-free if you want, but probably not necessary)
have a thread in the parent process which just reads an fd from the internal queue, and sends it via the pipe
all child processes inherit the other end of the pipe, and compete to read the next fd when they finish their current job
In Linux if we call blocking recv from one thread and close for the same socket from another thread, recv doesn't exit.
Why?
The "why" is simply that that's how it works, by design.
Within the kernel, the recv() call has called fget() on the struct file corresponding to the file descriptor, and this will prevent it from being deallocated until the corresponding fput().
You will simply have to change your design (your design is inherently racy anyway - for this to happen, you must have no locking protecting the file descriptor in userspace, which means that the close() could have happened just before the recv() call - and the file descriptor even been reused for something else).
If you want to wake up another thread that's blocking on a file descriptor, you should have it block on select() instead, with a pipe included in the file descriptor set that can be written to by the main thread.
Check that all file descriptors for the socket have been closed. If any remain open at the "remote end" (assuming this is the one you attempt to close), the "peer has not performed an orderly shutdown".
If this still doesn't work, call shutdown(sock, SHUT_RDWR) on the remote end, this will shut the socket down regardless of reference counts.