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

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

换行回车探讨

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

\r\nwindows下好像改善了使得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缓存
阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: linux

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