一般来说,当我们从多个进程附加到UNIX中的文件时,我们可以理所当然地认为什么?是否有可能丢失数据(一个进程会覆盖其他进程)?数据是否可能被破坏?(例如,每个进程在每个追加到日志文件时附加一行,是否有可能两行被破坏?)如果追加在上述意义上不是原子的,那么确保互斥的最佳方法是什么?
显然POSIX说明了这一点
文件描述符或流在其引用的打开文件描述上称为"句柄"; 打开的文件描述可能有几个句柄.[...]应用程序影响第一个句柄上文件偏移量的所有活动都应暂停,直到它再次成为活动文件句柄.[...]句柄不需要在同一过程中应用这些规则.- POSIX.1-2008
和
如果两个线程分别调用[write()函数],则每个调用应该看到另一个调用的所有指定效果,或者没有看到它们.- POSIX.1-2008
我对此的理解是,当第一个进程发出
write(handle, data1, size1)第二个进程并且第二个进程发出时
write(handle, data2, size2),写入可以以任何顺序发生,但是data1并且data2 必须既是原始的又是连续的.
但运行以下代码会给我带来意想不到的结果.
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/wait.h>
die(char *s)
{
perror(s);
abort();
}
main()
{
unsigned char buffer[3];
char *filename = "/tmp/atomic-write.log";
int fd, i, j;
pid_t pid;
unlink(filename);
/* XXX Adding O_APPEND to the flags cures it. Why? */
fd = open(filename, O_CREAT|O_WRONLY/*|O_APPEND*/, 0644);
if (fd < 0)
die("open failed"); …Run Code Online (Sandbox Code Playgroud) 我正在编写一个回调函数C:
static size_t writedata(void *ptr, size_t size, size_t nmemb, void *stream){
size_t written = fwrite(ptr, size, nmemb, (FILE)*stream);
return written;
}
Run Code Online (Sandbox Code Playgroud)
此函数将用于另一个函数,该函数执行HTTP请求,检索请求并将其写入本地计算机.该writedata功能将用于后面的部分.整个操作必须是multithreaded,所以我write和之间存在疑问fwrite.可能有人帮助我,概述之间的差异write(),并fwrite()在C,所以我可以选择哪一个最适合到我的问题吗?
我花了几个小时来研究行为,首先是关于这些问题:
看来,如果在打开文件时使用'O_APPEND'标志,则在Linux上从多个进程登录到同一文件总是可以的。而且我相信python肯定会在其日志记录模块中使用'O_APPEND'标志。
并通过一个小测试:
#!/bin/env python
import os
import logging
logger = logging.getLogger('spam_application')
logger.setLevel(logging.DEBUG)
# create file handler which logs even debug messages
fh = logging.FileHandler('spam.log')
logger.addHandler(fh)
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(formatter)
for i in xrange(10000):
p = os.getpid()
logger.debug('Log line number %s in %s', i, p)
Run Code Online (Sandbox Code Playgroud)
我用它运行:
./test.py & ./test.py & ./test.py & ./test.py &
Run Code Online (Sandbox Code Playgroud)
我发现spam.log中没有错。此行为可能支持上述结论。
但是问题来了:
最后,如果两个进程正在同一文件上执行写操作,那是指它们正在调用同一文件上的write(2),谁可以确保来自两个进程的数据不会交织(内核或文件系统?),以及如何交织。[注意:我只想深入了解写系统调用,对此的任何欢迎。
编辑1:
我想从两个进程中获取输出并将它们合并到一个文件中,例如:
proc1 >> output &
proc2 >> output &
Run Code Online (Sandbox Code Playgroud)
问题是输出可能会在最终文件中混淆。例如,如果第一个进程写入:
你好
第二个过程写道:
再见
结果可能类似于:
海贝罗
但我希望它们在单独的行中(顺序不重要):
再见
你好
所以我使用 flock 通过以下脚本同步写入文件:
exec 200>>output
while read line;
flock -w 2 200
do echo $line>>output
flock -u 200
done
Run Code Online (Sandbox Code Playgroud)
并运行如下进程:
proc1 | script &
proc2 | script &
Run Code Online (Sandbox Code Playgroud)
现在的问题是性能显着下降。如果没有同步,每个进程可以以 4MB/秒的速度写入,但使用同步脚本的写入速度为 1MB/秒。
谁能帮助我如何合并两个进程的输出并防止混合输出?
编辑:我意识到行长度和 std 缓冲区大小之间存在关系,如果每行的大小小于 std 缓冲区大小,那么一切都很好,没有任何混合(至少在我的测试中)。所以我用 bufsize 命令运行了每个脚本:
bufsize -o10KB proc1 | script &
bufsize -o10KB proc2 | script &
Run Code Online (Sandbox Code Playgroud)
现在我想确保这个解决方案是防弹的。我找不到缓冲区大小与现在发生的事情之间的任何关系!!!
假设我有一个如下所示的 bash 脚本:
array=( 1 2 3 4 5 6 )
for each in "${array[@]}"
do
echo "$each"
command --arg1 $each
done
Run Code Online (Sandbox Code Playgroud)
如果我想并行运行循环中的所有内容,我可以更改command --arg1 $each为command --arg1 $each &.
但现在假设我想获取结果command --arg1 $each并用这些结果做一些事情,如下所示:
array=( 1 2 3 4 5 6 )
for each in "${array[@]}"
do
echo "$each"
lags=($(command --arg1 $each)
lngth_lags=${#lags[*]}
for (( i=1; i<=$(( $lngth_lags -1 )); i++))
do
result=${lags[$i]}
echo -e "$timestamp\t$result" >> $log_file
echo "result piped"
done
done
Run Code Online (Sandbox Code Playgroud)
如果我只是&在 的末尾添加 …