[Linux]最近遇到的奇怪的不同平台编译相同代码生产的elf运行不一致问题

在不同的平台上(virtualbox/vmware/windowssubsystem),编译出的elf运行不一致,有的平台运行之后一直高频率进input dma中断。

查看主要的库版本,libc libm等版本一致,操作系统为ubuntu,大版本一致,小版本有差别。从现象上看怀疑工具链在不同平台运行有问题。

后来平台编译信息对比,发现最后的链接顺序不一样。正常的平台按照异常的平台的链接顺序编译的话,也是无法运行。

由于问题跟中断有关,无法用gdb进行单步跟踪,只好用其他方法。

中断分析:
正常状态下,input dma中断为10ms一次(100hz),发现出问题时dma的中断频率为(730hz)。之后发现另外的一个现象,修改某个函数的代码,之前正常的平台编译出来的elf运行也是730hz。 两个不同的行为出现了相同的现象。

于是,要来了DMA与dma搬运数据的codec模块寄存器表,发现dma一切正常,但是codec异常,寄存器全部为默认值。此时开始怀疑模块没有初始化。后来跟踪代码发现,codec模块初始化是由一个判断控制。这个判断用于一个power模块,而这个模块又没有使用。此时这个判断会从0地址去获取数据用于判断。

由于不同的链接顺序下,0地址的代码并不一致,从而导致的不同平台的不同表现。至于修改某个函数的代码也会出现,恰恰是这个函数被链接到了0地址,从而也引发了相应的现象。

现在回头想的话,在修改代码也出现问题的时候,应该去怀疑对应的地址的变化会导致出问题。当然最终定位还是需要看寄存器的相关信息。

 

[linux]select实现非阻塞异步输入

最近要实现一个非阻塞输入的C代码,查到可以通过select函数来完成。
select用于同时监控多个fd,在io可读/写时返回。
正常来说,select是阻塞的,除非超时或者等待的fd内容发生变化。因此,我我们将select的超时时间设为0,便可以非阻塞。按照返回来判断当前监视的fd内容是否变化,从而根据这个返回进行相应的操作。
以下为根据select man page修改的非阻塞输入的代码。
int main(void)
{
fd_set rfds;//fd集合
struct timeval tv;
int retval;
/* Watch stdin (fd 0) to see when it has input. */
/* Wait up to five seconds. */
char c;
char buf[100];
while(1)
{
//select返回后会把以前加入的但并无事件发生的fd清空,则每次开始select前都要重新从array取得fd逐一加入
FD_ZERO(&rfds);
FD_SET(0, &rfds); //监控fd为0,也就是stdin
//FD_ISSET(int fd, fd_set *set);//若存在多个fd,可以用此接口逐个判断
tv.tv_sec = 0; //时间设0,无论fd有没有数据均继续运行
tv.tv_usec = 0;
retval = select(1, &rfds, NULL, NULL, &tv); //1 here means (fd=0)+1,see man page
/* Don’t rely on the value of tv now! */
if (retval == -1)
perror(“select()”);
else if (retval)//select检测到有数据之后,通过read读fd=0的数据
{
int ret = 0;
ret = read(0, buf, 100);
if(ret > 0)
{
int i = 0;
for(i = 0;i < ret; i++)
printf(“%c”, buf[i]);
}
//printf(“Data is available now.\n”);
}
/* FD_ISSET(0, &rfds) will be true. */
else//此处为非阻塞正常运行的程序
{
//printf(“No data\n”);
}
retval = 0;
}
exit(EXIT_SUCCESS);
}