如何验证原子写入?

发布于 2024-08-16 02:04:12 字数 2996 浏览 11 评论 0原文

我努力搜索(在 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 并不完全相同,但我的观点是,仔细确定并没有什么坏处。我能想到的就是继续发送数据并寻找排列 - 我希望得到一些更科学的东西,并且理想情况下是确定性的。不幸的是,我对以前大学时代的并发编程课程一无所知。我非常感谢朝正确方向的一巴掌或一推。如果您选择回复,请提前致谢。

亲切的问候,

杰斯


drivers/char/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);
}


drivers/serial/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} 或更少字节的写入应该是原子的。

I have searched diligently (both within the S[O|F|U] network and elsewhere) and believe this to be an uncommon question. I am working with an Atmel AT91SAM9263-EK development board (ARM926EJ-S core, ARMv5 instruction set) running Debian Linux 2.6.28-4. I am writing using (I believe) the tty driver to talk to an RS-485 serial controller. I need to ensure that writes and reads are atomic. Several lines of source code (listed below the end of this post relative to the kernel source installation directory) either imply or implicitly state this.

Is there any way I can verify that writing/reading to/from this device is actually an atomic operation? Or, is the /dev/ttyXX device considered a FIFO and the argument ends there? It seems not enough to simply trust that the code is enforcing this claim it makes - as recently as February of this year freebsd was demonstrated to lack atomic writes for small lines. Yes I realize that freebsd is not exactly the same as Linux, but my point is that it doesn't hurt to be carefully sure. All I can think of is to keep sending data and look for a permutation - I was hoping for something a little more scientific and, ideally, deterministic. Unfortunately, I remember precisely nothing from my concurrent programming classes in the college days of yore. I would thoroughly appreciate a slap or a shove in the right direction. Thank you in advance should you choose to reply.

Kind regards,

Jayce


drivers/char/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);
}



drivers/serial/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;
}

Also, from the man write(3) documentation:

An attempt to write to a pipe or FIFO has several major characteristics:

  • Atomic/non-atomic: A write is atomic if the whole amount written in one operation is not interleaved with data from any other process. This is useful when there are multiple writers sending data to a single reader. Applications need to know how large a write request can be expected to be performed atomically. This maximum is called {PIPE_BUF}. This volume of IEEE Std 1003.1-2001 does not say whether write requests for more than {PIPE_BUF} bytes are atomic, but requires that writes of {PIPE_BUF} or fewer bytes shall be atomic.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

桃扇骨 2024-08-23 02:04:12

我认为,从技术上讲,设备不是 FIFO,因此根本不清楚您引用的保证是否适用。

您是否担心进程内的部分写入和读取,或者您实际上是否从不同进程读取和/或写入同一设备?假设是后者,您最好实现某种代理进程。代理独占该设备并执行所有读取和写入,从而完全避免了多进程原子性问题。

简而言之,我建议不要尝试验证“从此设备读取/写入实际上是原子操作”。如果较新版本的 linux(或完全不同的操作系统)无法按照您需要的方式实现原子性,那么这将很难充满信心地完成,并且您的应用程序可能会出现微妙的故障。

I think that, technically, devices are not FIFOs, so it's not at all clear that the guarantees you quote are supposed to apply.

Are you concerned about partial writes and reads within a process, or are you actually reading and/or writing the same device from different processes? Assuming the latter, you might be better off implementing a proxy process of some sort. The proxy owns the device exclusively and performs all reads and writes, thus avoiding the multi-process atomicity problem entirely.

In short, I advise not attempting to verify that "reading/writing from this device is actually an atomic operation". It will be difficult to do with confidence, and leave you with an application that is subject to subtle failures if a later version of linux (or different o/s altogether) fails to implement atomicity the way you need.

姐不稀罕 2024-08-23 02:04:12

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

如果您想以原子方式写入超过 PIPE_BUF 的内容,我认为您不走运。我认为除了应用程序协调和合作之外,没有任何方法可以确保更大尺寸的写入以原子方式发生。

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

I think PIPE_BUF is the right thing. Now, writes of less than PIPE_BUF bytes may not be atomic, but if they aren't it's an OS bug. I suppose you could ask here if an OS has known bugs. But really, if it has a bug like that, it just ought to be immediately fixed.

If you want to write more than PIPE_BUF atomically, I think you're out of luck. I don't think there is any way outside of application coordination and cooperation to make sure that writes of larger sizes happen atomically.

One solution to this problem is to put your own process in front of the device and make sure everybody who wants to write to the device contacts the process and sends the data to it instead. Then you can do whatever makes sense for your application in terms of atomicity guarantees.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文