Discussion on the atomicity of the output content of Error_log function in PHP
A few days ago to discuss with colleagues how to ensure that the Error_log output log in a login PHP has a unique identity header, the conclusion is the player account + current time + random number, in our current user volume level conditions should be to meet the demand, of course, this is not the focus of this article.
After identifying this simple scenario, I was thinking about two issues that are also the focus of this article today:
Can the 1.error_log call guarantee that the output is complete?
2. If it is complete, how is it guaranteed?
Verification began, initially thought Error_log call in the target file will be added to the file lock, directly read the source (php5.6.12):
From:basic_functions.c
PHPAPI int _php_error_log_ex (int opt_err, char *message, int message_len, char *opt, char *headers tsrmls_dc)/* {{*/{PH P_stream *stream = Null;switch (opt_err) {case 1:/*send an email */if (!php_mail (OPT, PHP error_log message, message, Heade RS, NULL tsrmls_cc)) {return FAILURE;} Break;case 2:/*send to an address */php_error_docref (NULL tsrmls_cc, e_warning, TCP/IP option not available!); Return failure;break;case 3:/*save to a file */stream = Php_stream_open_wrapper (opt, a, Ignore_url_win | Report_errors, NULL); if (!stream) {return FAILURE;} Php_stream_write (stream, message, Message_len);p Hp_stream_close (stream); Break;case 4:/* Send to SAPI */if (sapi_ Module.log_message) {sapi_module.log_message (message tsrmls_cc);} else {return FAILURE;} Break;default:php_log_err (message tsrmls_cc); return SUCCESS;} /* }}} */
From:streams.c
/* Writes a buffer directly to a stream, using multiple of the chunk size */static size_t _php_stream_write_buffer (php_str EAM *stream, const char *BUF, size_t count tsrmls_dc) {size_t didwrite = 0, towrite, justwrote;/* If we have a seekable st Ream we need to ensure this data is written in the * current stream->position. This means invalidating the read buffer and then * performing a low-level seek */if (Stream->ops->seek && (s Tream->flags & php_stream_flag_no_seek) = = 0 && stream->readpos! = stream->writepos) {stream-> Readpos = Stream->writepos = 0;stream->ops->seek (Stream, Stream->position, Seek_set, &stream-> Position tsrmls_cc);} while (Count > 0) {towrite = Count;if (Towrite > stream->chunk_size) towrite = Stream->chunk_size;justwrote = S Tream->ops->write (Stream, buf, Towrite tsrmls_cc);/* Convert Justwrote to a integer, since normally it is unsigned */if ((int) justwrote > 0) {buf + = justwrote;count-= Justwrote;didwrite + = justwrote;/* only screw with the buffer if we can seek, otherwise we lose data * buffered from FIFO S and sockets */if (Stream->ops->seek && (stream->flags & php_stream_flag_no_seek) = = 0) {Stream-> ;p osition + = Justwrote;}} else {break;}} return didwrite;}
By looking at the source file, Error_log just open the file in O_append mode, then it is write buf, OK, not see the file lock shadow. The problem is also obvious, write may be called multiple times, then it is basically determined that MSG is not guaranteed to be atomic output, multiple write makes this problem more serious.
Multiple write cannot guarantee atomic operation, so what about a single time?
1.from:man Write
o_append, the file offset is first set to the end of the file before writing. The adjustment of the file offset and the write operation is performed as an atomic step.
2.from:man Write
Atomic/non-atomic:a Write is Atomic if the whole amount written in one operation are not interleaved with data from any OT Her process. This was useful when there was multiple writers sending data to a single reader. Applications need to know what large a write request can be expected to be performed atomically. This maximum is called {pipe_buf}. This volume of IEEE Std 1003.1-2001 does not say whether write requests for more than {Pipe_buf} bytes is atomic, but req Uires that writes of {pipe_buf} or fewer bytes shall is atomic.
Man said that the approximate meaning is that if it is o_append mode, the location of the end of the file and the write call is an atomic operation, there will be no write at the end of the file needs to be adjusted, resulting in dislocation. The current atomicity guarantees only open and after the first write, and subsequent write is not guaranteed. Like Error_log, if the buf is too long, there is no guarantee that the BUF will be fully output once the write is repeated.
Keep going, then is the one-time write atomic? Man also gives the answer, no, only the data to be written less than equals pipe_buf to ensure atomic operation.
The question has been answered theoretically, and the following experiment has been validated:
test_error_log.php
check_line.py
filename =./append.txtfor line in open (filename): print len
test.sh
#!/bin/bash rm-f append.txtfor ((counter=0; counter < ++counter) do php test_error_log.php $counter $ & ;d onesleep 2#python check_line.py > A.txtpython check_line.py | Sort | Uniq-c
Verify the idea: in the output msg finally add line break, if the output of each line and the initial buf size is the same, the error_log complete output, verified that the kernel 3.5.0-23 pipe_buf 8k,8k error_log output can be guaranteed intact, Otherwise there is a risk of confusion.
http://www.bkjia.com/PHPjc/1050842.html www.bkjia.com true http://www.bkjia.com/PHPjc/1050842.html techarticle discussion on the atomicity of the output of error_log function in PHP a few days ago to discuss with colleagues how to ensure that the Error_log output log has a unique identity header in a login PHP call, the conclusion is to play ...