exit and wait function in UNIX (or LINUX) - linux

I’m writing a program that simulates an unix-based operating system and I have some questions:
From unix.org
The wait() function will suspend execution of the calling thread
until status information for one of its terminated child processes is available, or until delivery of a signal whose action is either to execute a signal-catching function or to terminate the process
Let's imagine there is process A with two child processes B and C. If B and C call the exit function, and then A calls the wait function, which exit status will be retrieved? The one from B or the one from C? Which first and why?
2.When the process is in the waiting state, it doesn't execute its code until for example the status information for one of the terminated child processes is available, is that right?
So it can't for example call a fork fuction while waiting, is that correct?
3.Are there any restriction on when a process can normally be killed in UNIX?
3.a. Are users authorized to kill root processes? (all of the root processes at will?)

wait() returns the PID of whatever child process exited. If two have exited, you must call wait() twice and check the returned PIDs. You shouldn't rely on the order.
Correct, the entire purpose wait() (without the WNOHANG option) is to block. So you cannot do anything else, apart from handling signals, in the waiting process.
I'm not sure exactly what you mean here, but I suspect the answer is mostly "no."
Users cannot kill root processes (at least, not without special configuration). Users also cannot kill processes owned by other users.

It is indeterminate whether B or C will be reported first.
While the process is in wait(), it can do nothing else (in a single-threaded process).
No restrictions for the most part. There are non-interruptible system calls, but the system tries to avoid getting processes hung in them.
No; a user can kill their own processes. User root can kill other people's process (in general); but no-one else can kill root's processes.

Related

Why do we need a wait() system call?

Hello I am new to learning about system calls. I am currently learning about fork() and wait() system calls. I know that fork() creates a new child process. What confuses me is the wait() call.
This is what I understand so far:
(1) When a process dies, it goes into a 'Zombie State' i.e. it does not release its PID but waits for its parent to acknowledge that the child process has died and then the PID is released
(2) So we need a way to figure out when the child process has ended so that we don't leave any processes in the zombie state
I am confused with the following things:
(1) When running a C program where I fork a new child process, if I don't call wait() explicitly, is it done internally when the child process ends? Because you could still write a block of code in C where you run fork() without wait() and it seems to work fine?
(2) What does wait() do? I know it returns the PID of the child process that was terminated, but how is this helpful/related to releasing the PID of the terminated process?
I am sorry for such naive questions but this is something I was really curious about and I couldn't find any good resources online! Your help is much appreciated!
wait isn't about preventing zombie states. Zombie states are your friend.
POSIX more or less lets you do two things with pids: signal them with kill or reap them (and synchronize with them) with wait/waitpid/waittid.
The wait syscalls are primarily for waiting on a process to exit or die from a signal (though they can also be used to wait on other process status changes such as the child becoming stopped or the child waking up from being stopped).
Secondarily, they're about reaping exit/died statuses, thereby releasing (zombified) pids.
Until you release a pid with wait/waitpid/waittid, you can continue flogging the pid with requests for it to die (kill(pid,SIGTERM);) or with some other signal (other then SIGKILL) and you can rest assured the pid represents the process you've forked off and that you're not accidentally killing someone else's process.
But once you reap a zombified pid by waiting on it, then the pid is no longer yours and another process might take it (which typically happens after some time, as pids in the system typically increment and then wrap arround).
That's why auto-wait would be a bad idea (in some cases it isn't and then you can achieve it with globally with signal(SIGCHLD,SIG_IGN);) and why (short-lived) zombies states are your friend. They keep the child pid stable for you until you're ready to release it.
If you exit without releasing any of your children's pids, then you don't have to worry about zombie children anymore--your child processes will be reparented to the init process, which will wait on them for you when they die.
When you call fork(), a new process is created with you being its parent. When the child process finishes its running with a call to exit(), its process descriptor is still kept in the kernel's memory. It is your responsibility as its parent to collect its exit code, which is done with a call to wait() syscall. wait() blocks the parent process until one of its childrens is finished.
Zombie process is the name given to a process whose exit code was never collected by its parent.
Regarding to your first question - wait() is not called automatically as zombie processes wouldn't exist if it did. It is your responsibility as a programmer. Omitting the call to wait() will still work as you mentioned - but it is considered a bad practice.
Both this link and this link explains it good.

Linux fork, execve - no wait zombies

In Linux & C, will not waiting (waitpid) for a fork-execve launched process create zombies?
What is the correct way to launch a new program (many times) without waiting and without resource leaks?
It would also be launched from a 2nd worker thread.
Can the first program terminate first cleanly if launched programs have not completed?
Additional: In my case I have several threads that can fork-execve processes at ANY TIME and THE SAME TIME -
1) Some I need to wait for completion and want to report any errors codes with waitpid
2) Some I do not want to block the thread and but would like to report errors
3) Some I don't want to wait and don't care about the outcome and could run after the program terminates
For #2, should I have to create an additional thread to do waitpid ?
For #3, should I do a fork-fork-execve and would ending the 1st fork cause the 2nd process to get cleaned up (no zombie) separately via init ?
Additional: I've read briefly (not sure I understand all) about using nohup, double fork, setgpid(0,0), signal(SIGCHLD, SIG_IGN).
Doesn't global signal(SIGCHLD, SIG_IGN) have too many side effects like getting inherited (or maybe not) and preventing monitoring other processes you do want to wait for ?
Wouldn't relying on init to cleanup resources leak while the program continues to run (weeks in my case)?
In Linux & C, will not waiting (waitpid) for a fork-execve launched process create zombies?
Yes, they become zombies after death.
What is the correct way to launch a new program (many times) without waiting and without resource leaks? It would also be launched from a 2nd worker thread.
Set SIGCHLd to SIG_IGN.
Can the first program terminate first cleanly if launched programs have not completed?
Yes, orphaned processes will be adopted by init.
I ended up keeping an array of just the fork-exec'd pids I did not wait for (other fork-exec'd pids do get waited on) and periodically scanned the list using
waitpid( pids[xx], &status, WNOHANG ) != 0
which gives me a chance report outcome and avoid zombies.
I avoided using global things like signal handlers that might affect other code elsewhere.
It seemed a bit messy.
I suppose that fork-fork-exec would be an alternative to asynchronously monitor the other program's completion by the first fork, but then the first fork needs cleanup.
In Windows, you just keep a handle to the process open if you want to check status without worry of pid reuse, or close the handle if you don't care what the other process does.
(In Linux, there seems no way for multiple threads or processes to monitor the status of the same process safely, only the parent process-thread can, but not my issue here.)

How linux kernel decide the next thread id

I have a question regarding linux kernel scheduling.
We know that, usually, linux maintains the current largest pid. If we want to start a new process, the kernel will use that largest id. So, if we kill and restart a new process, the process id are not sequential. Linux will use the largest id until it hits a limit.
But my question is how linux decides thread ID.
Say, process A and B are running. Process A crashes but process B is spawning new threads. Will process B just reuse that old tid belonging to process A, or, process B will also use the largest id as tid. Which case is more often? Do we have documents?
Thanks.
The kernel sets a maximum number of process/thread ids and simply recycles identifiers when the threads are garbage collected. So if process B spawns enough threads it will eventually reclaim thread ids from process A assuming it has been properly destroyed
Edit: Here are some links that can provide you with more specific answers
Difference between pid and tid
https://stackoverflow.com/a/8787888/5768168
"what is the value range of thread and process id?"
what is the value range of thread and process id?
"Linux PID recycling"
https://stackoverflow.com/a/11323428/5768168
"Process identifer"
https://en.wikipedia.org/wiki/Process_identifier#Unix-like
"The Linux kernel: Processes"
https://www.win.tue.nl/~aeb/linux/lk/lk-10.html
It sounds like you need to run your threads in with a PTHREAD_CREATE_JOINABLE attribute passed to pthread_create(), then have one reaper thread in your process dedicated to using pthread_join() or pthread_tryjoin() to wait for terminated threads. Rather than having an outside process trying to sort it out, have your process record the PID/TID pair after pthread_create() succeeds and have the reaper thread remove the pair when it detects the thread has terminated.
I typically combined that with a main thread that did nothing but spawn the thread-creation and reaper threads, then wait for a termination signal and terminate the thread-creator and reaper. The thread-creator stops immediately when signaled, the reaper stops when no more unterminated threads are running, the main thread terminates when both the thread-creator and reaper threads can be pthread_join()'d. Since the main thread's so simple it's unlikely to crash, which means most crashes in work threads simply deliver them to the reaper. If you want absolute certainty, your outside process should be the one to start your main process, then it can use wait() or it's siblings to monitor whether the main process has terminated (normally or by crashing).

What happens to wait() if multiple child process terminate simultaneously?

Say I create a process with multiple child process and I call wait() in the main process. If one child terminates, its pid is returned. But what happens if a couple of child process terminate simultaneously? The call should return with one of them, and a second call should return with the other, right? Is there a particular order in which they will return (maybe there is a precedence to the child with lower pid)?
No.
SUSv4 leaves explicitly unspecified in which order (if any) child processes are reaped by one or several wait calls. There is also no "accidential" order that you could rely on, since different Linux kernel versions perform differently. (Source: M. Kerrisk, TLPI, 26.1.1, page 542).
Somewhat related trivia:
You might wonder why you can reliably wait on several child processes that terminate concurrently at all. If you think about how signals work, you might be inclined to believe that it is perfectly possible to lose child termination signals. Signals, a well-known fact, are not queued (except for realtime signals, but SIGCHLD isn't one!). Which means that if you go strictly by the letter of the book, then clearly several children terminating could cause some child termination signals becoming lost!
You can only call wait once at the same time, so you can at most consume one signal synchronously as it is generated, and have a second one made pending before your next call to wait. It appears that there is no way to account for any other signals that are generated in the mean time.
Luckily, that isn't the case. Waiting on child processes demonstrably works fine. Always, 100%. But why?
The reason is simple, the kernel is "cheating". If a child process exits while you are blocked in wait, there is no question as to what's happening. The signal is immediately delivered, the status information is being filled in, the parent unblocks, and the child process goes * poof *.
On the other hand, if a child is exiting and the parent isn't in a wait call (or if the parent is in a wait call reaping another child), the system converts the exiting process to a "zombie".
Now, when the parent process performs a wait, and there are any zombie processes, it won't block waiting for a signal to be generated (which may never happen if all the children have already exited!). Instead, it will just reap one of the zombies, pretending the signal was delivered just now.

Terminating a process -- Transition from allproc list to zombieproc list

How does a process get terminated? Lets say a process has three threads A,B & C. Now when we send a SIG_KILL signal to the process. All is fine so far, Now each process has exit status field in its structure! So, when a process is sent a kill signal, my understanding is that it is sent to all the threads. A thread gets killed either when it traps into kernel. If it is alreading in the kernel, it quits when it exits from the kernel. If a thread is sleeping it exits when it wakes up. Is my understanding right or am i misunderstading/missing something?
If my understanding is correct, when is a process put into zombie list? when all the threads exited or as soon as it receives a kill signal?
Lets say a process has three threads A,B & C.
Ok. I assume modern Linux, with kernel supporting threads (Linux 2.6 + glibc > 2.3)
Then the process (or thread group) consists of 3 threads (or, there is a 3 threads with different tids and same tgid=PID)
Now when we send a SIG_KILL signal to the process.
So, you use a tgid (PID) here. Ok.
Now each process has exit status field in its structure!
Wwwhat? Yes, but killing and exiting from thread group have a special code to get right exit code to waiter. For killing, the exit status is get from signal; for exiting (syscall sys_group_exit) it is the argument of syscall.
So, when a process is sent a kill signal,... it is sent to all the threads.
No.
Basically there can be two kinds of signals:
process-wide - it will be delivered to ANY thread in the process
thread (Can't name it correctly) - which is delivered by tid to some thread and not another.
So, SIGKILL is process-wide, it will kill entire process. It is delivered to some thread.
When kernel will deliver this signal - it will call do_group_exit() function ( http://lxr.linux.no/linux+v2.6.28/kernel/exit.c#L1156 called from http://lxr.linux.no/linux+v2.6.28/kernel/signal.c#L1870) to kill all threads in thread group (in process).
There is a zap_other_threads() function to iterate over all threads and kill them (with resending a thread-delivered SIGKILL) http://lxr.linux.no/linux+v2.6.28/kernel/signal.c#L966
when is a process put into zombie list?
After do_exit() kernel function call. It has a tsk->state = TASK_DEAD; line at the end.
when all the threads exited or as soon as it receives a kill signal?
The moment, when task get its state to TASK_DEAD is after receiving SIGKILL. This signal is already redelivered to all threads of the process at this moment. Can't find the actual exit time of threads, but all threads have a flag of pending fatal signal, so they will be killed at any resched.
UPDATE: all threads of process must be killed (must receive the KILL signal and do a cleanup), as they have some accounting information to be accumulated in the first thread (here first mention not the first-started, but the thread which got an original process-wide SIGKILL; or the thread, which called an exit_group syscall). First thread must wait all another threads; and it will change status only after that.
In FreeBSD, a zombie process cannot execute any code. Therefore, everything that needs the moribund process to do something is performed before that point. If you see a process in this state in ps(1) (usually only if it gets stuck), it has a usual state such as D, S, R or I, with E (trying to exit) appended to it.
A signal is delivered to one thread (either a particular thread or any thread, depending on how the signal was generated). The act of terminating the process (default action of various signals) has a process-global effect. One of the things that happens is that the thread that was chosen to deliver the signal (or that called _exit(2)) requests all other threads to exit.
A thread does not have an exit status at the kernel level; the value available via pthread_join() is a userland feature.

Resources