首页 > 系统相关 >linux 多线程写

linux 多线程写

时间:2023-10-10 17:55:06浏览次数:47  
标签:pwrite thread read write fd linux pread 多线程

 

 

pread 和 pwrite 函数是 linux 下 C 语言编程中非常好用的 IO 操作函数。它们属于系统调用,在 2.1.60 之后版本的 linux 下都可以使用,尤其适合用于多线程的应用中,它们允许多个线程操作同一个文件描述符,不会互相影响彼此的文件偏移(offset)。

 

pread 和 pwrite 函数

所需头文件和函数原型

#include <unistd.h>
ssize_t pread(int fd, void *buf, size_t count, off_t offset);
ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);

函数说明

preadpwrite函数从文件描述符 fd(可由open函数获得)描述的文件的 offset 偏移处,读写 count 字节数据。

如果是读,读出的内容通过buf传出。如果是写,写入的内容由buf传入。

它俩的返回值与readwrite函数类似,成功时,preadpwrite函数分别返回成功读出或者写入的字节数。如果调用失败,返回 -1,失败码可从errno查出。

pread/pwrite 和 read/write 函数的联系和区别

以写入函数为例,pwrite函数在单线程的情况下,相当于 lseekwrite函数的组合:

pwrite(fd, buf, len, offset);
/**   相当于   */
lseek(fd, offset, SEEK_SET);
write(fd, buf, len);

只不过,在 fd文件描述符没有O_APPEND属性的情况下,write函数会修改文件偏移指针,下次不seek写入位置直接调用write函数时,write函数会接着上次的位置继续写入(offset+len)。而pwrite函数则不会改变文件偏移指针。

另外,很重要的一点是,pwrite函数的seek写入是原子操作,不会因为进程调度或者其他因素中断。而对于lseekwrite的组合,因为两个语句之间存在间隙,进程调度线程切换有可能发生在其间,导致写入的数据出错。

 

pread/pwrite 和 read/write 函数的实例demo对比

下面这段代码的功能很简单,就是创建了两个线程,一个线程负责顺序往 test.bin 文件顺序写入 0~254 的整数,另外一个线程则负责从中顺序读出。编译后,程序接收一个参数,1表示只从test.bin中读取,2表示只往test.bin中写入,3表示同时读写test.bin

lseekread/write的组合在多线程同时读写中的表现

 

#include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <pthread.h> #include <stdlib.h> int fd; #define MAX 255 #define filename "test.bin" void* r_thread(void* p) { int i = 0, j = 0; for(i=0; i<MAX; i++){ usleep(100); lseek(fd, i, SEEK_SET); read(fd, &j, 1); // pread(fd, &j, 1, i); printf("%s: %d\n", __FUNCTION__, j); } return NULL; } void* w_thread(void* p) { int i = 0, j = 0; for(i=0; i<MAX; i++){ usleep(100); lseek(fd, i, SEEK_SET); write(fd, &i, 1); // pwrite(fd, &j, 1, i); printf("%s: %d\n", __FUNCTION__, j); } return NULL; } int main(int argc, char* argv[]) { pthread_t ppid; fd = open(filename, O_RDWR|O_CREAT, 0777); if(argc<2){ printf("\n usage: %s [1(read),2(write),3(both read and write)]\n\n", argv[0]); exit(1); } switch(atoi(argv[1])){ case 1: pthread_create(&ppid, NULL, r_thread, NULL); break; case 2: pthread_create(&ppid, NULL, w_thread, NULL); break; case 3: pthread_create(&ppid, NULL, r_thread, NULL); pthread_create(&ppid, NULL, w_thread, NULL); break; default: printf("\n usage: %s [1(read),2(write),3(both read and write)]\n\n", argv[0]); break; } getchar(); return 0; }

 

我们编译,执行,发现有些数字并没有被顺序写入。

$ gcc pread_vs_read.c -lpthread $ ./a.out 3 w_thread: 0 r_thread: 0 r_thread: 254 ... $ ./a.out 1 ... r_thread: 43 r_thread: 211 r_thread: 210 r_thread: 46 r_thread: 208 r_thread: 48 r_thread: 52 r_thread: 202 r_thread: 54 ...

 

 

来自:https://blog.popkx.com/linux-multithreaded-programming-in-io-read-write-security-functions-pread-pwrite-and-read-write-what-is-the-difference-and-relat/

原因上面已经分析,就是因为lseekread/write的组合并不是原子操作,而且read/write函数也会改变文件偏移指针,这都会导致多线程同时读写时的数据紊乱。

 

pread/pwrite在多线程同时读写中的表现

现在来试试pread/pwrite函数的表现如何,我们将代码中的lseekread/write注释,取消pread/pwrite的注释:

...
// lseek(fd, i, SEEK_SET);
// read(fd, &j, 1);
pread(fd, &j, 1, i);
...
// lseek(fd, i, SEEK_SET);
// write(fd, &i, 1);
pwrite(fd, &i, 1, i);
...

再编译,做相同的测试:

$ gcc pread_vs_read.c -lpthread
lcc@lcc-vm:/lccRoot/xx_test/tmp/pread_vs_read$ ./a.out 3
w_thread: 0
r_thread: 0
w_thread: 1
r_thread: 1
...
$ ./a.out 1
r_thread: 0
r_thread: 1
r_thread: 2
r_thread: 3
r_thread: 4
r_thread: 5
...

发现数据被正确写入了。

总结

可以看出,pread/pwrite更适合在多线程编程中使用。当然,非要用read/write也可以,只是需要自行做好线程同步,这就比pread/pwrite麻烦了许多。

 

参考:

 

标签:pwrite,thread,read,write,fd,linux,pread,多线程
From: https://www.cnblogs.com/rebrobot/p/17755350.html

相关文章

  • 多线程分批处理数据(控制服务器cpu,控制数据库cpu)
    packageip;importcom.google.common.collect.Lists;importlombok.extern.slf4j.Slf4j;importjava.util.List;importjava.util.concurrent.CountDownLatch;importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;/***功能描述:总思路,根据业务......
  • linux学习记录(thrift) 10.10
       做一个游戏匹配系统1、定义接口2、server3、cilentthrift:跨语言的服务部署框架,rpc框架,远程函数调用 任务:实现游戏节点,匹配节点(两个服务,match_system文件,game文件,thrift文件存所有thrift接口)游戏节点到匹配系统的有向边(实现match_client端、实现match_server端)......
  • 【Linux】Alpine 固定ip
    vi/etc/network/interfacesautoeth0ifaceeth0inetstaticaddress192.168.31.4netmask255.255.255.0gateway192.168.31.2dns-nameservers192.168.31.2hostname$(hostname)vi/etc/resolv.confnameserver192.168.31.2重启 /etc/init.d/networkingresta......
  • 3款国产办公软件,不仅好用,还支持linux国产操作系统
    当提到国产办公软件并支持Linux国产操作系统时,以下是三款备受好评的软件:1.WPSOffice(金山办公套件)WPSOffice是中国知名的办公软件套件,也是一款跨平台的应用程序。它包含文字处理、表格编辑和演示文稿等常见办公功能模块。WPSOffice支持多个操作系统,包括Windows、macOS以及L......
  • 多线程使用场景三-异步调用
         ......
  • 多线程使用场景二(数据汇总)
       ......
  • Linux系统使用常见问题与解答
    作为一种强大而灵活的操作系统,Linux在实际使用过程中可能会遇到一些常见问题。本文旨在为大家整理和解答Linux系统使用中的常见问题,帮助读者更好地理解和应对技术挑战。无论您是Linux初学者还是有一定经验的用户,本文都能为您提供实用的解决方案和操作建议。一、安装和启动问题1.安......
  • Linux三剑客:grep、sed和awk
    运维必须会的linux三剑客,被称为linux利器。(建议收藏,运维面试100%会涉及)原创 小小IT仔 IT仔的笔记本 2023-08-2817:51 发表于云南收录于合集#linux11个#运维11个#利器1个Linux三剑客:grep、sed和awkLinux提供了许多命令行工具来帮助我们处理和分析文本数据。......
  • linux命令2
    1.grep  zgrepQYJF03*.zip|more  项目中使用了这个命令来查看日志中发过的含有交易码为QYJF03的报文。本来要一个一个去解压ZIP日志,然后搜索QYJF03找到对应的发送、响应报文,使用这个命令大大简化了操作,速度效率提升好多。望而生畏的grep是linux用户用来搜索文本字符串......
  • Linux用户和权限
    Linux用户和权限Linux系统的超级管理员用户是:root用户Root用户root用户拥有最大的系统操作权限,而普通用户在许多地方的权限是受限的。普通用户的权限,一般在其HOME目录内是不受限的。一旦出了HOME目录,大多数地方,普通用户仅有只读和执行权限,无修改权限。su命令su命令就是用于账......