嵌入式学习日记(七)

这篇嵌入式学习日记记录了作者在Linux环境中使用多线程进行文件内容拷贝的过程,重点介绍了如何避免IO错误冲突。通过使用pread()和pwrite()函数实现原子性操作,提高性能,避免了read/write + lseek的混用问题。在多线程编程中,作者强调了数据结构的设计和参数传递的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2017-07-14

今日是培训的第七天。

(不要问我为什么总写这句看似很low的话,这是一个程序员的修养)


今日,老师主要还在回顾一些之前的内容,新课讲的并不是很多。我就来show一下自己写的东西吧。


这是一个在Linux下的多线程将文件A的内容拷贝文件B中的程序

  • 头文件部分:
#include <stdio.h>
#include <fcntl.h> //用来避免一些系统安全问题
#include <sys/types.h> //类型
#include <stdlib.h>
#include <unistd.h> //unix的一些系统调用
#include <time.h>
#include <pthread.h> //多线程头文件
#include <sys/stat.h> //一些方便的函数


* 调用的一些关键函数部分:
普通版:
pthread_join() 等待线程的结束
pthread_create() 线程的创建
open() 打开文件,返回一个文件描述符,还可以声明一些权限部分
read() 通过文件描述符进行读
write() 通过文件描述符进行写
lseek() 进行文件读写位置的偏移
truncate() 进行文件的截断,即将目标文件变成与源文件大小相等
缺点:在多线程的运行过程中,会有一些IO错误冲突的情况,要注意避免。

解决方案:使用pread()、pwrite()
以上两个函数操作具有原子性,使得线程间互不干扰,而且不用lseek进行位置的偏移
评价:pread/pwrite性能优于read/write+lseek。并且具有原子性。强烈推荐
注意:不要和read/write/lseek混用

* 使用的数据结构
(在多线程中传递参数必须使用一个结构体作为参数列表传递数据):

/* * * 存放传输的信息 * * */
typedef struct file_info
{
  char *src;    //存储源文件的名字
  char *obj;    //存储目标文件的名字
  off_t offset;   //文件读写的偏移量
  off_t size;    //数据的大小
}F_INFO;


  • 宏定义
/* * * 表示所创建的子线程总数 * * */
#define THREAD_COUNT (5)


  • 多线程函数(用于让创建的线程执行的函数):
void *copy_thread(void *arg)
{
  int src_fd,obj_fd;        //文件描述符
  F_INFO *fi = (F_INFO *)arg;   //参数指针
  char buffer[1024];        //临时存储的文件内容
  int rd_size;            //当前线程——真实读取的大小
  int cnt=0;             //当前线程——总共读取的数量

  src_fd=open(fi->src,O_RDONLY);   //只读权限
  if(-1==src_fd)
  {
    perror("src is error!\n");
    return ;
  }

  obj_fd=open(fi->obj,O_RDWR|O_CREAT);//后面的参数是读写权限+如果没有则创建obj文件
  if(-1==obj_fd)
  {
    perror("obj is error!\n");
    return ;
  }

  while(cnt<fi->size)
  {
    off_t size;

  //所要读取的数据的大小
    size=((sizeof(buffer)+cnt)>=(dm->size))?(dm->size-cnt):(sizeof(buffer));

    rd_size=pread(src_fd,buffer,size,dm->offset);

    if(-1==rd_size)
    {
      perror("read error!\n");
      return ;
    }

    pwrite(obj_fd,buffer,rd_size,dm->offset);

    cnt+=rd_size;
  }

  close(src_fd);
  close(obj_fd);

  return ;
}


  • 以下为主函数的部分
int main(int argc,char **argv)
{
  char *src;             //源文件名称
  char *obj;             //目标文件名称
  int i;
  off_t offset=0;
  int rd_len;
  pthread_t tid[THREAD_COUNT];  //线程编号
  struct stat src_st;       //读取源文件的文件状态
  F_INFO fi[THREAD_COUNT];    //每个线程的参数列表

  src=argv[1];       //读取第一个参数,作为源文件

  stat(src,&src_st);   //获取文件状态(大小等...)

  obj=argv[2];       //读取第二个参数,作为目标文件

  truncate(obj,src_st.st_size);

  for(i=0;i<THREAD_COUNT;i++)
  {
    fi[i].src=src;
    fi[i].obj=obj;
    fi[i].offset=(src_st.st_size/THREAD_COUNT)*i;

    fi[i].size=src_st.st_size/THREAD_COUNT +
      ((i==THREAD_COUNT-1)?(src_st.st_size%THREAD_COUNT):0);

    pthread_create(&tid[i],NULL,copy_thread,(void *)&fi[i]);
  }

  for(i=0;i<THREAD_COUNT;i++)
  {
    pthread_join(tid[i],NULL);
  }

  return 0;
}


今天干货满满,感觉又变强了(^__^) 嘻嘻……


学会在markdown中插入整块代码。(没错,我就是来打广告的~)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值