【Linux C编程-高级篇】换行回车探讨&printf行缓冲&write函数掉电保护&线程安全相关

换行回车探讨

  • \r : 回车,定位到本行开头
  • \n : 换行,光标移到下一行
  • \r\n : 将光标移动到下一行开头
  • windows 下,每行结尾 \r\n
  • 类unix,每行结尾 \n
  • Mac系统,每行结尾\r

\r\n,windows下好像改善了,使得Linux下文件即便只有\n在windows下也能正确显示

结论:统一用 \n 就可以

printf行缓冲

  • printf末尾不加\n就无法及时将信息输出到屏幕,是因为行缓冲的存在【windows上无,类unix上才有】
  • ANSI C中将 \n 认为是行刷新标记,printf函数直至行满或遇到\n才会自动刷新输出流。
  • 要么在printf函数后加上\n,要么使用下面两种方式
    • fflush ( stdout ); // 刷新标准输出缓冲区
    • setvbuf ( stdout, nullptr, _IONBF, 0 ); // 禁止printf缓冲区【标准输出缓冲区】,使得printf直接输出

write()系统调用思考

多个进程同时调用write()往日志写数据:是否会造成数据混乱
  • 多个进程同时调用write()往日志写数据是否会产生数据混乱?通过让父子进程同时在死循环内执行日志写测试进行测试。

  • 多个进程同时写一个文件,可能会出现数据覆盖、混乱等情况,保证数据不混乱不覆盖需要以下几点:

    • a) ngx_log.fd = open ( ( const char * ) plogname, O_WRONLY | O_APPEND | O_CREAT, 0644 );
      • O_APPEND 这个标记能保证多个进程操作同一个文件时不会相互覆盖
    • b) 内核write()是系统调用,是原子操作,线程安全
    • c) 父进程fork()产生的子进程,父子进程是亲缘关系,共享文件表项
  • 资料:《Unix环境高级编程 第三版》 第三章:文件IO 3.10-3.12,涉及到了文件共享、原子操作以及函数dup、dup2的讲解

    • 第八章:进程控制 8.3 涉及到了fork函数
write()写的安全问题:数据是否被成功写入磁盘
  • write和物理磁盘间,存在着程序缓冲区内核缓冲区,write先将数据从程序缓冲区写入内核缓冲区,然后返回,内核会在一定时间后刷新一次内核缓冲,将内核缓冲数据实际写入到物理磁盘中,即数据在内核缓冲区没有写到磁盘之前,即便write函数返回,也不表示数据一定写到磁盘了

write_写磁盘.jpg

write_内核缓冲区.jpg

  • 为了确保内核缓冲区数据能及时写入磁盘,内核设置了写入时间上限,当到达这个时间上限后,内核会将内核缓冲区的脏数据全部写入磁盘,这些跟脏数据、写入时间有关的文件 在目录 /proc/sys/vm/dirty*

  • 目前代码并没有强制写磁盘,后续会讲,所以还是存在突然断电数据没有写入磁盘的可能性

掉电导致write数据丢失破解法

O_DERICT、O_SYNC、缓存同步技术

  • O_DERICT: open参数,绕过内核缓冲区,直接访问物理磁盘,但是可想而知效率肯定低

write_掉电保护O_DIRECT.jpg

  • O_SYNC: open参数,同步选项,只对write有效,是每次write操作等待物理IO完成,将掉电等问题造成的损失减到最小

write_掉电保护O_SYNC.jpg

不管用哪个,都要注意:每次写磁盘数据,务必要大块大块写,一般512-4k 4k写;【一个扇区是512B】,不要每次写几个字节,否则会被抽死******

前两个方法都不推荐,因为实时写入磁盘的方式都很慢,且违背 了操作系统设置 内核缓冲区的初衷
  • 缓存同步:尽量保证缓存数据和写到磁盘上的数据一致

    • sync(void): 将所有修改过的块缓冲区排入写队列,然后返回,并不等待实际写磁盘操作结束,数据是否写入磁盘没有保证
    • fsync(int fd): 将fd对应的文件的块缓冲区立即写入磁盘,并等待实际写磁盘操作结束返回**********
    • fdatasync(int fd): 类似于fsync,但只影响文件的数据部分。而fsync除数据外,还会同步更新文件属性【文件属性和文件数据是分开存放在两个文件的,所以fdatasync要比fsync快,不关心文件属性时可以使用fdatasync】
  • fsync( int fd ) 的正确使用姿势

      假设整个文件4M,先执行1000次write(4k)
      再执行1次fsync ( fd )
    

对于我们写的小的日志系统,没必要动用缓存同步技术,即便断电丢失几条日志也不是很重要

所以日志中只用write就足够了,而且随着win系统的发展,基本上write后去查看磁盘的时候已经写入了

应对掉电问题的方法,在大型数据库文件中需要特别注意,此时就需要用到缓存同步技术

标准IO库

// fopen, flose
// fread, fwrite
// fflush
// fseek
// fgetc, getc, getchar
// fputc, put, putchar
// fgets, gets
// printf, fprintf, sprintf
// scanf, fscanf, sscanf

fwrite 与 write 区别:

  • fwrite 是标准IO库函数,一般在stdio.h
  • write 是系统调用

fwrite等标准IO库函数又加了一层Clib缓冲,非线程安全,因此不建议使用,用的不好会导致数据写入混乱

而write等系统调用由于其原子性操作,是线程安全的,结合O_APPEND标志就可以正常写入数据

【注意是父子进程,如果是不相干进程依旧有可能乱序,具体建议看书参考学习:《Linux/Unix系统编程手册上》 第13章对也有对IO缓存比较清晰的描述 】

标准IO库函数加入的Clib缓冲如图示

标准IO库函数_Clib缓冲.jpg

标准IO库与系统调用、缓冲等如图示

标准IO库与系统调用.jpg

IO缓冲小结

标准IO库_IO缓冲小结.jpg

书籍总结

  • 资料:《Unix环境高级编程 第三版》 第三章:文件IO 3.10-3.12,涉及到了文件共享、原子操作以及函数dup、dup2的讲解
    • 第八章:进程控制 8.3 涉及到了fork函数
  • 《Linux/Unix系统编程手册上》 第13章 IO缓存
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

csdnGuoYuying

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值