小编R..*_*R..的帖子

可以进行短读/写的条件是什么?

该read和write功能(和亲戚一样send,recv,readv,...)可以在其他情况下,返回一个数字小于所请求的读/写字节长度,如果信号中断(在某些情况下),也许太.是否存在明确定义的条件,以确定何时会发生这种情况,还是在很大程度上取决于实施?以下是我对以下答案感兴趣的一些特殊问题:

  • 如果信号处理程序是非中断(SA_RESTART),则会在信号处理程序返回后重新传输任何数据之前导致IO操作中断.但是如果已经发生了部分读/写并且信号处理程序是非中断的,那么系统调用是否会立即以部分长度返回,还是会恢复尝试读/写余数?
  • 显然,当可用的数据少于请求数量时,读取函数可以返回网络,管道和终端文件描述符上的短读取.但是由于缓冲区大小有限,写入函数可以在这些情况下返回短写入,还是会阻塞直到所有数据都被写入?

我会对所有三种标准 - 所需,常见和特定于Linux的行为感兴趣.

c posix signals

6
推荐指数
1
解决办法
438
查看次数

pthread_atfork 锁定习惯用法坏了?

标准用法pthread_atfork应该是在 pre-fork 处理程序中获取所有锁,并在父处理程序和子处理程序中释放它们。然而,据我所知,这是不可能的。pthread_mutex_unlock如果调用线程不是互斥锁的所有者,则指定为具有未定义的行为(在正常或默认类型互斥锁的情况下)或失败(在递归或错误检查互斥锁的情况下)。并且在注册的子处理程序中pthread_atfork,调用线程是新创建进程的主线程,因此不能是互斥锁的所有者。

是我弄错了还是整个pthread_atfork习语被设计破坏了并且基本上无法使用?

编辑:我也没有看到针对该问题的任何有效(便携式)解决方法。理想情况下,可以在子进程中销毁并重新初始化互斥锁,除了调用pthread_mutex_destroy已初始化的互斥锁被指定为未定义行为,以适应其中互斥锁不是 POD 但涉及对某些内核级对象的引用的荒谬实现。

c posix fork pthreads

6
推荐指数
1
解决办法
571
查看次数

长双数学库实现?

什么是C99的长双数学库函数(可用便携式的实现expl,cosl,logl等),如果有的话?我看过fdlibm(基于Sun),NetBSD(基于UCB)等来源并没有看到它们.

c math c99 long-double

6
推荐指数
1
解决办法
1358
查看次数

在strtol等人的规范中混淆语言

strtol概念上,规范将输入字符串分为"初始空格","主题序列"和"最终字符串",并将"主题序列"定义为:

输入字符串的最长初始子序列,从第一个具有预期形式的非空白字符开始.如果输入字符串为空或完全由空格字符组成,或者第一个非空白字符不是符号或允许的字母或数字,则主题序列不应包含任何字符.

有一次,我认为"最长的初始子序列"业务类似于scanf工作方式,在哪里"0x@"扫描"0x",失败的匹配,然后"@"作为下一个未读的角色.然而,在经过一些讨论之后,我基本上确信strtol处理了预期形式的最长初始子序列,而不是最长的初始字符串,它是预期形式的某些可能字符串的初始子序列.

令我困惑的是规范中的这种语言:

如果主题序列为空或者没有预期的形式,则不进行转换; str的值存储在endptr指向的对象中,前提是endptr不是空指针.

如果我们接受似乎是"主题序列"的正确定义,那么就没有不具有预期形式的非空主题序列,而是(为了避免冗余和混淆)文本应该只读:

如果主题序列为空,则不执行转换; str的值存储在endptr指向的对象中,前提是endptr不是空指针.

谁能为我澄清这些问题?也许与过去的讨论或任何相关缺陷报告的链接将是有用的.

c standards-compliance language-lawyer strtol

6
推荐指数
1
解决办法
226
查看次数

信号量没有破坏/解除竞争条件

注意:在公开集思广益之后,我已经大量编辑了这个问题.然而,所描述的实际算法以及关于它们是否足以避免比赛的问题应该是相同的.

我正在尝试实现信号量,避免glibc错误号12674中描述的竞争条件:

http://sourceware.org/bugzilla/show_bug.cgi?id=12674

基本上,如果你不关心这种破坏的竞争条件,写一个基于futex的信号量的有效方法是:

帖子:

  1. 原子增量信号量值.
  2. 检查服务员数量.如果它非零,则执行futex唤醒.

等待:

  1. 信号量值的原子递减 - 如果为正(如果成功则返回).
  2. 如果失败,请增加服务员并执行互斥等待.
  3. 在唤醒时,减少服务员,循环并重试.

后期操作的第2步是不安全的.

有两个明显的实现可以避免这个问题,但两者都存在重大问题:

第一种解决方案是不存储服务员计数或标志,并始终在帖子上执行futex唤醒.这显然是安全的,但却违背了futexes的全部目的(在用户空间中保持无竞争的情况).

第二种解决方案不是存储服务员计数,而是在等待争用时让信号量值减少到-1.然后,post操作从-1转换为1并唤醒所有服务员.其中一个成功将信号量值递减为0,如果有剩余,则将值设置为-1,因此下一个帖子将执行另一个唤醒.这个解决方案显然也是安全的,但是当它发布时,它会导致争夺信号量的线程踩踏.

总之,第一种解决方案仅适用于总是争用的信号量,而后者仅适用于通常不超过一名服务员的信号量.一般用途都不可接受.

现在,尝试一个真正的解决方案......

此时,应该注意的是,还有一些其他要求使现实世界的实施变得复杂.后期操作需要是异步信号安全的(因此它基本上不能使用锁),并且需要等待操作以允许信号,超时或线程取消中断.实际上,这意味着后期操作必须能够安全地"退出"它对信号量状态所做的任何更改.我已经掩饰了这些问题,因为我的方法似乎对他们没有任何问题,但他们确实做出了一些其他明显的变化/解决方案,所以任何人建议新方法作为答案都应该意识到这些问题......

我有一个建议的解决方案,但我不确定它是否会受到新的(可能更糟)的竞争条件的影响.我将在这里描述它,我希望一些并发神(或者至少是半神人)可能有善意去审查它的正确性.

我的方法是上面描述的第二个"坏"解决方案和原始方法(在glibc错误报告中描述的竞争)的混合.它使用服务器计数和服务器标志(-1存储在信号量值中).

等待操作的关键更改是,只要有服务员(预先存在的服务员计数或本身作为新的服务员),它就会在信号量值中存储-1而不是0.

等待:

  1. 读取信号量值.
  2. 如果值为正,则按如下方式确定新的信号量值:如果值正好为1并且有等待者,则新值应为-1; 否则只是递减旧值.使用compare-and-swap更新值,如果成功则返回成功.
  3. 否则,递增等待者,用-1原子地将值0替换为0,并以-1作为值执行futex等待.
  4. 在唤醒时,减少服务员,循环并重试.

对post操作的关键更改是它在递增信号量值之前执行对服务器计数的读取,而不是之后:

帖子:

  1. 读取并保存信号量值.
  2. 阅读并保存服务员人数.
  3. 确定新的信号量值(-1变为1,否则只是增量).
  4. 原子比较和交换以更新信号量值.失败时,转到1.
  5. 如果保存的服务员计数非零或信号量值为-1,则执行futex wake.

步骤4中的比较和交换提供了服务员计数仍然正确的一些安全性,但仍然存在ABA竞争 - 在步骤1和4之间,其他线程可能执行等待和后续操作,使信号量值保持不变作为其初始值.

在查找此算法可能无法唤醒服务器的情况时,我们只需考虑初始服务器计数读取为0且读取的信号量值不为-1的情况.此外,如果信号量值为正并且没有预先存在的服务员,则当前帖子不对任何唤醒负责,因此这种情况也不感兴趣.我们还在研究等待操作以零信号量值和零等待计数开始的情况.在这种情况下,为了不具有竞争条件,在步骤2和4之间发生的导致新服务员的任何事件必须改变信号量值,以便步骤4中的比较和交换失败.显然,任何单个插入的帖子或等待都将改变信号量值(分别为1或-1),因此更具体地说,关注的情况是导致信号量值为0但存在服务员的操作序列.

我认为由于等待操作中遵循的程序不会发生这种情况,但我并没有100%相信自己.


最后,这里有一些比赛的例子,如果你削弱了我的算法,为了确定它正在做什么的动机,如果不清楚的话.

失败1:使用纯等待计数,信号量值中没有-1标志.琐碎的比赛看起来像:

  1. 信号量值从0开始
  2. 线程1开始发布,读取0信号量值和0等待计数.
  3. 线程2开始等待,增加等待计数和futex等待.
  4. 线程1执行成功的比较和交换,返回而不唤醒服务员.

失败2:使用服务器计数并让新服务员将信号量值设置为-1,但是等待成功时简单地减少信号量值(如果其他线程仍在等待,则不将其设置为-1):

  1. 信号量值从0开始
  2. 线程1开始发布,读取0信号量值和0等待计数.
  3. 线程2和3等待,递增等待计数和futex等待.
  4. 线程4帖子,将信号量值设置为1.
  5. 线程2唤醒并将信号量值减少为0,服务器计数为1.
  6. 线程1执行成功的比较和交换,返回而不唤醒线程3.

c linux semaphore pthreads race-condition

6
推荐指数
1
解决办法
1020
查看次数

Linux futex系统调用虚假唤醒,返回值为0?

我遇到了Linux futex系统调用(FUTEX_WAIT操作)的问题,有时候看起来很早就没有原因.文档指定了可能导致它提前返回的某些条件(没有a FUTEX_WAKE),但这些条件都涉及非零返回值:EAGAIN如果futex地址的值不匹配,则ETIMEDOUT定时等待超时,EINTR当被a(非但是我看到返回值为0.除了指针指向futex FUTEX_WAKE的线程的终止之外,返回值为0的原因是什么?set_tid_addressFUTEX_WAIT

如果它有用,我正在等待的特定futex是线程tid地址(由clonesyscall 设置CLONE_CHILD_CLEARTID),并且线程没有终止.我的(显然是不正确的)假设FUTEX_WAIT操作返回0只能在线程终止时导致程序逻辑出现严重错误,我已经通过循环和重试来修复,即使它返回0,但现在我很好奇为什么会这样.

这是一个最小的测试用例:

#define _GNU_SOURCE
#include <sched.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <linux/futex.h>
#include <signal.h>

static char stack[32768];
static int tid;

static int foo(void *p)
{
        syscall(SYS_getpid);
        syscall(SYS_getpid);
        syscall(SYS_exit, 0);
}

int main()
{
        int pid = getpid();
        for (;;) {
                int x = clone(foo, stack+sizeof stack,
                        CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND
                        |CLONE_THREAD|CLONE_SYSVSEM //|CLONE_SETTLS
                        |CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID
                        |CLONE_DETACHED,
                        0, &tid, 0, …
Run Code Online (Sandbox Code Playgroud)

c linux futex

6
推荐指数
1
解决办法
2053
查看次数

何时可以使用cond var来同步自己的销毁/取消映射?

根据POSIX,

销毁当前没有线程被阻塞的初始化条件变量应该是安全的.

此外,指定信号和广播操作以解除阻塞在条件变量上阻塞的一个/所有线程.

因此,在我看来,以下形式的自同步破坏应该是有效的,即pthread_cond_destroy:

  1. 在成功的信号之后,在等待或信令线程中,当cond var上只有一个线程被阻塞时.
  2. 在成功广播之后,立即在任何等待线程或广播线程中.

当然,假设没有其他服务员到达,之后不再执行任何信号,如果使用,应用程序负责保证pthread_cond_destroy.

我是否认为破坏在这些情况下是有效的?还有其他自我同步的破坏场景需要注意条件变量吗?

最后,对于在没有破坏的情况下取消映射共享映射的进程共享cond变量可能是有意义的,期望取消映射在相同的上下文中有效是合理的,破坏是有效的,或者如果同一进程中的多个线程必须进一步同步(地址空间)使用相同的映射,并希望在上述某个上下文中取消映射?

c synchronization posix pthreads condition-variable

6
推荐指数
1
解决办法
695
查看次数

使用GLOB_MARK的glob是否应附加/到symlink-to-directory结果?

该glob函数有一个GLOB_MARK标志,指定为对目标结果附加斜杠:

GLOB_MARK

作为匹配模式的目录的每个路径名都应<slash>附加一个.

(来源:http://pubs.opengroup.org/onlinepubs/9699919799/functions/glob.html)

但是,据我所知,没有提供有关此功能如何工作的更多详细信息.特别是,如果结果本身不是目录,而是目录的符号链接,是否应该附加斜杠?glibc实现就是这样做的.

我知道这是一个难以回答的问题,因为标准的简洁性glob,所以很好的答案将引用历史实践,历史标准或POSIX以外的文档,可能进一步指明行为glob等.提出理由的答案为什么一种行为或另一种行为更有用也会很有趣.

c posix glob language-lawyer

6
推荐指数
1
解决办法
382
查看次数

如何广泛支持pragma weak,它是否克服了使用gcc属性的问题?

我刚刚#pragma weak在GCC中发现了该指令:

6.57.9弱的语用

为了与SVR4兼容,GCC支持一组#pragma指令,用于声明符号弱,并定义弱别名.

#pragma weak symbol

该pragma声明符号为弱,就好像声明具有相同名称的属性一样.该编译指示可能出现在符号声明之前或之后.符号永远不会被定义,这不是错误.

#pragma weak symbol1 = symbol2

该pragma将symbol1声明为symbol2的弱别名.如果未在当前转换单元中定义symbol2,则会出错.

http://gcc.gnu.org/onlinedocs/gcc/Weak-Pragmas.html

尽管GCC开发人员通常不喜欢#pragma并鼓励您使用__attribute__可能是pragma的各种事物,但我倾向于认为#pragma weak可能实际上优于基于属性的方法,它看起来像:

extern __typeof(old_name) new_name __attribute__(weak, alias("old_name"))
Run Code Online (Sandbox Code Playgroud)

除了要求的丑陋__typeof(或要求你知道的类型和拼写出来明确,即使这是一个非常复杂的函数类型),属性为基础的方法最大的问题是,"old_name"必须通过与gcc 作为一个字符串是从字面上粘贴到生成的程序集中.这是有问题的,因为不同的系统具有不同的名称修改特性(最流行的是在所有C符号名称前加下划线,或根本不执行任何操作),并且要将正确的字符串传递给alias属性,您需要知道名称修改约定您正在构建的系统,这实际上不是属于应用程序级库的知识,其中弱别名可能有用.

语法#pragma weak new_name = old_name似乎通过在编译器级别处理这两个名称来避免这个问题,其中t可以适当地修改它们,除非我弄错了.

所有的预赛都完成后,我的实际问题是:我是否错误地认为#pragma weak这具有"便携性"优势?并且所有类似unix的系统上的现代编译器(gcc,pcc,tinycc,icc,llvm/clang等)是否仍然支持传统的SVR4 #pragma weak?

我知道以下类似的问题,但它似乎并不完全相同,答案不能令人满意地解决我的问题:

弱连接的便携性如何?#pragma weak my_symbol

c gcc pragma

6
推荐指数
1
解决办法
3214
查看次数

Linux O_PATH文件描述符的语义?

Linux 2.6.39引入了O_PATH开放模式,(粗略地说)根本没有真正打开文件(即不创建开放文件描述),而只是提供了一个文件描述符,它是未打开目标的句柄.它的主要用途是作为*at函数(openat等)的参数,它似乎适合作为O_SEARCHLinux以前缺少的POSIX 2008 功能的实现.但是,我一直无法找到关于其确切语义的任何好文档O_PATH.我有几个具体问题:

  1. Linux O_PATH文件描述符可以进行哪些操作?(只有*at功能?)
  2. 是O_PATH与非目录永远有用吗?
  3. 如何将文件描述符绑定到底层文件系统对象,以及如果它被移动,删除等会发生什么?O_PATH文件描述符是否计为一个引用,以防止在取消链接最后一个链接时释放该对象?等等.

c linux posix

6
推荐指数
1
解决办法
1904
查看次数