如何验证原子写入?

时间:2009-12-28 18:25:37

标签: linux embedded arm atomic rs485

我努力寻找(在S [O | F | U]网络内和其他地方),并认为这是一个不寻常的问题。我正在使用运行Debian Linux 2.6.28-4的Atmel AT91SAM9263-EK开发板(ARM926EJ-S内核,ARMv5指令集)。我正在使用(我相信)tty驱动程序与RS-485 serial controller交谈。我需要确保写入和读取是原子的。几行源代码(在本文末尾列出相对于内核源代码安装目录的内容)暗示或隐含地说明了这一点。

有什么方法可以验证这个设备的写入/读取实际上是一个原子操作吗?或者,/ dev / ttyXX设备是否被视为FIFO并且参数在那里结束?仅仅相信代码正在强制执行这一主张似乎还不够 - 就像今年2月份freebsd demonstrated to lack atomic writes for small lines一样。是的,我意识到freebsd与Linux并不完全相同,但我的观点是,小心谨慎并不会有害。我能想到的只是继续发送数据并寻找排列 - 我希望能有一些更科学的东西,理想情况下是确定性的。不幸的是,我记得在大学时代的同时编程课程中没有任何东西。我会非常欣赏正确方向的一记耳光或一拳。如果您选择回复,请提前感谢。

亲切的问候,

Jayce


的驱动/焦炭/ tty_io.c中:1087

void tty_write_message(struct tty_struct *tty, char *msg)
{
    lock_kernel();
    if (tty) {
        mutex_lock(&tty->atomic_write_lock);
        if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags))
            tty->ops->write(tty, msg, strlen(msg));
        tty_write_unlock(tty);
    }
    unlock_kernel();
    return;
}


拱/臂/包括/ ASM / bitops.h:37

static inline void ____atomic_set_bit(unsigned int bit, volatile unsigned long *p)
{
    unsigned long flags;
    unsigned long mask = 1UL << (bit & 31);

    p += bit >> 5;

    raw_local_irq_save(flags);
    *p |= mask;
    raw_local_irq_restore(flags);
}


驱动器/串行/ serial_core.c:2376

static int
uart_write(struct tty_struct *tty, const unsigned char *buf, int count)
{
    struct uart_state *state = tty->driver_data;
    struct uart_port *port;
    struct circ_buf *circ;
    unsigned long flags;
    int c, ret = 0;

    /*
     * This means you called this function _after_ the port was
     * closed.  No cookie for you.
     */
    if (!state || !state->info) {
        WARN_ON(1);
        return -EL3HLT;
    }

    port = state->port;
    circ = &state->info->xmit;

    if (!circ->buf)
        return 0;

    spin_lock_irqsave(&port->lock, flags);
    while (1) {
        c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
        if (count < c)
            c = count;
        if (c <= 0)
            break;
        memcpy(circ->buf + circ->head, buf, c);
        circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1);
        buf += c;
        count -= c;
        ret += c;
    }
    spin_unlock_irqrestore(&port->lock, flags);

    uart_start(tty);
    return ret;
}

另外,从man写的(3)文档:

  

尝试写入管道或FIFO有几个主要特征:

     
      
  • 原子/非原子:如果在一次操作中写入的总量与来自任何其他进程的数据不交错,则写入是原子的。当有多个写入器将数据发送到单个读取器时,这很有用。应用程序需要知道可以预期以原子方式执行写入请求的大小。此最大值称为{PIPE_BUF}。 IEEE Std 1003.1-2001的这一卷未说明超过{PIPE_BUF}字节的写请求是否是原子的,但要求{PIPE_BUF}或更少字节的写入必须是原子的。
  •   

2 个答案:

答案 0 :(得分:3)

我认为,从技术上讲,设备不是FIFO,所以你所引用的保证应该适用并不是很清楚。

您是否关注进程内的部分写入和读取,或者您是否实际从不同进程读取和/或编写相同的设备?假设后者,您可能最好实现某种代理进程。代理专有地拥有设备并执行所有读写操作,从而完全避免了多进程原子性问题。

简而言之,我建议不要试图验证“从这个设备读/写实际上是一个原子操作”。如果Linux的更高版本(或者完全不同的o / s)无法以您需要的方式实现原子性,那么您将很难自信地做一个应用程序,这个应用程序会受到微妙的失败。

答案 1 :(得分:2)

我认为PIPE_BUF是正确的。现在,少于PIPE_BUF字节的写入可能不是原子的,但如果它们不是,则它是操作系统错误。我想你可以在这里询问操作系统是否已知错误。但实际上,如果它有这样的错误,它应该立即修复。

如果你想以原子方式写PIPE_BUF以上,我认为你运气不好。我不认为在应用程序协调和合作之外还有任何方法可以确保更大规模的写入以原子方式发生。

此问题的一个解决方案是将您自己的流程放在设备前面,并确保每个想要写入设备的人都联系流程并将数据发送给它。然后,您可以在原子性保证方面为您的应用程序做任何有意义的事情。