Case:
A regular Linux C program that performs several printf operations during execution and redirects stdout to a different file using the Bash Script redirection feature. In the run-time with CTRL + C to terminate the program, found that the directed file is always empty, that is, write failed.
Analysis:
Originally thought to be a problem caused by the bash redirection mechanism, the redirect was canceled, instead of printf using fprintf. The file is written inside the C program. Find the problem still. (Exclude fopen open failure factor)
Careful observation, found that the problem is concentrated in two levels, one is what CTRL + C exactly do, and the second is why the file operation failed.
First, CTRL + C does not represent the kill process, only represents interrupts. Interrupts and kills are not exactly the same concept. Google a bit, there are the following findings:
"The KILL command in Linux is used to terminate the operation of the specified process (terminate a process) and is a common command for process management under Linux. Typically, terminating a foreground process can use the CTRL + C key, but for a background process to be terminated with the KILL command, we need to first get the process PID using tools such as Ps/pidof/pstree/top. Then use the KILL command to kill the process. The KILL command ends the process by sending a specified signal to the process. By default, a term signal of number 15 is used . The term signal terminates all processes that cannot capture the signal. For those processes that can capture the term signal, a kill signal numbered 9 is used to forcibly "kill" the process. ”
“
1. Kill command can be with signal number option or without. If there is no signal number, the KILL command emits a stop signal (15), which can be captured by the process, allowing the process to clean up and release resources before exiting. You can also use kill to send a specific signal to the process. For example:
Kill-2 123
The effect is equivalent to pressing the CTRL + C key while the foreground is running the PID 123 process. However, a normal user can only use a KILL command without the signal parameter or a maximum of 9 signals.
2. Kill can have a process ID number as a parameter. When you send a signal to these processes with kill, you must be the owner of those processes. If you attempt to revoke a process that does not have permission to revoke or undo a process that does not exist, you get an error message.
3, you can signal to multiple processes or terminate them.
4. When kill successfully sends a signal, the shell displays the process termination information on the screen. Sometimes this information is not displayed immediately, only if you press ENTER to make the shell's command prompt reappear.
5, it should be noted that the signal to force the process to terminate, which often brings some side effects, such as data loss or terminal can not return to normal state. Be careful when sending a signal, only use the Kill signal (9) only if it is a last resort, because the process cannot capture it first. To undo all background jobs, you can enter kill 0. Because some commands that run in the background start multiple processes, it's a hassle to track down and find the PID of all the processes you want to kill. At this point, it is a valid method to use Kill zero to terminate all processes initiated by the current shell.
6, only the 9th kind of signal (SIGKILL) can unconditionally terminate the process, other signal processes have the right to ignore. The following are commonly used signals:
HUP 1 terminal disconnection
INT 2 Interrupt (with Ctrl + C)
Quit 3 exit (with Ctrl + \)
Term 15 termination
Kill 9 Forced termination
CONT 18 Continuation (contrary to stop, FG/BG command)
Stop 19 paused (with Ctrl + Z)
”
At this point, we can draw a conclusion: we usually press CTRL + C is not equivalent to "terminate the process." CTRL + C is generally equivalent to Kill-s SIGINT. That is, the process accepts the SIGINT signal. Accepting the SIGINT signal is not a simple kill process.
In general program writing, you can use the relevant functions contained in signal.h (such as sigaction) to respond to these different categories of signals, so that the corresponding post-processing, such as the release of resources and so on.
So, in a bash script, if it appears:
$cmd &
spid=$!
Kill-s SIGINT $spid
is unable to kill the process. The process is still running in the background. If you change it into a kill-9 $spid. To kill the process.
It's a little far. Back to the previous question, regardless of which kill method is taken, this will result in a failed write file.
What about the usual procedures for writing files and so on in the face of these kill? Google a bit, get the following useful information:
"SIGINT can be caught and handled. It's like telling the process, "Please stop, what do you ' re doing." The process is free to ignore the signal, orimplement a handler that does anything it wants. The default behavior is to terminate, and this is what processes would do. It's typical, but isn't required, for a process to handle SIGINTby gracefully terminating--Closing any open files, network connections, or database handles and stopping the current operation in such a Prevents data loss or corruption.
SIGKILL can ' t is handled by the receiving process. If a process gets sent SIGKILL,it ' s toast, period. If a process receives SIGKILL in the middle of a database transaction or file write (for instance),it has no chance to exit gracefully and data loss or corruption may occur.
When you did ctrl-c in the terminal, the terminal sends SIGINT to the running process. Like I said before,Most processes'll gracefully terminate onReceiving SIGINT. Once it ' s terminated, it's just as surely ended as if it had been SIGKILL ' Ed orexited normally--you shouldn ' t expect the IT in PS because it ' s still gone.
But there is some programs that and don ' t respond to SIGINT on that. Bash is one example; If you hits ctrl-c at a shell prompt, it ' ll just cancel whatever you ' ve typed on that line--not terminate the whole shell . Again, this was because the behavior when a process receives SIGINT was determined by the process itself. VI is another program, doesn ' t handle SIGINT by terminating. "
As we have analyzed above, most programs require a handler to deal with SIGINT signals. The article mentions the exit normally. It seems that only a normal exit can guarantee the success of writing files?
StackOverflow there is a question and answer that just solves my question.
Q:
I ' ve read in a mans page that was exit()
called all streams was flushed and closed automatically. At first I was skeptical as-to-do and whether it's truly reliable but seeing as I can ' t find-out all more I ' m going to accept the it just works-we ' ll see if anything blows up. Anyway, if this stream closing behavior are present in are such behavior also present in the exit()
default handler SIGINT
for (The interrupt signal usually triggered with CTRL + C)? Or, would it is necessary to does something like this:
#include <signal.h>#include <stdlib.h>voidOninterrupt(IntDummy) {Exit(0); }IntMain() { Signal (sigint, Oninterrupt file *file = Fopen ( "file" , "a" for (;;) { Fprintf (file, "Bleh" ); } } /span>
To get to is file
closed properly? Or can the signal(SIG...
and void onInterrupt(...
lines be safely omitted?
Please restrict any replies to C, C99, and POSIX as I am not using the GNU libc. Thanks.
A:
1.So in C99, if it's closed then it s flushed.
2.You ' ll has to handle the signal if you want your buffers flushed. Otherwise the process would be terminated and the file descriptors closed without flushing the stdio buffers.
By default, a SIGINT would terminate the process abnornally. Processes so terminated don't call exit () and so does not have buffers flushed.
In other words, flush is only possible if you exit normally. Otherwise, the write fails.
After Baidu has a Chinese material, found the same conclusion.
The role of the Fflush library function is to write out all the uncommitted data in the file stream at once. For example, you can use this function to ensure that an interactive prompt is sent to the terminal before attempting to read a user response. You can also use this function to ensure that important data has been written to disk before the program continues execution. Sometimes when you are debugging a program, you can also use it to determine that the program is writing data instead of being suspended. Note that calling the fclose function implicitly performs a flush operation, so you do not have to call Fflush before Fclose .
fclose library function closes the specified file stream stream so that all data not yet written is written out. Because the stdio Library buffers data, it is important to use fclose. If the program needs to make sure that the data is all written out, you should call fclose function. Although the fclose function is automatically invoked for all open file streams when the program ends normally, there is no opportunity to check for errors reported by fclose. As with file descriptors, there is a limit to the number of available file streams. This restriction is defined by the fopen_max constant in the header file stdio.h, with a minimum of 8.
The so-called flush of a buffer, refers to the write buffer, the buffer in the data are all written to the actual file, and the buffer is emptied, so that the file is in the latest state. Flush is required because the write buffer causes the file to be in an unsynchronized state, logically some data has been written to the file, but in fact the data is still in the buffer, if the program unexpectedly exits (exception or power outage, etc.), then the data in the buffer will not be able to write to the file. Flush can prevent this from happening to some extent. ”
So, usually we write procedures, need to be cautious and follow the process, fclose do a lot of things, do not rely on the main function return automatically help you close files. Because in the event of such interruption, the problem will arise.
Solution:
Having analyzed so much, the solution is quite clear.
Programme one:
Add the SIGINT response function to the C program to ensure that the program exits normally.
Scenario Two:
Add the Fflush function to the C program to ensure that all outputs are written to the file at the first time.
Solution one is the best solution, and scenario two is a bit hack.
That's it.
Analysis of failure to write file due to interrupt program under Linux