To initialize an application the parent process forks 3 child processes, the child processes then set up their signal handlers and signal back to the parent that they are ready to start activity. SIGUSR1 signal is used to achieve this.
The parent process in the meanwhile is waiting for these signals from the child processes. As soon as a signal is received, the parent matches its pid with the child pids it has stored and increments a counter. Once the parent knows that go-ahead signals from all child processes have been received it starts to send each one of them a SIGUSR1 signal to indicate to start activity.
The fact that all signals are sent from the parent for each child is verified; however, most times one of the child processes misses the signal. Over multiple trials, I have identified that the process to which the parent sends the signal first, misses it. However sometimes it also occurs that all child processes miss their signals. I have also used the 'strace' tool to check the flow of all signals but still can't seem to identify why the child processes fail to catch the signals sent by the parent.
Any feedback will be appreciated.
SIGUSR1 and other POSIX signals are not queued. If the process has already one pending, any other signals will be discarded.
You can avoid this by using "realtime signals". You use them just like you use the standard POSIX signals; the first one is named SIGRTMIN+0, and the last one is named SIGRTMAX-0. If you use sigqueue(), you can even attach one int (or a void pointer) as a payload.
POSIX realtime signals are queued (up to a limit), so you are less likely to lose them.
However, I would not use signals for tracking the child processes. I would use pipes, with the child having the write ends, and parent having the read ends, and all descriptors marked for close-on-exec using fcntl(descriptor, O_SETFD, O_CLOEXEC) on each.
The children update the parent on their status via one-byte messages. If the child exits or executes another program, the parent will see it as end-of-file condition (read() returning zero). If the parent exits, the write end will become unwritable for the child write end, and any attempt at writing to the pipe will fail with EPIPE error. (It will also raise the SIGPIPE signal, so you might wish to use sigaction() to ignore the SIGPIPE signal.)
The parent can monitor the child process statuses in parallel using select() or poll(). Whenever a child process sends data, or exits or executes another program (which closes the write end of the pipe), the parent descriptor (read end of the pipe) will become readable. Personally, I also mark the parent descriptors nonblocking using fcntl(rfd, F_SETFL, O_NONBLOCK), so that if there is a glitch, instead of blocking on a mis-read, the read on a parent will just fail with EWOULDBLOCK in errno.
If you want bidirectional data flow, it is easiest to use an additional pipe for each child, parent writing and child reading from it.
It is also possible to use unnamed Unix domain datagram sockets (created via socketpair(AF_UNIX, SOCK_DGRAM, 0, fds). (See also man 2 socket and man 7 unix for details on the parameters.) Also use fcntl(fds[0], F_SETFL, O_CLOEXEC) and fcntl(fds[1], F_SETFL, O_CLOEXEC) to make the descriptors close-on-exec, just as in the pipe case, too.
The problem with Unix domain socket pairs (of any type -- SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET), is that they can contain ancillary data. This ancillary data can contain additional file descriptors, which are a limited commodity. If there is a possibility of an untrustworthy or nasty child process, it might kill its parent by sending it a few thousand file descriptors. For security, the parent process should monitor what it receives from the child process, and if it contains ancillary data, immediately close that descriptor (as the child process is obviously hostile!), and if any file descriptors were provided, close those too. You can only avoid this if you trust your child processes as much as you trust your original process for not doing anything nefarious.
Securing unix domain sockets is not hard, but checking each received datagram or receive for ancillary data is a couple of dozen additional lines of code.. Pipes are simpler.
Related
I am working on signals in C programming. I have parent process and 5 child processes, I am trying to send SIGUSR2 from child processes to parent process when they are done some calculations while parent waits for them with sigsuspend(). When all 5 child processes sends SIGUSR2, parent process continues its work. I increment a global variable in the signal handler function to do so. Sometimes it runs fine but sometimes parent process gets stuck waiting.
Can one of you guys help me with a better solution approach rather than counting the signals received (I must use signals for synchronization)?
To the best of my knowledge, you can't use signals for that. If two signals of the same kind are sent to a process before it gets scheduled to handle the first one, it will only see one signal. Think of it as a bit mask, there is one bit for each pending signal, and when the process gets scheduled it will receive them all. But if it is waiting for some other process, and a signal for which the bit in the mask is already set, then nothing more happens.
A better solution would probably be to open a pipe to each subprocess, and each of them writes a message when done. When the parent has read the message from all children, it can continue. There are other synchronisation methods, but this would probably be the simplest.
I have a while loop that reads data from a child process using blocking I/O by redirecting stdout of the child process to the parent process. Normally, as soon as the child process exits, a blocking read() in this case will return since the pipe that is read from is closed by the child process.
Now I have a case where the read() call does not exit for a child process that finishes. The child process ends up in a zombie state, since the operating system is waiting for my code to reap it, but instead my code is blocking on the read() call.
The child process itself does not have any child processes running at the time of the hang, and I do not see any file descriptors listed when looking in /proc/<child process PID>/fd. The child process did however fork two daemon processes, whose purpose seems to be to monitor the child process (the child process is a proprietary application I do not have any control over, so it is hard to say for sure).
When run from a terminal, the child process I try to read() from exits automatically, and in turn the daemon processes it forked terminate as well.
Linux version is 4.19.2.
What could be the reason of read() not returning in this case?
Follow-up: How to avoid read() from hanging in the following situation?
The child process did however fork two daemon processes ... What could be the reason of read() not returning in this case?
Forked processes still have the file descriptor open when the child terminates. Hence read call never returns 0.
Those daemon processes should close all file descriptors and open files for logging.
A possible reason (the most common) for read(2) blocking on a pipe with a dead child, is that the parent has not closed the writing side of the pipe, so there's still an open (for writing) descriptor for that pipe. Close the writing side of the pipe in the parent process before reading from it. The child is dead (you said zombie) so it cannot be the process with the writing side of the pipe open. And don't forget to wait(2) for the child in the parent, or you'll get a system full of zombies :)
Remember, you have to do two closes in your code:
One in the parent process, to close the writing side of the pipe, leaving the parent process with only a reading descriptor.
One in the child process (just before exec(2)ing) closing the reading side of the pipe, leaving the child process only with a writing descriptor.
In case you want to use the pipe(2) to send information to the child, change the reading for writing and viceversa in the above two points.
What happens when the child process gets killed while the parent is blocked on read() from a pipe? How should I handle this scenario in parent process?
For clarification, parent process has two threads. Lets say thread1 was reading from the pipe when thread2 killed the child.
Will read() return -1?
Will appreciate any help here.
Pipe behavior has nothing to do with process relationships. The same rules apply regardless of whether the reader is the parent, child, sibling, or some other distant relation of the writer. Or even if the reader and writer are the same process.
The short answer is that death of a writing process is just an EOF from the reader's point of view, not an error, and this doesn't depend on whether the writing process voluntarily called _exit() or was killed by a signal.
The whole cause and effect chain goes like this:
Process X dies -> all of process X's file descriptors are closed.
One of process X's file descriptors was the write end of a pipe
A pipe write file descriptor is closed -> was it the last one?
3a. There are other write file descriptors on the same pipe (e.g. inherited by fork and still open in another process), nothing happens. Stop.
3b. There are no more write file descriptors for this pipe -> the pipe has hit EOF.
Pipe hits EOF -> readers notice.
4a. All read file descriptors for the pipe become readable, waking up any process that was blocking on select or poll or read or another similar syscall.
4b. If there is any leftover data in the pipe buffer (written before the last write file descriptor was closed), that data is returned to the reader(s).
4c. repeat 4b until the pipe buffer is empty
4d. Finally, read() returns 0, indicating EOF.
The exit status of a child process is returned to the parent by the wait family of syscalls, and you have to check that if you want to know when your child processes have been killed by a signal.
I am trying to program the server side of a groupchat system using C whilst my friend is programming the client side. For each client connection the server receives, it forks a child process so as to handle the client and continue accepting any possibly further clients.
The server is required to send a list of all online users(connected clients) to each of the current connected clients and for this reason I have used pipes. Basically, when a child process is created, it receives come information from the client through a socket and sends such information to the parent, which is keeping a list of all the clients, through a pipe. This list has to be updated every time a client makes a change like starts chatting or disconnects. For example if a client disconnects then the child sends a message to the parent through the pipe and the parent makes the necessary operations to the list so that it gets updated. Note that the pipe is created for each and every new connection.
My problem is that if for example I receive 3 connections one after another, and the 2nd child disconnects, the parent is not reading the information from the pipe since such parent has a different pipe from the 2nd child. (Remember that a new pipe has been created because a 3rd connection has been made). How can I go about solving this problem?
I have also tried creating one common pipe but if I don't close the pipe before reading/writing I get an error and if I do close them I get a segmentation fault when the second client connects since the pipe would be closed.
Any help would be greatly appreciated because I have been searching for hours to no avail.
Thanks.
The parent server process knows when a child is created because it creates the child. It can tell when a child dies by setting a SIGCLD signal handler so it is notified when a child does die. The Nth child has N-1 pipes to close — those going to the other children (unless some of the children have died). The parent process closes the write end of the pipes it creates; the child process closes the read end of the pipes it inherits (which leaves it with a socket to the client and the write end of the pipe created for it to communicate with the parent).
If you need to know when a child starts communicating with a client, then you need to send a message down the pipe from the child to the parent. It is not so obvious how to tell when the child stops communicating — how long needs to elapse before you declare that the child is idle again?
In the parent, you end up polling in some shape or form (select(), poll(), epoll()) on the listening socket and all the read pipes. When some activity occurs, the parent wakes up and responds appropriately. It's a feasible design as long as it doesn't have to scale to thousands or more clients. It requires some care, notably in closing enough file descriptors.
You say:
My problem is that if for example I receive 3 connections one after another, and the 2nd child disconnects, the parent is not reading the information from the pipe since such parent has a different pipe from the 2nd child. (Remember that a new pipe has been created because a 3rd connection has been made). How can I go about solving this problem?
The parent should have an array of open file descriptors (pipes open for reading to various children), along with an indication of which child (PID) is on the other end of the pipe. The parent will close the pipe when it gets EOF on the pipe, or when it is notified that the child has died (via waitpid() or a relative). The polling mechanism will tell you when a pipe is closed, at least indirectly (you will be told the file descriptor won't block, and then you get the EOF — zero bytes read).
In your scenario, the parent has one listening socket open, plus 3 read file descriptors for the pipes to the 3 children (plus standard input, output and error, and maybe syslog).
Although you could use a single pipe from all the children, it is much trickier to handle. You'd have to identify which child wrote each message in the message, ensuring that the message is written atomically by the child. The parent has to be able to tell how much to read at any point so as not to be confused. The advantage of a single pipe is that there is less file descriptor manipulation to do for the polling system call; it also scales indefinitely (no running out of file descriptors).
In neither case should you run into problems with core dumps.
I am currently learning sockets programming using C in a Linux environment. As a project I am attempting to write a basic chat server and client.
The intention is to have the server fork a process for each client that connects.
The problem that I am having is reading data in one child and writing it to all of the connected clients.
I have tried to accomplish this by looping on a call to select in the child that waits for data to arrive on the socket or read end of the pipe. If it arrives on the socket the idea is that it writes to the write end of the pipe which causes select to return the read end of the pipe as ready for reading.
As this pipe is shared between all the children, each child should then read the data on the pipe. This does not work as the data pipe, it appears, cannot be read by each child process at the same time and the children that "miss" the data block in the call to read.
Below is the code in the child process that does this:
for( ; ; )
{
rset = mset;
if(select(maxfd+1, &rset, NULL, NULL, NULL) > 0)
{
if(FD_ISSET(clientfd, &rset))
{
read(clientfd, buf, sizeof(buf));
write(pipes[1], buf, strlen(buf));
}
if(FD_ISSET(pipes[0], &rset))
{
read(pipes[0], buf, sizeof(buf));
write(clientfd, buf, sizeof(buf));
}
}
}
I am presuming the method that I am currently using simply will not work. Is it going to be possible for messages received from a client to be written to all of the other connected clients via IPC?
Thanks
To get around the problem of a child reading from the pipe more data than it should (and in-turn making another child get "stuck" trying to read from an empty pipe), you should probably look into using either POSIX message queues or a single pipe between the parent and an individual child processes rather than a single global pipe to communicate between the parent and child processes. As it stands right now, when the server writes to the pipe to communicate with its children, it's not really able to control exactly which child will read from the pipe at any given time, since the scheduling of processes by the OS is non-deterministic. In other words, without some type of synchronizing mechanism or read/write barriers, if the server writes to the pipe, there is nothing in your code stopping one child from "skipping" a read, and a second child from doing a double-read, leaving another child that should have gotten the broadcasted data from the server starved, and therefore blocked.
A simple way around this could again be to have a single private pipe shared between the parent and an individual child. Thus in the server the child processes can read from the client, send that data back to the parent process, and the parent can then, using the entire list of pipe descriptors that it's accumulated for all the children, write back to each individual child process the broadcast message which is then sent back to each client. No child ever gets "starved" of data since there is no possibility of a double-read by another child-process. There is only a single reader/writer on each pipe, and the communication is deterministic.
If you don't want to handle multiple pipes for each child in the server's parent process, you could use a global message queue using POSIX message queues (found in mqueue.h). With that approach, if a child grabs a message that it's not suppose to have (i.e., you would need to pass around a struct that contained some type of ID value), it would place the message back in the queue and attempt to read another message ... that's not quite as efficient speed-wise as the direct pipe approach, but it would allow you to write-back a message that was not designated for the current child without the interleaving complications that would take place with a global pipe or FIFO mechanism.
Each byte of data written to a pipe will be read exactly once. It isn't duplicated to every process with the read end of the pipe open.
If you want the data duplicated to multiple destination processes, you have to explicitly duplicate the data. For example, you could have one "master" process that has a pipe to and from every "slave" process. When a slave wants to broadcast a message to the other slaves, it sends it to the master process, which loops around and writes it once to each pipe going to the other slaves.