Colderleo's blog Colderleo's blog
首页
Linux
C++
Python
前端
工具软件
mysql
索引
关于
GitHub (opens new window)

Colder Leo

热爱代码的打工人
首页
Linux
C++
Python
前端
工具软件
mysql
索引
关于
GitHub (opens new window)
  • 常见程序性能开销cost、latency延迟耗时的经验值
  • 面试常见问题
  • 静态链接-动态链接-elf详解-elfloader
  • 动态库和静态库的依赖问题
  • glibc和ld的编译调试-为某程序单独设置ld
  • dl_iterate_phdr遍历linkmap头、获取so加载地址
  • shell、bash语法和脚本模板
  • so文件查找路径
  • 逻辑地址-线性地址or虚拟地址-物理地址
  • 通过ELF读取当前进程的符号表并获取符号的版本信息
  • 虚拟内存,cache,TLB,页表
  • 用户内存空间分布和mmap
  • numa网卡绑定
  • 隔核绑核、服务器优化
  • popen底层原理和仿照实现-execl
  • tmux用法
  • ASLR机制
  • 程序后台运行、恢复前台nohup
  • 大页内存huge_page
  • 用perf查看page-fault
  • Bash设置显示全部路径
  • 查看socket fd状态,设置nonblock
  • cout输出到屏幕的过程
  • 多进程写同一文件-write原子性-log日志
    • 参考
    • append
    • setvbuf
    • 测试多进程用append模式写同一个文件
    • 注
    • 底层原理
  • vim用法
  • epoll用法
  • signal信号、软中断、硬中断、alarm
  • 内核模块
  • 读写锁之pthread_rwlock和内核rwlock自旋读写锁
  • systemtap
  • xargs、awk用法
  • openssl libssl.so.10.so缺失问题
  • netstat用法
  • fork函数
  • tcp延迟确认ack
  • 90.centos7上一次std-string编译错误定位
  • docker用法
  • find用法
  • dmesg
  • gcc编译用法
  • avx-sse切换惩罚
  • Centos7防火墙
  • chmod用法
  • kernel-devel安装版本
  • Linux-Centos7系统安装、网络设置、常见报错
  • linux下g++编译c++文件
  • MegaCli 安装及使用
  • mysql
  • mysql忘记密码修改方法
  • set用法
  • crontab
  • ssh传文件scp
  • ssh连接
  • tcpdump、tshark、tcpreplay用法
  • ubantu root登录以及创建新用户
  • ubuntu安装g++和gdb
  • uClibc编译失败解决方法
  • win10安装WSL open-ssh
  • yum升级git
  • 比较so文件版本-md5sum
  • 查看磁盘信息
  • 合并两个硬盘,挂载到一个文件夹下
  • 软件安装目录usr-local-src
  • 下载centos历史版本
  • sh脚本转可执行文件、加密
  • Linux
gaoliu
2022-03-26
目录

多进程写同一文件-write原子性-log日志

# 参考

https://www.jianshu.com/p/b5a731940ff9 (opens new window)

# append

  • 在打开文件时使用append追加模式,则写入文件时不会因为文件指针位置错误而覆盖已写入的内容(linux内系统调用都是原子性的,多线程安全)

  • 使用setvbuf设置文件写入模式为行缓冲,即每次遇到换行或缓冲满了时写入文件。(测试不要它文件也不会乱)

实测在多线程或多进程模式下,写同一个文件时,比如log,可以多进程各自按行写入,不会出现多行错乱的情形(比如一行内出现两个线程要写的东西,printf就会乱),也不会出现写入数据被其他线程覆盖的情形。

# setvbuf

  • int setvbuf(FILE *stream, char *buffer, int mode, size_t size)

# 测试多进程用append模式写同一个文件

main.cpp

#include <unistd.h>
#include <stdio.h>
#include <thread>
#include <sys/types.h>
#include <fcntl.h>

void fun1() {
    printf("func1 start, write aaaa\n");
    int fd = open("log", O_CREAT | O_RDWR | O_APPEND);
    for(int i=0; i<100000; ++i) {
        write(fd, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", 33);
        usleep(10);
    }
    close(fd);
    printf("func1 end\n");
}

void fun2() {
    printf("func2 start, write bbbb\n");
    int fd = open("log", O_RDWR | O_APPEND);
    for(int i=0; i<100000; ++i) {
        write(fd, "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", 33);
        usleep(10);
    }
    close(fd);
    printf("func2 end\n");
}

int main(int argc, char *argv[]) {
    std::thread t1(fun1);
    usleep(1000);
    std::thread t2(fun2);
    if(t1.joinable()) {
        t1.join();
    }
    if(t2.joinable()) {
        t2.join();
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

main2.cpp, 将上面的aaaa bbbb改成cccc dddd即可

编译:

set -x
g++ -o main -pthread  main.cpp 
g++ -o main2 -pthread  main2.cpp 
1
2
3

开两个窗口分别执行main和main2,然后查看log文件,会发现没有数据错乱。

# 注

write操作是原子性的,配合append模式时多进程写文件不会混乱。但是要保证write写完,即write的返回值与要写的长度一样,而不是写不完就返回了。通常也都是写完了才返回。

对于socket的多线程write,阻塞模式下, 正常都会写完再返回, 不需要加锁。 非阻塞模式下,如果一次没写完(缓冲区满了),则有可能发生数据混乱。

普通文件的写入,一般write会一次性写完,不会写一半返回,不像socket。

# 底层原理

参考 https://blog.csdn.net/weixin_33959449/article/details/112632457 (opens new window)

  • APPEND模式,同一文件的write原子性的,系统通过锁inode,保证每次写操作均在inode中获取的文件size后追加数据,写完后释放锁;
  • 非APPEND模式, 只有共享fd时write是原子性的,系统通过锁file结构体后获取file结构体的pos字段,并将数据追加到pos后,写完更新pos字段后释放锁。

值得一再重申的是,由于write调用只是在inode或者file层面上保证一次写操作的原子性,但无法保证用户需要写入的数据的一次肯定被写完,所以在多线程多进程文件共享情况下就需要用户态程序自己来应对short write问题,比如设计一个锁保护一个循环,直到写完成或者写出错,不然循环不退出,锁不释放

编辑 (opens new window)
上次更新: 2023/05/07, 17:27:54
cout输出到屏幕的过程
vim用法

← cout输出到屏幕的过程 vim用法→

最近更新
01
通过模板实现结构体成员拷贝-按成员名匹配
05-07
02
c++17通过模板获取类成员的个数
05-01
03
avx-sse切换惩罚
04-30
更多文章>
Theme by Vdoing | Copyright © 2019-2023 Colder Leo | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×