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

是无符号字符[4] [5]; 一个[1] [7]; 未定义的行为?

来自C标准的未定义行为的示例之一(J.2):

- 数组下标超出范围,即使一个对象显然可以使用给定的下标访问(如左边的表达式a [1] [7],给出声明int a [4] [5])(6.5.6)

如果声明从更改int a[4][5]为unsigned char a[4][5],访问a[1][7]仍会导致未定义的行为吗?我的意见是,它没有,但我从其他人那里听到了不同意见,我想看看其他一些想成为专家的想法.

我的推理:

  • 根据6.2.6.1第4段和第6.5段第7段的通常解释,对象的表示a是sizeof (unsigned char [4][5])*CHAR_BIT位,可以作为unsigned char [20]与对象重叠的类型数组进行访问.

  • a[1]将type unsigned char [5]作为左值,但在表达式中使用(作为运算[]符的操作数,或等效地作为运算+符的操作数*(a[1]+7)),它衰减为类型的指针unsigned char *.

  • 值a[1]也是指向a表单中"表示"的字节的指针unsigned char [20].以这种方式解释,添加7 a[1]是有效的.

c arrays strict-aliasing undefined-behavior

9
推荐指数
1
解决办法
670
查看次数

在用户空间中实现可取消的系统调用

我正在努力在Linux上实现pthread取消,而不会在我最近的一些其他问题中讨论任何"不愉快的行为"(有些人可能会说错误).到目前为止,取消pthread取消的Linux/glibc方法一直将它视为不需要内核支持的东西,并且可以在库级别处理,纯粹通过在进行系统调用之前启用异步取消,并恢复先前的取消状态在系统调用返回后.这至少有两个问题,其中一个非常严重:

  1. 系统调用从内核空间返回后,但在用户空间保存返回值之前,取消可以执行.如果系统调用分配了资源,则会导致资源泄漏,并且无法使用取消处理程序对其进行修补.
  2. 如果在线程被可取消的系统调用阻塞时处理信号,则整个信号处理程序在启用异步取消的情况下运行.这可能是非常危险的,因为信号处理函数可以调用它们是异步信号安全的,但不是异步取消安全功能.

我解决问题的第一个想法是设置一个标志,线程处于取消点,而不是启用异步取消,并且当设置此标志时,让取消信号处理程序检查保存的指令指针,看它是否指向系统调用指令(特定于arch).如果是这样,这表示系统调用未完成,并且在信号处理程序返回时将重新启动,因此我们可以取消.如果没有,我认为系统调用已经返回,并推迟取消.但是,还存在竞争条件 - 线程可能根本没有到达syscall指令,在这种情况下,系统调用可能会阻塞并且永远不会响应取消.另一个小问题是,如果在输入信号处理程序时设置了取消点标志,则从信号处理程序执行的不可取消的系统调用会被错误地取消.

我正在寻找一种新的方法,并寻找有关它的反馈.必须满足的条件:

  • 在系统调用完成之前收到的任何取消请求必须在系统调用阻塞任何重要时间间隔之前执行,但不会因为信号处理程序中断而挂起重启.
  • 完成系统调用后收到的任何取消请求必须推迟到下一个取消点.

我想到的想法需要为可取消的系统调用包装器进行专门的组装.基本想法是:

  1. 将即将发生的系统调用指令的地址压入堆栈.
  2. 将堆栈指针存储在线程本地存储中.
  3. 从线程本地存储测试取消标志; 如果已设置,则跳转到取消例程.
  4. 进行系统调用.
  5. 清除线程本地存储中保存的指针.

取消操作将涉及:

  1. 在目标线程的线程本地存储中设置取消标志.
  2. 测试目标线程的线程本地存储中的指针; 如果它不为空,则向目标线程发送取消信号.

取消信号处理程序然后:

  1. 检查保存的堆栈指针(在信号上下文中)是否等于线程本地存储中的已保存指针.如果没有,那么取消点被信号处理程序中断,现在没什么可做的.
  2. 检查程序计数器寄存器(保存在信号上下文中)是否小于或等于保存的堆栈指针中保存的地址.如果是这样,这意味着系统调用尚未完成,我们执行取消.

到目前为止我看到的唯一问题是信号处理程序的第1步:如果它决定不动作,那么在信号处理程序返回之后,线程可能会在系统调用上被阻塞,忽略待处理的取消请求.为此,我看到了两个可能的解决方案:

  1. 在这种情况下,安装一个计时器来向特定线程发送信号,基本上每毫秒左右重试一次,直到我们运气好.
  2. 再次提升消除信号,但是从取消信号处理器返回而不取消屏蔽消除信号.当中断的信号处理程序返回时,它将自动取消屏蔽,然后我们可以再试一次.但是,这可能会干扰信号处理程序中取消点的行为.

关于哪种方法最好的想法,或者是否还有其他更基本的缺陷?

c linux posix pthreads cancellation

9
推荐指数
1
解决办法
663
查看次数

普通字符在非二进制补码系统上通常/总是无符号吗?

显然,标准对此没有任何说明,但我对实际/历史观点更感兴趣:使用非二进制补码算法的系统是否使用了char无符号的普通类型?否则你可能有各种各样的怪异,比如null终结符的两个表示,以及无法表示所有"byte"值char.做这么奇怪的系统真的存在吗?

c twos-complement unsigned-char

9
推荐指数
2
解决办法
695
查看次数

每个cpu arch的真正ELF TLS ABI要求是什么?

Ulrich Drepper关于线程本地存储的论文概述了几种不同cpu架构的TLS ABI,但我发现它不足以作为实现TLS的基础,原因有两个:

  1. 它省略了许多重要的拱门,如ARM,MIPS等(虽然包括一堆与Itanium完全无关的)
  2. 更重要的是,它将大量实现细节与ABI混合在一起,因此很难说出互操作性需要哪些属性,哪些只是其实现的一部分.

例如,i386唯一的实际ABI要求是:

  • %gs:0 指向指向自身的指针.
  • 主可执行文件的TLS段(如果有)必须位于此地址的固定(通过链接器,负)偏移量.
  • 初始加载的库的所有其他TLS段必须具有相对于此地址的运行时常量(即,对于每个线程相同,但在不同的程序运行中不一定相同)(并且动态链接器必须能够填充重定位)这些补偿).
  • ___tls_get_addr并且__tls_get_addr函数必须以正确的语义存在,以便查找任意TLS段.

特别是,DTV的存在或布局不是 ABI的一部分,也不是主程序之外的TLS段的排序/布局.

似乎任何使用"TLS变体II"的拱门都具有大致上述ABI要求.但我完全不了解"TLS变体I"的要求,而且从阅读来源(在uClibc和glibc中)看来,甚至可能存在"变体I"的几种变体.

有没有更好的文件我应该看一下,或者熟悉TLS工作的人能向我解释ABI的要求吗?

c linux elf abi thread-local-storage

9
推荐指数
1
解决办法
960
查看次数

mmap/mprotect-readonly零页数是否计入已提交的内存?

我希望在我的进程中为以前使用但目前不需要的内存保留虚拟地址空间.我对主机内核是Linux的情况感兴趣,并且它被配置为防止过度使用(通过详细计算所有已提交的内存来实现).

如果我只是想阻止我的应用程序不再使用的数据占用物理内存或交换到磁盘(无论哪种方式浪费资源),我可以madvise在内核中不需要它,或者mmap新的零页面.但是这些方法都不一定会减少计数为已提交的内存量,然后阻止其他进程使用.

如果我将页面替换为标记为只读的新零页怎么办?我的意图是它们不计入已提交的内存,并且我可以在以后使用它mprotect来使它们可写,并且如果使它们可写将失败将超过提交的内存限制.我的理解是否正确?这会有用吗?

c linux mmap memory-overcommitment

8
推荐指数
1
解决办法
1672
查看次数

使用宏进行类型泛型编程:确定类型的技巧?

可以将某些类型的类型泛型函数作为C中的宏来执行,例如:

#define SQRT(x) (sizeof(x) == sizeof(float) ? sqrtf((x)) : \
                 sizeof(x) == sizeof(double) ? sqrt((x)) : \
                 sqrtl((x)) )
Run Code Online (Sandbox Code Playgroud)

只要x是浮点类型,这(大多数)就可以正常工作.

但是,如果我想要一个可以采用整数类型或指针类型的类型通用宏,它可能具有相同的大小.有没有一种聪明的方法来测试宏参数是整数还是指针?整数与浮点类型怎么样?

c generics macros c-preprocessor

8
推荐指数
1
解决办法
4561
查看次数

以编程方式确定有符号整数类型的最大值

此相关问题是关于在编译时确定签名类型的最大值:

C问题:off_t(和其他有符号整数类型)的最小值和最大值

但是,我已经意识到在运行时确定有符号类型(例如time_t或off_t)的最大值似乎是一项非常困难的任务.

我能想到的最接近解决方案的是:

uintmax_t x = (uintmax_t)1<<CHAR_BIT*sizeof(type)-2;
while ((type)x<=0) x>>=1;
Run Code Online (Sandbox Code Playgroud)

只要type没有填充位,这就避免了任何循环,但是如果type确实有填充位,则强制转换会调用实​​现定义的行为,这可能是信号或无意义的实现定义转换(例如,剥离符号位).

在我看来,我开始认为问题是无法解决的,这有点令人不安,并且会成为C标准的缺陷.证明我错的任何想法?

c signed overflow

8
推荐指数
1
解决办法
5156
查看次数

复合文字和类似函数的宏:gcc或C标准中的错误?

在C99中,我们有复合文字,它们可以传递给函数,如:

f((int[2]){ 1, 2 });
Run Code Online (Sandbox Code Playgroud)

但是,如果f不是函数而是函数式宏,gcc barfs由于预处理器解析它不是作为一个参数而是作为两个参数" (int[2]){ 1"和" 2 }".

这是gcc或C标准中的错误吗?如果它是后者,那几乎排除了所有透明使用类似函数的宏,这似乎是一个巨大的缺陷......

编辑:作为一个例子,人们会期望以下是一个符合要求的程序片段:

fgetc((FILE *[2]){ f1, f2 }[i]);
Run Code Online (Sandbox Code Playgroud)

但是因为fgetc可以实现为宏(虽然需要保护其参数而不是多次评估它),但这段代码实际上是不正确的.这对我来说似乎很令人惊讶.

c gcc c99 compound-literals c-preprocessor

8
推荐指数
2
解决办法
735
查看次数

有没有办法用i387 fsqrt指令进行正确的舍入?

有没有办法用i387 fsqrt指令进行正确的舍入?...

... 除了改变 x87控制字中的精度模式 - 我知道这是可能的,但它不是一个合理的解决方案,因为它有令人讨厌的重入类型问题,如果sqrt操作被中断,精度模式将是错误的.

我正在处理的问题如下:x87 fsqrt操作码在fpu寄存器的精度中执行正确舍入(按IEEE 754)平方根操作,我假设它是扩展(80位)精度.但是,我想用它来实现高效的单精度和双精度平方根函数,并且结果正确舍入(按照当前的舍入模式).由于结果具有过高的精度,因此将结果转换为单精度或双精度的第二步再次舍入,可能会留下不正确舍入的结果.

通过一些操作,可以通过偏差来解决这个问题.例如,我可以通过以2的幂的形式添加偏置来避免过度结果的过度精度,该偏置将双精度值的52个有效位强制为63位扩展精度尾数的最后52位.但我没有看到任何明显的方法用平方根做这样的技巧.

任何聪明的想法?

(还标记为C,因为预期的应用程序是C sqrt和sqrtf函数的实现.)

c floating-point assembly rounding x87

8
推荐指数
1
解决办法
735
查看次数

即使使用SA_RESTART,哪些功能也会被信号中断?

是否有任何合理完整的列表,其中POSIX中的哪些功能在EINTR接收或处理信号时被中断,即使没有信号处理程序或者处理程序是否已安装SA_RESTART?一些例子:

  • select
  • nanosleep
  • 等等

c posix signals

7
推荐指数
1
解决办法
3218
查看次数