C:每个子进程读取备用行

时间:2010-10-11 22:03:37

标签: c

我正在训练一个典型的map-reduce架构(在O.S.类中),我可以自由决定主进程如何告诉其N个子进程解析日志。所以,我有点陷入这两种可能性:

  1. 计算行数并为每个地图提供X行或

  2. 每个地图读取其ID行,下一行读取= current_one + number_of_existent_maps 例如:有3张地图,每张地图都会读到以下这些行:

    • Map1:1,4,7,10,13
    • Map2:2,5,8,11,14
    • Map3:3,6,9,12,15
  3. 我必须执行此操作才能超出分析整个日志文件的单个进程,因此我在子进程之间拆分作业的方式必须与此目标一致。

    您认为哪一个最好?如何使scanf或fgets适应1)或2)?

    我会对2)的一些示例代码感到满意,因为fork / pipes不是我的问题:P

    RE-编辑: 我不鼓励在这里使用select,只在map procs和将监视读取的reduce进程之间。我现在有限制:

    我希望每个进程都读取total_lines / N行。但似乎我必须让map procs打开文件然后读取相应的行。所以这是我的怀疑:

    1-使每个过程同时或几乎同时打开文件是不是甚至可能?这有助于加快速度吗?

    2-如果无法做到这一点,我将让父母打开文件(而不是每个孩子这样做),发送一个具有最小和最大限制的结构,然后地图过程将读取任何行他们负责,处理它们并给予缩减过程一个结果(现在这个问题无关紧要。)

    如何通过N个地图正确划分行数并同时将它们读取?我认为fseek()可能是一个好武器,但我不知道我如何使用它。求救!

3 个答案:

答案 0 :(得分:6)

如果我理解正确,您希望让所有进程从单个文件中读取行。我不推荐这个,它有点混乱,你必须a)多次读取文件的相同部分或b)使用锁定/互斥或其他一些机制来避免这种情况。它会变得复杂而且难以调试。

我有一个主进程读取该文件,并将行分配给子进程池。您可以使用共享内存来加快速度,并减少对数据复制IPC的需求;或使用线程。

至于示例,我回答了一个关于分叉和IPC的问题,并给出了一个示例函数的代码片段,该函数分叉并返回一对管道以进行父子通信。让我看一下(...)这里是= P Can popen() make bidirectional pipes like pipe() + fork()?

编辑:我一直在想这个= P.这是一个想法:

  • 让主进程生成子进程,其类似于我在上面链接中显示的内容。

  • 每个流程都首先向主设备发送一个字节,表明它已准备好,并在read()上阻止。

  • 让主进程从文件读取一行到共享内存缓冲区,并在其子管道上阻塞select()

  • select()返回时,读取其中一个表示准备就绪的字节,并向该子进程发送共享内存空间中行的偏移量。

  • 主进程重复(读取一行,选择块,读取一个字节以消耗就绪事件等)

  • 孩子们以你需要的方式处理这条线,然后再向主人发送一个字节,以便再次发出信号准备状态。

(如果你愿意,你可以避免使用共享内存缓冲区,并将这些行发送到管道中,尽管它会涉及持续的数据复制。如果每行的处理计算成本很高,它就不会真正成为差异;但如果线条需要很少的处理,它可能会减慢你的速度。)

我希望这有帮助!

根据Newba的评论编辑2:

好的,所以没有共享内存。使用上面的模型,只代替向下发送管道读取共享内存空间中读取的行的偏移​​量,发送整行。这可能听起来像你在浪费时间,只能从文件中读取它,但相信我,你不是。管道比从硬盘中的常规文件读取快几个数量级,如果您希望子进程直接从文件读取,您将遇到我在答案开头指出的问题。

所以,掌握过程:

  • 使用我写的函数(上面的链接)之类的东西生成子进程,它创建用于双向通信的管道。

  • 从文件中读取一行到缓冲区(私有,本地,无共享内存)。

  • 您现在可以处理数据了。调用select()来阻止与子进程通信的所有管道。

  • 选择任何有可用数据的管道,从中读取一个字节,然后将等待处理的行发送到相应管道的缓冲区中(记住,我们每个子进程有2个,上升,一下去。)

  • 从步骤2开始重复,即读取另一行。

子进程:

  • 当他们开始时,他们有一个阅读管和一个书写管可供他们使用。向写入管道发送一个字节,以指示您准备好的主进程并等待处理数据(这是我们在上面的步骤4中读取的单个字节)。

  • 阻止read(),等待主进程(知道您已准备好因为步骤1)将数据发送到进程。继续阅读,直到你到达换行符(你说你正在读行,对吧?)。注意我正在关注您的模型,一次向每个进程发送一行,如果您愿意,可以发送多行。

  • 处理数据。

  • 返回步骤1,即发送另一个字节表示您已准备好接收更多数据。

您可以使用简单的协议将任务分配给任意数量的子进程。对一个孩子进行测试可能很有趣,n个孩子(其中n是计算机中的核心数)和超过n个孩子,并比较性能。

哇,这是一个很长的答案。我真的希望我能帮助xD

答案 1 :(得分:0)

由于每个进程都必须完整地读取文件(除非日志行的长度都相同,这是不寻常的),因此提案2确实没有任何好处。

如果你要将作品分成3部分,那么我会这样做:

  • 测量(stat())日志文件的大小 - 称之为N字节。
  • 将字节范围0 ..(N / 3)分配给第一个孩子。
  • 将字节范围(N / 3)+ 1..2(N / 3)分配给第二个孩子。
  • 将字节范围2(N / 3)+ 1..end分配给第三个孩子。
  • 定义第二个和第三个孩子必须通过向前读到他们的起始位置后的第一个换行符来同步。
  • 定义每个孩子负责在其范围结束时或之后阅读第一个换行符。
  • 请注意,如果日志文件正在增长,第三个孩子(最后一个孩子)可能需要做更多工作。

然后进程正在读取文件的独立段。

(当然,与他们共享文件,然后系统缓冲池保存重新读取磁盘,但数据仍然复制到三个进程中的每一个,只是让每个进程丢弃2/3的是复制为别人的工作。)


另一个更激进的选择:

  • mmap()将日志文件存入内存。
  • 按照前面描述的行将子项分配到文件的不同段。

如果您使用的是64位计算机,则效果非常好。如果您的日志文件不是太大(例如1 GB或更少),您也可以在32位计算机上执行此操作。当文件大小超过1 GB左右时,您可能会开始遇到内存映射和分配问题,尽管在达到小于4 GB(在32位计算机上)的大小之前,您可能会远离它。这里的另一个问题是日益增长的日志文件。 AFAIK,mmap()不会映射额外的内存,因为额外的数据写入文件。

答案 2 :(得分:0)

使用主从队列模式。

  • 主设备设置等待队列中的工作项的从设备。
  • 然后,主机逐行读取文件。
    • 然后,每一行代表您放入队列的工作项目 用函数指针指出工作原理。
    • 等待从属中的一个然后获取队列的项目
    • 奴隶处理工作项目。
    • 当奴隶完成后,它会重新加入工作队列。