探讨php中error_log函数输出内容的原子性问题
前几天跟同事讨论如何在一次login php调用中保证error_log输出的日志都有唯一的标识头,结论是玩家帐号+当前时间+随机数,在我们当前用户量级条件下应该是满足需求的,当然,这并不是本文讨论的重点。
在确定这个简单的方案之后,我在想两个问题,也是今天本文讨论的重点:
1.error_log调用是否可以保证输出内容是完整的?
2.如果是完整的,那么是怎样保证的?
求证开始了,起初以为error_log调用中会对目标文件加文件锁,直接看源码(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) /* {{{ */ { php_stream *stream = NULL; switch (opt_err) { case 1: /*send an email */ if (!php_mail(opt, PHP error_log message, message, headers, 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); php_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); break; } 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_stream *stream, const char *buf, size_t count TSRMLS_DC) { size_t didwrite = 0, towrite, justwrote; /* if we have a seekable stream we need to ensure that data is written at the * current stream->position. This means invalidating the read buffer and then * performing a low-level seek */ if (stream->ops->seek && (stream->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 = stream->ops->write(stream, buf, towrite TSRMLS_CC); /* convert justwrote to an 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 fifos and sockets */ if (stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) { stream->position += justwrote; } } else { break; } } return didwrite; }
通过看源码文件,error_log只是以O_APPEND模式打开文件,然后就是write buf,好吧,没有看到文件锁的影子。而问题也很明显,write可能会调用多次,那么也就基本认定msg并不保证被原子输出,多次的write使得这个问题更严重。
多次write不能保证原子操作,那么单次呢?
1.from:man write
If the file was open(2)ed with <strong>O_APPEND</strong>, the file offset is first set to the end of the file before writing. The adjustment of the file offset and the write operation are performed as an atomic step.
2.from:man write
Atomic/non-atomic: A write is atomic if the whole amount written in one operation is not interleaved with data from any other process. This is useful when there are multiple writers sending data to a single reader. Applications need to know how 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 are atomic, but requires that writes of {PIPE_BUF} or fewer bytes shall be atomic.
man说的大概意思就是如果是O_APPEND模式,文件尾的定位与write调用是原子操作,不会存在write时文件尾还需要再调整,导致错位。当前这个原子性,只保证open和之后的第一次write,后续的write就不保证了。像error_log那样如果buf过长导致了多次write肯定就不保证buf一次完整输出。
继续深究,那么单一次write是原子的吗?man也给出了答案,不是的,只有要写出的数据小于等于PIPE_BUF时才保证原子操作。
问题得到了理论上的解答,下面开始实验验证:
test_error_log.php
<!--?php $str = ; for($i = 0; $i < (int)$argv[2]; $i++) { $str = $str.$argv[1]; } for($i = 0; $i < (int)$argv[3]; $i++) { error_log($str. , 3, ./append.txt); } ?-->check_line.py
filename = ./append.txt for line in open(filename): print len(line)test.sh
#!/bin/bash rm -f append.txt for ((counter=0; counter < 10; ++counter)) do php test_error_log.php $counter $1 $2 & done sleep 2 #python check_line.py > a.txt python check_line.py | sort | uniq -c

验证思路:在输出msg最后加换行符,如果输出的每行与初始buf大小相同,说明本次error_log完整输出,经验证,内核3.5.0-23上PIPE_BUF是8K,8K以内的error_log输出都可以保证完整,否则会存在错乱的风险

Python 中有许多方法可以帮助我们理解代码的内部工作原理,良好的编程习惯,可以使我们的工作事半功倍!例如,我们最终可能会得到看起来很像下图中的代码。虽然不是最糟糕的,但是,我们需要扩展一些事情,例如:load_las_file 函数中的 f 和 d 代表什么?为什么我们要在 clay 函数中检查结果?这些函数需要什么类型?Floats? DataFrames?在本文中,我们将着重讨论如何通过文档、提示输入和正确的变量名称来提高应用程序/脚本的可读性的五个基本技巧。1. Comments我们可

连续分级概率评分(Continuous Ranked Probability Score, CRPS)或“连续概率排位分数”是一个函数或统计量,可以将分布预测与真实值进行比较。机器学习工作流程的一个重要部分是模型评估。这个过程本身可以被认为是常识:将数据分成训练集和测试集,在训练集上训练模型,并使用评分函数评估其在测试集上的性能。评分函数(或度量)是将真实值及其预测映射到一个单一且可比较的值 [1]。例如,对于连续预测可以使用 RMSE、MAE、MAPE 或 R 平方等评分函数。如果预测不是逐点

js是弱类型语言,不能像C#那样使用param关键字来声明形参是一个可变参数。那么js中,如何实现这种可变参数呢?下面本篇文章就来聊聊JavaScript函数可变参数的实现方法,希望对大家有所帮助!

一、前言前几天在Python钻石交流群有个叫【emerson】的粉丝问了一个Python排序的问题,这里拿出来给大家分享下,一起学习下。其实这里【瑜亮老师】、【布达佩斯的永恒】等人讲了很多,只不过对于基础不太好的小伙伴们来说,还是有点难的。不过在实际应用中内置函数sorted()用的还是蛮多的,这里也单独拿出来讲一下,希望下次再有小伙伴遇到的时候,可以不慌。二、基础用法内置函数sorted()可以用来做排序,基础的用法很简单,看个例子,如下所示。lst=[3,28,18,29,2,5,88

Python 中的 main 函数充当程序的执行点,在 Python 编程中定义 main 函数是启动程序执行的必要条件,不过它仅在程序直接运行时才执行,而在作为模块导入时不会执行。要了解有关 Python main 函数的更多信息,我们将从如下几点逐步学习:什么是 Python 函数Python 中 main 函数的功能是什么一个基本的 Python main() 是怎样的Python 执行模式Let’s get started什么是 Python 函数相信很多小伙伴对函数都不陌生了,函数是可

好嘞,今天我们继续剖析下Python里的类。[[441842]]先前我们定义类的时候,使用到了构造函数,在Python里的构造函数书写比较特殊,他是一个特殊的函数__init__,其实在类里,除了构造函数还有很多其他格式为__XXX__的函数,另外也有一些__xx__的属性。下面我们一一说下:构造函数Python里所有类的构造函数都是__init__,其中根据我们的需求,构造函数又分为有参构造函数和无惨构造函数。如果当前没有定义构造函数,那么系统会自动生成一个无参空的构造函数。例如:在有继承关系

形参变量在未出现函数调用时并不占用内存,只在调用时才占用,调用结束后将释放内存。形参全称“形式参数”,是函数定义时使用的参数;但函数定义时参数是没有任实际何数据的,因而在函数被调用前没有为形参分配内存,其作用是说明自变量的类型和形态以及在过程中的作用。

Golang的函数类型断言是一个非常重要的特性,它可以让我们在函数中精细地控制变量的类型,从而更加方便地进行数据处理和转换。本文将介绍Golang函数的类型断言用法,希望能够对大家的学习有所帮助。一、什么是Golang函数的类型断言?Golang函数的类型断言可以理解为函数参数中所声明变量的类型具有多态性,这使得一个函数在不同的参数传递下可以灵活


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

SublimeText3 Linux新版
SublimeText3 Linux最新版

SecLists
SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

WebStorm Mac版
好用的JavaScript開發工具

SublimeText3 英文版
推薦:為Win版本,支援程式碼提示!