该read和write功能(和亲戚一样send,recv,readv,...)可以在其他情况下,返回一个数字小于所请求的读/写字节长度,如果信号中断(在某些情况下),也许太.是否存在明确定义的条件,以确定何时会发生这种情况,还是在很大程度上取决于实施?以下是我对以下答案感兴趣的一些特殊问题:
SA_RESTART),则会在信号处理程序返回后重新传输任何数据之前导致IO操作中断.但是如果已经发生了部分读/写并且信号处理程序是非中断的,那么系统调用是否会立即以部分长度返回,还是会恢复尝试读/写余数?我会对所有三种标准 - 所需,常见和特定于Linux的行为感兴趣.
标准用法pthread_atfork应该是在 pre-fork 处理程序中获取所有锁,并在父处理程序和子处理程序中释放它们。然而,据我所知,这是不可能的。pthread_mutex_unlock如果调用线程不是互斥锁的所有者,则指定为具有未定义的行为(在正常或默认类型互斥锁的情况下)或失败(在递归或错误检查互斥锁的情况下)。并且在注册的子处理程序中pthread_atfork,调用线程是新创建进程的主线程,因此不能是互斥锁的所有者。
是我弄错了还是整个pthread_atfork习语被设计破坏了并且基本上无法使用?
编辑:我也没有看到针对该问题的任何有效(便携式)解决方法。理想情况下,可以在子进程中销毁并重新初始化互斥锁,除了调用pthread_mutex_destroy已初始化的互斥锁被指定为未定义行为,以适应其中互斥锁不是 POD 但涉及对某些内核级对象的引用的荒谬实现。
什么是C99的长双数学库函数(可用便携式的实现expl,cosl,logl等),如果有的话?我看过fdlibm(基于Sun),NetBSD(基于UCB)等来源并没有看到它们.
strtol概念上,规范将输入字符串分为"初始空格","主题序列"和"最终字符串",并将"主题序列"定义为:
输入字符串的最长初始子序列,从第一个具有预期形式的非空白字符开始.如果输入字符串为空或完全由空格字符组成,或者第一个非空白字符不是符号或允许的字母或数字,则主题序列不应包含任何字符.
有一次,我认为"最长的初始子序列"业务类似于scanf工作方式,在哪里"0x@"扫描"0x",失败的匹配,然后"@"作为下一个未读的角色.然而,在经过一些讨论之后,我基本上确信strtol处理了预期形式的最长初始子序列,而不是最长的初始字符串,它是预期形式的某些可能字符串的初始子序列.
令我困惑的是规范中的这种语言:
如果主题序列为空或者没有预期的形式,则不进行转换; str的值存储在endptr指向的对象中,前提是endptr不是空指针.
如果我们接受似乎是"主题序列"的正确定义,那么就没有不具有预期形式的非空主题序列,而是(为了避免冗余和混淆)文本应该只读:
如果主题序列为空,则不执行转换; str的值存储在endptr指向的对象中,前提是endptr不是空指针.
谁能为我澄清这些问题?也许与过去的讨论或任何相关缺陷报告的链接将是有用的.
注意:在公开集思广益之后,我已经大量编辑了这个问题.然而,所描述的实际算法以及关于它们是否足以避免比赛的问题应该是相同的.
我正在尝试实现信号量,避免glibc错误号12674中描述的竞争条件:
http://sourceware.org/bugzilla/show_bug.cgi?id=12674
基本上,如果你不关心这种破坏的竞争条件,写一个基于futex的信号量的有效方法是:
帖子:
等待:
后期操作的第2步是不安全的.
有两个明显的实现可以避免这个问题,但两者都存在重大问题:
第一种解决方案是不存储服务员计数或标志,并始终在帖子上执行futex唤醒.这显然是安全的,但却违背了futexes的全部目的(在用户空间中保持无竞争的情况).
第二种解决方案不是存储服务员计数,而是在等待争用时让信号量值减少到-1.然后,post操作从-1转换为1并唤醒所有服务员.其中一个成功将信号量值递减为0,如果有剩余,则将值设置为-1,因此下一个帖子将执行另一个唤醒.这个解决方案显然也是安全的,但是当它发布时,它会导致争夺信号量的线程踩踏.
总之,第一种解决方案仅适用于总是争用的信号量,而后者仅适用于通常不超过一名服务员的信号量.一般用途都不可接受.
现在,尝试一个真正的解决方案......
此时,应该注意的是,还有一些其他要求使现实世界的实施变得复杂.后期操作需要是异步信号安全的(因此它基本上不能使用锁),并且需要等待操作以允许信号,超时或线程取消中断.实际上,这意味着后期操作必须能够安全地"退出"它对信号量状态所做的任何更改.我已经掩饰了这些问题,因为我的方法似乎对他们没有任何问题,但他们确实做出了一些其他明显的变化/解决方案,所以任何人建议新方法作为答案都应该意识到这些问题......
我有一个建议的解决方案,但我不确定它是否会受到新的(可能更糟)的竞争条件的影响.我将在这里描述它,我希望一些并发神(或者至少是半神人)可能有善意去审查它的正确性.
我的方法是上面描述的第二个"坏"解决方案和原始方法(在glibc错误报告中描述的竞争)的混合.它使用服务器计数和服务器标志(-1存储在信号量值中).
等待操作的关键更改是,只要有服务员(预先存在的服务员计数或本身作为新的服务员),它就会在信号量值中存储-1而不是0.
等待:
对post操作的关键更改是它在递增信号量值之前执行对服务器计数的读取,而不是之后:
帖子:
步骤4中的比较和交换提供了服务员计数仍然正确的一些安全性,但仍然存在ABA竞争 - 在步骤1和4之间,其他线程可能执行等待和后续操作,使信号量值保持不变作为其初始值.
在查找此算法可能无法唤醒服务器的情况时,我们只需考虑初始服务器计数读取为0且读取的信号量值不为-1的情况.此外,如果信号量值为正并且没有预先存在的服务员,则当前帖子不对任何唤醒负责,因此这种情况也不感兴趣.我们还在研究等待操作以零信号量值和零等待计数开始的情况.在这种情况下,为了不具有竞争条件,在步骤2和4之间发生的导致新服务员的任何事件必须改变信号量值,以便步骤4中的比较和交换失败.显然,任何单个插入的帖子或等待都将改变信号量值(分别为1或-1),因此更具体地说,关注的情况是导致信号量值为0但存在服务员的操作序列.
我认为由于等待操作中遵循的程序不会发生这种情况,但我并没有100%相信自己.
最后,这里有一些比赛的例子,如果你削弱了我的算法,为了确定它正在做什么的动机,如果不清楚的话.
失败1:使用纯等待计数,信号量值中没有-1标志.琐碎的比赛看起来像:
失败2:使用服务器计数并让新服务员将信号量值设置为-1,但是等待成功时简单地减少信号量值(如果其他线程仍在等待,则不将其设置为-1):
我遇到了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) 根据POSIX,
销毁当前没有线程被阻塞的初始化条件变量应该是安全的.
此外,指定信号和广播操作以解除阻塞在条件变量上阻塞的一个/所有线程.
因此,在我看来,以下形式的自同步破坏应该是有效的,即pthread_cond_destroy:
当然,假设没有其他服务员到达,之后不再执行任何信号,如果使用,应用程序负责保证pthread_cond_destroy.
我是否认为破坏在这些情况下是有效的?还有其他自我同步的破坏场景需要注意条件变量吗?
最后,对于在没有破坏的情况下取消映射共享映射的进程共享cond变量可能是有意义的,期望取消映射在相同的上下文中有效是合理的,破坏是有效的,或者如果同一进程中的多个线程必须进一步同步(地址空间)使用相同的映射,并希望在上述某个上下文中取消映射?
该glob函数有一个GLOB_MARK标志,指定为对目标结果附加斜杠:
GLOB_MARK
作为匹配模式的目录的每个路径名都应
<slash>附加一个.
(来源:http://pubs.opengroup.org/onlinepubs/9699919799/functions/glob.html)
但是,据我所知,没有提供有关此功能如何工作的更多详细信息.特别是,如果结果本身不是目录,而是目录的符号链接,是否应该附加斜杠?glibc实现就是这样做的.
我知道这是一个难以回答的问题,因为标准的简洁性glob,所以很好的答案将引用历史实践,历史标准或POSIX以外的文档,可能进一步指明行为glob等.提出理由的答案为什么一种行为或另一种行为更有用也会很有趣.
我刚刚#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?
我知道以下类似的问题,但它似乎并不完全相同,答案不能令人满意地解决我的问题:
Linux 2.6.39引入了O_PATH开放模式,(粗略地说)根本没有真正打开文件(即不创建开放文件描述),而只是提供了一个文件描述符,它是未打开目标的句柄.它的主要用途是作为*at函数(openat等)的参数,它似乎适合作为O_SEARCHLinux以前缺少的POSIX 2008 功能的实现.但是,我一直无法找到关于其确切语义的任何好文档O_PATH.我有几个具体问题:
O_PATH文件描述符可以进行哪些操作?(只有*at功能?)O_PATH与非目录永远有用吗?O_PATH文件描述符是否计为一个引用,以防止在取消链接最后一个链接时释放该对象?等等.