use*_*719 5 linux embedded arm atomic rs485
我已经努力搜索(在 S[O|F|U] 网络和其他地方),并认为这是一个不常见的问题。我正在使用运行 Debian Linux 2.6.28-4 的 Atmel AT91SAM9263-EK 开发板(ARM926EJ-S 内核,ARMv5 指令集)。我正在使用(我相信)tty 驱动程序与RS-485 串行控制器通信。我需要确保写入和读取是原子的。几行源代码(在本文末尾相对于内核源安装目录列出)暗示或隐含地说明了这一点。
有什么方法可以验证向/从该设备写入/读取实际上是原子操作吗?或者,/dev/ttyXX 设备是否被认为是 FIFO 并且参数在那里结束?仅仅相信代码正在执行它提出的这个声明似乎还不够——就在今年 2 月,freebsd 被证明缺乏小行的原子写入. 是的,我意识到 freebsd 与 Linux 并不完全相同,但我的观点是仔细确定并没有什么坏处。我能想到的就是继续发送数据并寻找排列 - 我希望有一些更科学的东西,理想情况下是确定性的。不幸的是,我对过去大学时代的并发编程课程一无所知。我会非常感谢在正确的方向上一记耳光或一推。如果您选择回复,请提前致谢。
亲切的问候,
杰斯
驱动程序/字符/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;
}
arch/arm/include/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 write(3) 文档:
尝试写入管道或 FIFO 有几个主要特征:
- 原子/非原子:如果在一个操作中写入的全部量不与来自任何其他进程的数据交错,则写入是原子的。当有多个写入器向单个读取器发送数据时,这很有用。应用程序需要知道可以预期以原子方式执行的写入请求有多大。此最大值称为 {PIPE_BUF}。本卷 IEEE Std 1003.1-2001 没有说明超过 {PIPE_BUF} 字节的写请求是否是原子的,但要求 {PIPE_BUF} 或更少字节的写是原子的。
我认为,从技术上讲,设备不是 FIFO,因此根本不清楚您引用的保证是否适用。
您是否担心进程内的部分写入和读取,或者您实际上是否从不同进程读取和/或写入同一设备?假设是后者,您最好实现某种代理进程。代理独占该设备并执行所有读取和写入,从而完全避免了多进程原子性问题。
简而言之,我建议不要尝试验证“从此设备读取/写入实际上是原子操作”。如果较新版本的 linux(或完全不同的操作系统)无法按照您需要的方式实现原子性,那么这将很难充满信心地完成,并且您的应用程序可能会出现微妙的故障。