以下是小编为大家准备了基于FPGA和ARM的GPS信号处理平台,本文共9篇,欢迎参阅。

篇1:基于FPGA和ARM的GPS信号处理平台
基于FPGA和ARM的GPS信号处理平台
介绍了GPS信号处理平台的具体实现.该平台采用GP芯片作为GPS接收机的'射频前端,在FPGA上采用Verlog硬件描述语言实现GPS信号12通道相关器,采用基于ARM7核的微处理器实现GPS信号的基带处理,在上位机显示处理结果.最终测试结果表明该GPS信号处理平台成功实现了对GPS信号的捕获与跟踪,能够实时显示卫星方位与用户坐标,扩展性强,应用前景广阔.
作 者:傅遁嘉 陈佳品 滕依良 FU Nai-jia CHEN Jia-pin TENG Yi-liang 作者单位:上海交通大学微纳科学技术研究院,薄膜与微细技术教育部重点实验室,上海,40 刊 名:微电子学与计算机 ISTIC PKU英文刊名:MICROELECTRONICS & COMPUTER 年,卷(期): 26(4) 分类号:P228.4 关键词:GPS接收机 FPGA ARM 数字相关器 基带信号处理篇2:基于FPGA的光栅尺信号智能接口模块
基于FPGA的光栅尺信号智能接口模块
摘要:介绍了一种基于ALTERA公司大规模可编程逻辑器件EPF10K10的多功能光栅尺处理品电路。叙述了该电路的主要电路――四倍频细分、辨向电路、计数电路、接口处理电路的设计原理,风时给出了详细的电路和仿真波形。关键词:光栅尺 四倍频细分 辨向 EDA FPGA EPF10K10
1 光栅尺信号及电路设计要求
将光源、两块长光栅(动尺和定尺)、光电检测器件等组合在一起构成的光栅传感器通常称为光栅尺。光栅尺输出的是电信号,动尺移动一个栅距,输出电信号便变化一个周期,它是通过对信号变化周期的测量来测出动就与定就职相对位移。目前使用的光栅尺的输出信号一般有两种形式,一是相位角相差90o的2路方波信号,二是相位依次相差90o的4路正弦信号。这些信号的空间位置周期为W。本文针对输出方波信号的光栅尺进行了讨论,而对于输出正弦波信号的光栅尺,经过整形可变为方波信号输出。
输出方波的光栅尺有A相、B相和Z相三个电信号,A相信号为主信号,B相为副信号,两个信号周期相同,均为W,相位差90o。Z信号可以作为较准信号以消除累积误差。
图1给出了动尺移动时A、B信号的变化情况。在A信号的下降沿采集B信号,就可以判断出运动方向。图中前半部分为正向运动,A信号的上升沿及下降沿均比B信号超前1/4W,在A信号下降沿采集的B信号为“1”;后半部分为反向运动,A信号的上升沿及下降沿均比B信号滞后1/4W,在A信号下降沿采集到的B信号为“0”。根据采集到的运动信号方向和A信号变化的周期数用计数器进行曲计数(正向计数或逆向计数),就可以测算出总位移。
在上述信号处理、测量电路中,用到了触发器、计数器等多种数字集成电路,测量分辨率为光栅栅距W。目前,计量用光栅尺的刻线一般为每毫米50~250线,对应的栅距W为20~4μm ,在精密测量中往往不能满足要求,需要进行曲细分。如果同时考虑A、90o信号上升沿和下降沿的各种情况,就可以实现信号四细分,其主要电路有:细分辨向、计数和接口电路等。以上功能可以由通用数字集成电路来完成,但这种设计方法所用芯片多,结构复杂。当然也可以通过单片机以及一些外围芯片来完成,只是这种方法通用性差,编程复杂,而且增大了单片机的负担,使单片机响应其它事件的实时性变差。
随着大规模可编程逻辑器件(CPLD:复杂可编程逻辑器件;FPGA:现场可编程门阵列)的飞速发展,传统的电路设计方法已大为改观。许多传统的逻辑电路完全可以用可编程逻辑器件来代替,并且可提高系统的可靠性,减小PCB的面积,使产品小型化,还有利于保护知识产权。利用EDA(电子设计自动化)技术设计可编程逻辑器件已成为现代电子设计的一种必然趋势。本文所介绍电路的接口模块就是基于FPGA芯片完成的。
该电路设计有如下要求:利用FPGA芯片完成双路光栅尺信号处理(考虑到2维X-Y平台的应用场合)、四细分及辨向功能、24位可逆计数器、与微处理品器及各种单片机的并行接口电路(包括锁存、译码、清零电路等)。其对外接口信号如图2所示。
INA1、INB1、INA2、INB2分别为两路A、B信号。作为处理电路 输入信号,这2路信号经四细分、辨向后,可为两路24信可逆计数器提供计数脉冲和方向信号。接口电路包括锁存、译码、清零电路等,通过数据线D0~D7、地址线A0~A4、片选信号线CS来读写控制与外部微控制器接口。接口采用8位数据总线,计数值(48位,占6个读口)及清零命令等数据交换均通过不同口地址的读写完成。该模块的操作与其它智能接口器件(如8255、8253等)相类似。
2 FPGA器件的选择
根据设计要求和综合估算整个电路所需要的管脚和宏单元的个数,本设计选用EPF10K10。它是ALTERA公司FLEX10K系列产品之一,是一种嵌入式可编程逻辑器件。EPF10K10采用CMOS SRAM制靠工艺,使用权SRAM来存储编程数据,具有在系统可编程特性。具体的配置方式有被动型和主动型两种,其中被动型配置是在上电后由计算机通过编译后产生的后缀为SOF的文件利用专门的下载电缆配置芯片。而主动型配置是在上电后由专门的可编程配置芯片(如EPC1441)自动对EPF10K10芯片进行配置。EPF10K10具有高密度(可用逻辑门1万~25万;RAM;6114~4096位,512个宏单元)、高速度、低功耗等特点。芯片内含有专用进位链和级联链及快速通道,故其互连方式十分灵活。
3 电路设计
本电路采用Altera公司的Max -plus 开发平台进行设计。Max -plus 为Altera公司的专门开发平台,它包括设计输入、编译、仿真、器件编程等功能。该平台使用方便,允许用户用原理图、VHDL语言、波形图等多种输入方法进行设计。下面介绍系统主要电路的设计。
3.1 细分辨向电路
光栅尺信号的`细分与辨向是提高光栅尺测量精度的关键性一步。在笔者所参考的关于光栅辨向和细分电路的资料中,很多设计者都没有综合考虑辨向和细分的复杂性,而是把辨向和细分电路分开,辨向电路只对光栅尺的输出信号进行辨向,而不是对细分后的脉冲信号进行辨向,这样实现测量误差仍是光栅尺的栅距。在考虑辨向功能时,应对细分后的信号进行辨向设计,否则不能提高测量精度。
细分辨向电路的原理图如图3所示,光栅尺输出的相差为90 o的方波信号INA、INB经RC滤波和施密特整形后(芯片外处理)输出信号A、B,然
后经第一级D触发器后变为A’、B’信号,再经过第二级D触发器变为A”、B”信号。通过D触发器可以对信号进行整形,从而消除了输入信号中尖脉冲带来的影响,这样在后续倍频电路中不再使用权原始信号A、B,因此提高了系统的抗干扰性能。D触发器的时钟由外部有源晶振提供,其频率为1MHz,远高于A、B波形变化的频率,因而可以认为,D触发器的输出端Q能跟踪输入端D的变化。在四倍频辨向电路中,采用组合、时序逻辑实现A’、A”、B’、B”信号进行的逻辑组合。
当光栅尺正向运动时,从CLKADD信号端输出四倍频脉冲,而CLKSUBB端无信号输出。当光栅尺反向运动时,从CLKSUBB信号端输出四倍频脉冲,而CLKADD端无信号输出。CLKADD和CLKSUBB相与后作为可逆计数器的计数脉冲CLK,读出该计数器的值便可得出光栅移动的位置。CLKADD和CLKSUBB信号组成的RS触发器电路可产生ENADD,ENSUBB。ENADD可作为可逆计数器的方向信号。其仿真波形如图4所示。
3.2 计数电路
本系统中的24位计数器采用VHDL语言进行设计。输入信号定义为时钟CLK、方向信号fx =ENADD ,清零信号CLR(后面有介绍)。输出信号定义为24位的计数结果COUNT(23:0)。用VHDL语言来编写实现24位可逆计数器功能。其仿真信号如图5所示。
3.3 接口电路
接口电路用原理图法设计,电路包括以下部分:
(1)地址译码电路:输入信号为外部(微处理器、单片机等)的地址线A0~ A4、片选信号线CS、读写控制信号,通过逻辑门电路的连接构成组合逻辑,给每一个内部单元提供使能信号。
(2)锁存接口电路:由于内部各计数单元工作属于动态过程,因此外部微处理器(或单片机等)在读取数据时,应该先给其发出锁存信号然后再读取数据,以保证读出稳定的数据。锁存器输出设计为三态门输出,与外部数据线连接,三态门的使能信号由译码电路提供。
(3)清零电路:电路中设计了清零电路。清零脉冲是通过外部写命令(8位)内部进行译码的方式进行的,而不是使用一根信号线进行清零,这样可以有效地防止在只使用一根信号线时受干扰等原因而引起的误清零现象。
4 结束语
本设计经过仿真、编译实现后,将代码下载到EPC1441可编程配置芯片,属于主动配置模式。在接口模块上电后由EPC1441自动对EPF10K10芯片进行配置。该接口模块已成功应用于于笔者设计的运动控制系统中,成功地完成了对光栅尺(运动控制系统中的位置反馈部件)信号的四细分处理功能,性能稳定可靠。如果在此设计的基础上再加上译码驱动和显示电路,就可作为位移测量和显示电路独立使用。
篇3:unix关于信号、信号处理函数
信号是发生某件事时对进程的通知,它不可以被预知,信号可以来自其它进程或者进程本身,也可以是来自内核。
每个信号都有一个处理办法(disposition),也称作与信号关联的行为(action),一般有三种处理方法:
1 提供一个函数(signal handler),在信号发生时调用,这称之为捕获(catching)。
2 设置信号的处理办法为SIG_IGN,忽略它,但有两种信号是不能忽略的:SIGKILL、SIGSTOP。
3 SIG_DFL可以设置缺省处理办法,一般是收到信号时终止进程。当然,也可以忽略它们,例如SIGCHLD和SIGURG。
POSIX中定义信号处理函数用sigaction函数,处理函数的指针做为填充在做为参数之一的struct sigaction结构中,当然还有函数所对应的需要处理的信号,原型如下:
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
UNP中为sigaction做了一个包裹函数:
Sigfunc *signal(int signo, Sigfunc *func);
它与非POSIX的signal函数一致,就是向操作系统登记要处理的信号,
信号处理函数在登记完成后便一直有效。信号处理函数在执行时,可以通过设置sa_mask来指定要屏蔽的其实信号。此时被屏蔽(阻塞)的信号是不排队的。当然,在实时UNIX系统中不会阻塞信号,所有的信号排队接受处理。
UNIX对信号的处理不排队,这个特性就是说:当同时有若干个信号,只捕获第一个,其它的就被抛弃了。可以想像一个服务器同时连接着很多客户机,当若干个客户同时断开连接,服务器处理这些客户的进程结束后几乎同时向父进程发送SIGCHLD信号。只有第一个SIGCHLD被处理了,其它的信号会屏蔽掉,子进程仍会处于僵尸状态(Zombie)。
处理这种情况要使用waitpid函数,它比wait提供更多控制机制,下面就是一个典型的SIGCHLD信号处理函数:
cpp 代码复制内容到剪贴板
篇4:信号处理求职简历
信号处理求职简历模板
个人信息
姓 名:***** 性别: 男
出生年月:1972年7月
学 位:博 士
专业:信号与信息处理
联系电话:139************
Email :
教育背景
9月---4月,******大学,信号与信息处理,工学博士;
9月---207月,******大学,应 用 数 学,理学硕士;
1992年9月---7月,*******大学,应 用 数 学,理学学士。
工作经历
204月---至今,****************研发中心,高级系统工程师
主要从事:
1.WCDMA R99系统的基带信号处理的工作:在Node B的多径检测与估计、信道估计、Rake合成、Doppler估计、AFC和SIR估计等关键算法在物理层协议配置下的实现、SPW模拟和关键参数确定,具体算法及其参数均实现于UT NB8001系统;
2.TD-SCDMA系统的智能天线、联合检测等关键算法的实现、SPW模拟及论证;
3.WCDMA R99系统的'性能测试;
4.HSDPA系统及STTD HSDPA的分组调度、HARQ及RV选择、自适应编码与调制、ACK/NACK检测辅助调度和多用户无线系统动态资源分配(功率、码域和时域资源)等关键技术的链路层和系统层模拟和实现;
专业技能
熟悉WCDMA物理层协议;
了解TD-SCDMA物理层协议;
有着良好的数学功底,具有扎实的数字通信理论和信号处理理论基础,对自适应信号处理、纠错编码理论、调制解调技术、智能天线技术有较深的研究,并熟悉各种通信系统的算法、SPW与MATLAB仿真及其实现;
精通DBF技术,熟悉智能天线及MIMO无线通信系统抗干扰技术;
精通空时(space-time)编码及分集技术;
熟悉OFDM及WCDMA LTE技术;
能熟练使用C、MATLAB、SPW和Opnet软件进行各种通信和信号处理模拟;
篇5:基于FPGA的实时图像处理技术研究
基于FPGA的实时图像处理技术研究
随着多媒体应用已经普及,相应的数字图像处理也得到了广泛的应用.虽然图像处理的`技术有很多种,但是实时图像处理通常需要巨大的数据吞吐量和运算量.因此专用的硬件或者多重处理技术的并行处理很必要.FPGA特有的逻辑结构单元对实现实时图像处理来说有着先天的优势.本文首先给出了FPGA在图像处理中的简单应用,然后详细介绍了国内外目前的研究现状及其中的关键问题.
作 者:唐奋飞 作者单位:湘谭职业技术学院,湖南,湘潭,411102 刊 名:咸宁学院学报 英文刊名:JOURNAL OF XIANNING COLLEGE 年,卷(期): 29(3) 分类号:P393 关键词:多媒体 FPGA技术 图像处理 数字图像篇6:基于FPGA的电子稳像平台的研究
基于FPGA的电子稳像平台的研究
摘要:分析了传统电子稳像平台的缺陷,研究并设计了基于FPGA的专用平台。针对该平台研制过程中所涉及的一些关键问题进行了详尽的分析与探讨,给出了可行的解决办法。实验结果表明该平台工作稳定,扩展性好。关键词:视频处理 电子稳像 FPGA
电子摄像系统已广泛应用于军用及民用测绘系统中,但是效果受到其载体不同时刻姿态变化或震动的影响。当工作环境比较恶劣,尤其是在航空或野外操作时,支撑摄像机平台的震动会引起图像画面的抖动,令观察者视觉疲劳,从面产生漏警和虚警。所以在运行中,如何稳像成为十分重要的问题,特别是在长焦距、高分辨力的监视跟踪系统中更加突出。具璞蒿、实性性强、体积小巧等特点,得到更广泛的应用。
图1
稳像系统的反应速度是电子稳像要解决的关键技术之一。传统的基于“摄像机-图像采集卡-计算机”模式的稳像系统、图像检测和匹配算法全部由计算机以软件方式实现。尽管当今计算机的性能很高,能够部分满足单传感器电子稳系统的实时处理要求,但在以下几个方面有着难以解决的问题:首先,其固有的串行工作方式使得单计算机难以适应其于多传感器视频处理系统的实时稳像,阻碍了在实际中的应用adw欠,传统的图像采集卡中能将采集图像数据实时传输给计算机,而不能传输给标准接口的视频监视设备lk之很多应用场合对听要求很高。因此,研制专用的电子稳像平台,既能实时地高速获取视频数据,又能将数据实时地传后续的图像处理系统,既有实际意义又有工程价值。
(本网网收集整理)
1 系统涉及的关键技术
摄像头输入的PAL制式电视信号首先通过视频处理接口完成对其解码、同步和数字化的工作,数字化后的图像信息进入到由FPGA实现的帧存控制器中,完成数据的交换(数据的缓冲),同时完成系统要求的去隔行和放大的操作,最后处理好的数据通过VGA控制器,完成时序变化,经视频、A变为模拟信号送到VGA监视器上实时显示。
图3
1.1 视频处理接口
由于在进行视频处理时,多为从摄像头输入模拟信号,如NTSC或PAL制式电视信号,除图像信号外,还包括行同步信号、行消隐信号、场同步信号、场消隐信号以及槽脉冲信号等。因而对视频信号进行A/D转换的电路也非常复杂。Philips公司将这些转换电路集成到了一块芯片中,从而生产出功能强大的视频输入处理芯片SAA7111,为视频信号的数字化应用提供了极大的方便。
系统设计采用SAA7111对复合信号进行采样、同步产生、亮色分离并输出标准的数字化信号。SAA7111输出的数字化图像信息符合CCIR.601建议,PAL制式的模拟信号数字化后的图像分辨率为720×572,像素时钟13.5MHz。在本稳像系统中要求图像输出符合VGA(640×480,60Hz)标准,因此在采集数据时要对数据进行选择,避开行、场消隐信号和部分有效像素信息,在较大的图像中截取所需要的大小。SAA7111向帧存控制电路输出像素时钟(LCC2)、水平参数(HREF)、垂直参考(VREF)、奇偶场标志信号(ODD)和16位像素信息(RGB565).其中LCC2用来同步整个采集系统;HREF高电平有效,对应一行720个有效像素;VREF高电平有效,对应一场信号中的286个有效行;ODD=1时,标志当前场为奇数场;ODD=0时,标志当前场为偶数场。采用16位RGB表示每个像素的彩色信息。图1(a)为数字化图像中的`一行像素的时序图。其中两个HREF分别表示有效行的起始与结束位置,实际为一个信号;可以清楚地看到一行中有效的720个像素与像素时钟LLC2的对应关系,在采集时通过帧存器控制电路选择其中部的640个像素进行采集。图1(b)为一帧数字图像的输出时序图。可以看到在第624~22行时,VREF处于无效状态,因此在后续的采集中,这部分的信息不予处理并通过ODD的电平区分奇偶场数据。
图4
1.2 去隔行支持
PAL制电视信号采用隔行扫描机制,采用人眼的视觉暂留来实现两场1/50s扫描312.5行的图像构成625行(一帧)图像。而标准的VGA显示模式采用逐行扫描方式,在一个扫描周期内实现对图像的完全扫描。因此需要对视频信号进行去隔行处理。视频信号在经过缓冲后,按照取样时钟把经过模数转换的数字信号送入存储器缓存,通过数据内插的方法进行数据扩展,即相邻行之间按照一定的算法进行加权,从而得到内插行的数据,再以适当的速度读取处理后的数据,即可实现倍行频/倍场频的扫描。倍行频扫描可以消除行间的闪烁现象,倍场频扫描虽然行扫描频率不变,但是场频加倍,即能消除行间闪烁现象,还可以消除场间的大面积闪烁。去隔行问题的实质就是在每一场中填补被跳过的那些行,其过程如图2所示。
实际上为实现去隔行已经提出了很多简单的滤波器。一种选择是用同一场中的垂直内插值,这是个一维二倍上转换的问题。理想的垂直滤波器是一个半带低通滤波器。然而,这个滤波器要求无限长度冲击响应是不可实现的。实际应用中使用的是短得多的滤波器。最简单的是行平均,它用丢失行的上一行和下一行的平均来估计
该丢失行。在图2中,对于第t场,D=(C+E)/2。由于没有使用时域滤波,所以它沿时间频率轴具有全通特性。为了改进性能,另一种选择是使用更长的垂直内插滤波器,其频率响应更接近理想的半带低通滤波器。对于第t场的行,满意的内插方法是D=(A+7C+7E+G)/16。以上两种方法都是只用了垂直内插。一种替代方法是使用时间内插。值得注意的是,对于一场中每个丢失行,在同一帧的另一场中有一个对应行。一个简单的时间内插方案是复制此对应行,即D=K,J=C。这种方法称为场合并。因为每一个去隔行帧都由合并两场获得,但是这两场的时间内插是相反的(对于某些特殊图案可能会产生视觉人为失真)。由于只在时间方向上进行了滤波,因此在垂直方向上是全通的。
为了改进性能,可以使用一种对称的滤波器,例如,对前一场和后一场中的对应行去平均以获得当前场中丢失的行,即D=(K+R)/2。这种方法称为场平均。然而这种办法内插任何一场需要涉及三个场,需要两帧存储器。与场合并的方法相比,在存储器容量和延时上有不可忽视的增加。为了在时间和空间人为失真方面达到折衷,较好的方法是既用垂直内插也用时间内插。例如,通过对同一场中上一个和下一个像素以及前一场和后一场取平均进行内插的办法得到一个丢失的像素。综上所述,当成像的景物在相邻两场之间静止时,在奇数场中丢失的偶行数应该与前一个和后一个偶场中对应的偶数行完全一样。因此时间内插将产生精确的估计。另一方面,当景物中存在运动时,相邻场中对应行可能不在同一个物体位置上,时间内插将产生不可接受的人为失真。而同时使用空间和时间平均的方法将产生不太严重的人为失真,但在存储器容量和反应时间方面作出牺牲。
通过上述方案的对比及系统的具体要求,设计中采用了场合并的办法,具体实现由帧存控制器完成。
1.3 帧存控制器
帧存储器是图像处理器与显示设备之间的通道,所有要显示的图形数据先存放到帧存储器中,然后再送到显示设备进行显示,因此帧存储器的设计是图形显示系统设计的一个关键。传统上,可以实现帧存储器的存储器件有多种,如DRAM、SDRAM及SRAM等。DRAM、SDRAM属于动态存储器,容量大、价格全家但速度较慢,且在使用中需要定时刷新。对于基于FPGA的视频处理器,需要设计专用的刷新电路,增加了系统设计的复杂程度。SRAM速度高、接口简单、容量较小。随着集成电路技术的不断发展,容量不断增大,价格也不断下降。在需要高速实时显示的视频处理系统中的使用越来越普遍。
帧存控制器的设计对于实现两种不同视频系统之间的图像信号的存储、采集和显示显得非常重要。为了保证数据处理与采集的连续,设计中使用了两组帧存储器(FRAM1、FRAM2),由于数字化的图像每帧大小为640×480=307200(16bit)共300K×16bit的数据量,笔者使用每组512K×16bit的静态存储器,存储时间为12ns,可以保证快速地读出和写入图像数据。图3为帧存控制器的逻辑框图。
由于输入信号为隔行扫描的图像数据,显示输出需要逐行扫描数据,因此数据存入帧存储器时需要进行处理。设计中采用场合并行法,将两场的数据写入一个帧存中,构成一幅完整的逐行扫描图像,系统利用VREF信号对此进行控制,产生的帧切换控制信号控制数据在两个帧存中的切换。当VREF信号有效时,表明新的一场开始了,此时无效行计数器开始工作,控制不需要采集的图像行,计数到阈值后,有效行计数器开始工作,控制所要采集的图像行,并发出高位地十信号A[18..11];同样,当HREF有效后,无效像素计数器开始计数每行中的无效像素,然后有效像素计数器开始计数需要采集的行听有效像素;每次计满640个像素后,等待下一次有效行信号的到来,同时将有效行计数器加1。由于系统选用的帧存容量较大,因此利用ODD的反相信号作为帧存地址的A10,为每行图像提供了1024个存储空间(实际使用640个),可以简化数据写入与读出的控制电路。隔行的视频信号就会被逐行地存储到帧存体中。总线隔离与控制电路用来完成数据在帧存中的写入与读出的同步。由于采用SRAM作为帧存体,有效像素的写入与后续视频接口的读出不能在一个帧存体中同时进行,系统采用双帧存轮流操作的方法,系统采用双帧存轮流操作的方法:当数字化后的图像信息写入其中的一个帧存时,帧存控制器将另一个帧存中的像素顺序读出,送到显示设备,反之亦然。
1.4 视频图像的放大变换
应用栅格理论几何变换处理过程可以按下面方式进行描述:给定一个定义于点阵Λ1上已采样信号,需要产生一个定义于另一个点阵Λ2上的信号。如果,Λ1中的每一个点也在Λ2中,那么此问题是上转换(或内插)问题,可以先将那些在Λ2中而不在Λ1中的点添零(即零填充),然后用一个作用于Λ2上的内插滤波器估计这些点的值;若Λ1)Λ2,即为下转换(或抽取)问题,可以简单地从Λ1中取出那么也在Λ2中的点。然而,为避免下采样信号中出现混叠,需要对信号进行预滤波,以将其带宽限制到Λ2*的沃格纳晶体。上转换和下转换的过程示于图4(a)、(b)中。更一般的情况,如果Λ1和Λ2互相不包含,就需要找到另一个即包括Λ1又包括Λ2的点阵Λ3,可以先将Λ1上采样到Λ3,然后再下采样到Λ2。此过程示于图4(c)。图4(c)中Λ3中的中间滤波器完成两个任务:首先,内插出Λ1中漏下的采样点;其次把Λ3中的信号频谱限制于Λ2*的沃格纳晶格。
由于系统中进行放大变换采用FPGA实现,因此本文讨
论的重点在于如何简化实现并提高转换速度,上转换中的上采样过程为:
(1)式中Ψs,1和Ψs,3分别为原理图像和上采样信号;U(.)为上采样运算;Λ2\Λ1表示在Λ2而不在Λ1内的点的集合。插值滤波器的定义如下:
(2)式中,d(Λ)为栅格Λ的采样密度;v*表示栅格Λ的转逆栅格的Voronoi单元,即栅格Λ原点的单位元,它向所有栅格点平移将会无重叠地覆盖整个连续空间。最简单的插值滤波为线性插值,也可以采用二加权滤波的方法。图像的缩放还可以采用3次样条插值和小波分解的方法,虽然这些方法在理论上可以取得很好的图像缩放效果,但计算复杂,即使采用快速算法,也难以实现视频图像的实时显示。
针对视频信号数据量大、数据流速度的特点,采用FPGA设计,可以完成帧存控制、视频信号的实时放大与叠加功能。基于运算速度与算法实现的难易程度分析,对视频信号的放大采用了简单的线性插值的办法,原理如图5所示。视频信号是以场或帧进行存储的,由于数据写入时存储地址与图像显示的空间位置有确定的对应关系,因此系统需要的放大处理就变为对帧存储体的地址线的控制问题。
对于本系统具体的4倍放大要求,将行同步信号先进行二倍行使能运算,并利用场同步信号对该寄存器进行复位,将生成后的二分频行同步信号控制行地址发生器,也就是产生帧存储器所需的高位地址;类似地利用像素时钟、行同步信号和场同步信号就可以得到所需的低位地址。由于在帧存控制器向帧存储器写入数据时采用了一行点1024个位置的办法,所以在低位地址后连接了一个比较器,当产生的低位地址小于640时,帧存储器的读信号有效,否则无效,以保证不会混叠入无效的数据。
1.5 VGA接口控制器
标准的VGA(640×480,60Hz)接口需要提供以下几组信号:3个RGB模拟信号、行同步信号HS和场同步信号VS。它的信号时序如图6所示。
图7
图6中VS为场同步信号,场周期为16.683ms,每场有525行,其中480行为有效显示行,45行为场消隐区,场同步信号每场有一个脉冲,该脉冲的低电平宽度为63μs(2行)。行周期为31.78μs,每显示行包括800点,其中640点为有效显示区,160点为行消隐区(非显示区)。行同步信号HS每行有一个脉冲。该脉冲的低电平宽度为3.81μs(即96个脉冲)。因此,VGA控制器的任务就是按要求产生所需要的时序。
DISCLK为视频显示时钟,频率为25MHz,首先输入到模等于800的像素计数器中,输出的计数值与一个预先设好的比较器进行比较,当计数器的值大于160时,输出高电平,反之输出低电平,作为行同步信号;同理,利用一个模等于525的计数器对行同步信号进行计数和一个阈值为45的比较器可以产生所需要的场同步脉冲VS。
产生的行、场同步信号和像素显示时钟分别被送到两个地址发生器中,产生所需要的控制帧存储器的地址信号。由于前面介绍的帧存控制器中采用为每行数据提供1024个存储空间的办法,因此在数据读出时也要进行相应管理。低位地址发生器产生的地址数据与一个比较器进行比较。当地址小于640时,帧存储器的读信号MEMRD位低电平有效,否则无效,这样有效像素数据就被完整地提出。由于VGA是一个模拟的接口标准,RGB彩色信息需要输入模拟量,因此帧存储器输出的数字信息还要经过D/A变换。系统先用飞利浦公司出品的TDA8771AH,它内部集成了三个视频D/A转换器,基于电阻网络架构,转换速率最高可达35MHz。由于它专用于数字电视、视频处理等相关领域,因此使用十分简单,只需要提供24bit数字信息和一个转换时钟即可。VGA控制器原理图如图7所示。
图8
2 系统集成
综上所述,完整的电子稳像系统结构如图8所示。摄像头输入的信号采用PAL制式,经过视频处理接口后形成RGB565格式的数字视频信号和控制信息;帧存控制器作为整个平台的核心,在将数据写入帧存储器的同时,对数字化的图像信息进行去隔行处理,再将数据读出送往VGA控制器时进行放大变换。VGA控制器则负责将数据按照VGA标准时序送往显示器上。
在该平台上实现了文献中K0等人提出的一种最简单的基本位平面的电子稳像算法,对于8位的灰度图像,可以表示为:利用第4层进行运算,其依据是在多帧图像进行BPM运算后发现,该层的误差结果较平滑。然而,K0的BMP-b4算法在不同的图像序列和信噪比的情况下,并不能总得到一个最优解;在某些情况下,b4、b5或b6会得到更好的结果。
目前资料显示电子稳像技术作为近年新兴技术还处于试验研究阶段,因其适用范围广阔而展现了乐观的研发前景。
篇7:Perl信号处理学习简单小结
作者:乡村运维 字体:[增加 减小] 类型:
这篇文章主要介绍了Perl信号处理学习简单小结,本文着重列出了Unix下常见的处理信号表,需要的朋友可以参考下
Unix 下常见的处理信号
代码如下:
No Name Default Action Description
1 SIGHUP terminate process terminal line hangup
2 SIGINT terminate process interrupt program
3 SIGQUIT create core image quit program
4 SIGILL create core image illegal instruction
5 SIGTRAP create core image trace trap
6 SIGABRT create core image abort program (formerly SIGIOT)
7 SIGEMT create core image emulate instruction executed
8 SIGFPE create core image floating-point exception
9 SIGKILL terminate process kill program
10 SIGBUS create core image bus error
11 SIGSEGV create core image segmentation violation
12 SIGSYS create core image non-existent system call invoked
13 SIGPIPE terminate process write on a pipe with no reader
14 SIGALRM terminate process real-time timer expired
15 SIGTERM terminate process software termination signal
16 SIGURG discard signal urgent condition present on socket
17 SIGSTOP stop process stop (cannot be caught or ignored)
18 SIGTSTP stop process stop signal generated from keyboard
19 SIGCONT discard signal continue after stop
20 SIGCHLD discard signal child status has changed
21 SIGTTIN stop process background read attempted from control terminal
22 SIGTTOU stop process background write attempted to control terminal
23 SIGIO discard signal I/O is possible on a descriptor (see fcntl(2))
24 SIGXCPU terminate process cpu time limit exceeded (see setrlimit(2))
25 SIGXFSZ terminate process file size limit exceeded (see setrlimit(2))
26 SIGVTALRM terminate process virtual time alarm (see setitimer(2))
27 SIGPROF terminate process profiling timer alarm (see setitimer(2))
28 SIGWINCH discard signal Window size change
29 SIGINFO discard signal status request from keyboard
30 SIGUSR1 terminate process User defined signal 1
31 SIGUSR2 terminate process User defined signal 2
perl的信号处理原理
Perl 提供了%SIG 这个特殊的默认HASH.调用需要使用到系统保留全局HASH数组%SIG,即使 用‘$SIG{信号名}‘截取信号,相当于,在perl程序中出现这个信号时,执行我们自己定义某段代码(子函数)的地址值(定义信号响应函数),这代码就是截取这个信息后要执行的结果了.
举个SIGALRM例子,也就是超时处理:
代码如下:
my $timeout = 10 ;
eval {
local $SIG{ALRM} = sub { die “alarmn” }; # n required
alarm $timeout; #如果到了$timeout时间就会去执行上面的sub
sleep 15;
print “ if timeout ,this will not print”;
alarm 0; #恢复到默认的状态
};
if ($@) {
die unless $@ eq “alarmn”; #可能捕获的不是超时,是其他错误,就die吧
print “timeout n” ;
}
else {
print “not timeout”;
}
这里要说一下perl的错误捕获机制
代码如下:
eval {
open(FH,”domains.txt”) or die “Can‘t open files,$!”;
};
捕获异常
代码如下:
if($@){#出现异常}
else{#无异常,打印文件内容
while{
…
}
close FH;
}
如果eval块中的程序有语法错误、运行时错误或者遇到die语句,eval将返回undef,
Perl信号处理学习简单小结
,
错误码被保存在$@中。
篇8:基于FPGA的毫米波多目标信号形成技术的研究
基于FPGA的毫米波多目标信号形成技术的研究
摘要:毫米波多目标信号形成是实现毫米波雷达模拟器的关键技术,要求目标分辨精度高、时延差值达ns级是其显著特点。介绍一种基于可编程逻辑器件FPGA的多目标信号产生的新方法。实践结果表明应用FPGA实现目标之间的延具有延时精度高、系统可靠性好等特点。关键词:毫米波雷达模拟器 多目标形成 现场可编程门阵列
近年来,精确制导武器的研制已经成为现代武器研制的一大热点,而毫米波多目标信号发生器正是精确制导武器研制的关键手段。毫米波多目标信号发生器通过模拟的方法产生多种类型高精度的雷达多目标回波信号,在实际雷达系统前端不具备的条件下对雷达系统后级进行调试,便于制导武器的性能测试,大大加快新武器的研制进程。毫米波多目标信号产生的关键是要求回波信号距离分辨率极高,常规的多目标信号产生方法如使用数字延时线产生多目标之间的延时,其控制不灵活,并且有些延时线需要接ECL电源,使用不方便也增加了设计的复杂度。使用分立元件实现延时则使电路元件过多,电路的稳定性及延时的精确性也会大大降低。本文介绍一种新的产生毫米波雷达模拟器的多目标信号的方法,针对毫米波多目标信号回波之间距离分辨率要求高的特点,采用现场可编程门阵列(FPGA)实现回波之间的时延。本文详述了使用FPGA控制及产生延时多目.标信号间精确延时的设计方法。该方法实现电路体积小、稳定性高,同时使延时精度得到了很大的提高,具有很好的工程应用价值。
(本网网收集整理)
1 多目标信号产生器
为了精确制导武器研制的需要,本信号发生器根据外部设定的工作方式及工作参数产生相应的毫米波雷达中频多目标信号。每个脉冲的开始保持严格的初相值,脉冲宽度间的多普勒信号调制要求回波目标信号相一致,目标之间的距离分辨率为0.3m,目标回波间延时范围为0~10ns。整个系统基于DSP+FPGA结构,高速DSP主要生成多目标信号产生器的回波数据,设计中采用静态RAM扩充存储一个相干区的回波信号的程序及数据,用EPROM存储相位表。FPGA实现所有的控制、地址发生等逻辑及产生多回波信号回波间分辨率为2 ns的时延。输入输出的显示由单片机控制。图1所示为多目标信号发生器产生一路模拟回波信号的结构框图,回波数据包含I、Q两路数据,系统中每路回波信号数据采用两片双口RAM进行存储。将从双DA输出的各路模拟回波信号相加(1支路与1支路相加,Q支路与Q支路相加),然后进行正交调制得到毫米波雷达模拟器多目标中频信号。整个系统结构简单、体积小、可靠性高。
回波信号包括目标信号、噪声和杂波信号两部分。利用回波数学方程考虑目标杂波特性以及随机噪声,产生运动目标的多普勒回波信号的数学方程为:
Si=Aiexp[-j 4πfi/c(R0-ut)]+G1(t)+G2(t)
其中fi=f0+i△f,i=0,1,…,255;G1(t)为高斯白噪声,G2(t)为杂波。高速DSP根据目标要求的信号幅度、多普勒频率、信号所处的距离单元等计算所需目标信号数据。对噪声的模拟,考虑到噪声是由系统内部产生,采用窄带高斯白噪声为模型。对杂波信号的模拟,由于杂波是系统外产生,分为地杂波、海杂波、气象杂波等,其数学模型多种多样,故把这部分作为可重加载模块实现。对不同的杂波模型,以不同的程序块实现。由DSP计算出的回波数字信号经双DA进行数模转换,输出模拟的回波基带信号。DSP与双DA间用双口RAM接口,这样可实现数据高速、可靠及灵活的调度。双口RAM的地址信号由VIRTEX-II系列FPGA提供。设计中,将双DA转换时钟之间应用FPGA实现了0、2、4、6、8和10ns的可变时延差,因此双DA输出的两路回波基带信号之间相应地产生了0、2、4、6、8和10ns的.延时。从而达到了模拟出的两路回波之间的延时范围为0~10ns, 目标之间达到0.3m的距离分辨率的设计要求。
2 多目标信号间高精度高可靠性延时的设计与实现
多目标信号各目标回波之间的距离体现在回波之间的时延上,多目标信号产生器的各回波之间的时延由FPGA产生。DSP将计算出的回波信号数据存储在双口RAM中,然后由双DA读出数据进行数模转换输出模拟的回波信号。FPGA需要为数据转换提供时序控制信号、读数据时的地址信号及双DA的转换时钟信号等;将时钟信号经过FPGA进行精确的延时,延时后的信号作为双口RAM读出数据时地址发生器的时钟信号,将延时后的信号与DSP提供给双DA的初始化信号相与后提供给双DA作为数据转换时钟。
产生各目标回波间时延有多种方法,如采用分立元件实现,但这种方法存在电路复杂、可靠性差等缺点。本文采用FPGA器件实现回波间高精度的延时具有电路简单、功能强、修改方便和可靠性高等优点。VIRTEX-II系列FPGA器件有4~12个数字时钟管理器DCM,每个DCM都提供了应用范围广、功能强大的时钟管理功能。如时钟去时滞、频率合成及移相等。它利用延时锁定环DLL,消除时钟焊盘和内部时钟引脚间的摆动,同时它还提供多种时钟控制技术,实现时钟周期内任意位置的精确相位控制,非常适合时序微调应用,对设置和保持时序对准非常关键。
DCM相移具有可变相移和固定相移两种模式。设计中,由于延时量由用户外部输入提供,故采用可变相移模式。在可变相移模式中,用户可以动态地反复将相位向前或向后移动输入时钟周期的1/256。可变相移模式中,相移控制针如表1所示。当PSEN信号有效,则相移值可以由与相移时钟PSCLK同步的PSINCDEC信号决定动态地增加或减少,本设计中相移时钟由输入时钟提供。PSDONE输出信号与相移时钟同步,它输出一个相移时钟周期的高电平表示相移已经完成,同时表示一个新的相移可以开始。输入时钟经过DCM移相电路移相后,得到所需延时之后的时钟输出。将该输出时钟作为双口RAM读出数据时地址发生器・的触发时钟及双DA进行数据转换的时钟输入,便可以实现回波信号的精确延时。
表1 相移控制针
控制针方 向功 能PSINCDEC输入相增加或者减少PSEN输入使能加减相移PSCLK输入相移时钟PSDONE输出移相完成后使能如前所述,毫米波多目标信号产生的关键是实现回波信号之间极高的距离分辨率。本文采用FPGA提供精确时延实现多目标信号产生的方法,为系统调试提供了极为有效的手段。设计采用自顶向下的设计方法,采用硬件描述语言VHDL完成DCM移相、状态机控制及参数输入三大功能模块的设计输入。DCM的相移模式为可变相移模式。根据用户输入的所需延时量,在-64~+64之间取一个整数相移值,通过时钟选择器选择用CLK0、CLKl80实现0~10ns的多种时延。
DCM工作在可变相移模式,因此对其移相操作的控制相对复杂。数字电路常用的控制单元有状态机及时序电路、状态机实现控制等优化设计。采用状态机编辑器,用户不用自己写HDL代码,只要输入功能块的状态机图表描述,编辑器就可以自动生成与此描述相对应的HDL代码,使设计变得异常灵活方便。状态机的主要功能是产生DCM的PSEN输人信号,控制DCM的相移操作,同时给出相移完成提示信号PSSUCCEED。
状态机如图2所示,共有6个状态。本系统状态转移与输入时钟同步。在系统复位后,状态机进入初始状态状态1,用户输入所需要的相移量,给出开始相移信号后,状态机接收到DCM锁定及开始相移信号,便检测输入的相移量是否为0。如果为0,状态机直接进入末状态;如果相移量不为0,则进入状态2,并对PSEN赋一个相移时钟周期的高电平,使DCM进行一次相移;当相移时钟上升延到达,则无条件转入状态3,直到DCM的PSDONE输出变为1,状态3进入状态4,并再给PSEN赋一个相移时钟周期的高电平。相移时钟上升延到达后,状态4五条件转入状态5;如果相移未达到所需要的值,则状态5进人状态2,直到相移值达到所需的值后,状态5进入末状态6,PSSUCCEED输出变为高电平。
3 仿真结果
设计中采用仿真工具ACTIVE-HDL 5.1软件对系统进行功能仿真及布局布线之后的后仿真,图3、图4、图5是使用该软件对产生时钟延时部分进行功能仿真的部分仿真结果。输入时钟CLK频率为50MHz,其中RESET为系统复位信号,DELAYIN为需要的十六进制的延时输入,START为启动时钟延时操作信号,CLKOUT为输出时钟,LOCKED为DCM锁定信号,CLK0为DCM的CLK0输出。PSSUCCEED输出表示用户所需要的延时操作已完成,高有效。当不对时钟进行延时,则输出时钟沿完全与输入时钟沿同步,如图3所示,显示整个移相操作完成后,输入输出时钟沿处在同一时间点1030ns处。图4所示为对时钟进行2ns延时的仿真结果,显示整个移相操作完成后,输入时钟沿在4150ns处时,输出时钟沿在4152.053ns处,且输出时钟选择CLK0。图5所示为对时钟进行6ns延时的仿真结果,显示整个移相操作完成后,输入时钟沿在7150ns处时,输出时钟沿在7156.037ns处,且输出时钟选择CLKl80。
通过以上仿真结果证明这种方法能够精确实现各种时延,其延时精确到了0.1ns。该延时体现在双DA的转换时钟上,则由双DA转化得到的模拟信号之间也会相应地产生各种时延。该多目标信号产生的设计方法已在实际雷达模拟器中得到应用,此方法对于其它类似的应用场合也具有很好的实际参考价值。
篇9:linux进程管理之信号处理
另外,内核经常使用force_sig_info()/force_sig()来给进程发送信号.这样的信号经常不可以忽略,不可以阻塞.我们来看一下它的处理.代码如下:
int force_sig_info(int sig, struct siginfo *info, struct task_struct *t)
{
unsigned long int flags;
int ret, blocked, ignored;
struct k_sigaction *action;
spin_lock_irqsave(&t->sighand->siglock, flags);
//取进程的信号的处理函数
action = &t->sighand->action[sig-1];
//如果该信号被忽略或者该信号被阻塞
ignored = action->sa.sa_handler == SIG_IGN;
blocked = sigismember(&t->blocked, sig);
if (blocked || ignored) {
//重信号处理函数为默认的处理
action->sa.sa_handler = SIG_DFL;
//如果信号被屏弊
if (blocked) {
//清除信号屏弊位
sigdelset(&t->blocked, sig);
//重新计算进程是否有末处理的信号
recalc_sigpending_and_wake(t);
}
}
//“特殊”的信号发送
ret = specific_send_sig_info(sig, info, t);
spin_unlock_irqrestore(&t->sighand->siglock, flags);
return ret;
}
当进程的信号阻塞标志被更改时,就会引起TIF_SIGPENDING标志的变化.对于TIF_SIGPENDING标志的检测是在recalc_sigpending_and_wake()调用recalc_sigpending_tsk()来完成的.它实际是判断等待队列中是否有没有被阻塞的信号.如果有,则设置TIF_SIGPENDING标志.
specific_send_sig_info()内核用于将信号发送到进程.我们比较一下它跟用户空间的发送有什么不同.它的代码如下:
static int
specific_send_sig_info(int sig, struct siginfo *info, struct task_struct *t)
{
int ret = 0;
BUG_ON(!irqs_disabled());
assert_spin_locked(&t->sighand->siglock);
/* Short-circuit ignored signals. */
//信号被忽略,退出
if (sig_ignored(t, sig))
goto out;
/* Support queueing exactly one non-rt signal, so that we
can get more detailed information about the cause of
the signal. */
//如果不是实时信号,且已经有信号在等待队列中了.直接等待(不排队)
if (LEGACY_QUEUE(&t->pending, sig))
goto out;
//将信号发送到目标进程
ret = send_signal(sig, info, t, &t->pending);
// TODO: 这里调用signal_wake_up()直接唤醒进程
if (!ret && !sigismember(&t->blocked, sig))
signal_wake_up(t, sig == SIGKILL);
out:
return ret;
}
这样,内核就将信号传送给目标进程.无论进程用什么样的方式,都不能阻止对此信号的处理.
四:信号的处理
信号处理的时机:每次从内核空间返回用户空间时,都会检查当前进程是否有末处理的信号.如果有,则对信号进行处理
信号的处理函数如下:
static void fastcall do_signal(struct pt_regs *regs)
{
siginfo_t info;
int signr;
struct k_sigaction ka;
sigset_t *oldset;
//判断是否是处于返回到用户空间的前夕.不需要处理
if (!user_mode(regs))
return;
//要从task->saved_sigmask中恢复进程信号掩码
if (test_thread_flag(TIF_RESTORE_SIGMASK))
ldset = ¤t->saved_sigmask;
else
ldset = ¤t->blocked;
//对等待信号的处理
//只有遇到用户重设信号处理函数的信号或者处理完等待信号才会返回
signr = get_signal_to_deliver(&info, &ka, regs, NULL);
if (signr > 0) {
//对用户设置了信号处理函数的信号处理
if (unlikely(current->thread.debugreg[7]))
set_debugreg(current->thread.debugreg[7], 7);
/* Whee! Actually deliver the signal. */
if (handle_signal(signr, &info, &ka, oldset, regs) == 0) {
if (test_thread_flag(TIF_RESTORE_SIGMASK))
clear_thread_flag(TIF_RESTORE_SIGMASK);
}
return;
}
//没有Catch信号的系统调用重启
/* Did we come from a system call? */
if (regs->orig_eax >= 0) {
/* Restart the system call - no handlers present */
switch (regs->eax) {
case -ERESTARTNOHAND:
case -ERESTARTSYS:
case -ERESTARTNOINTR:
regs->eax = regs->orig_eax;
regs->eip -= 2;
break;
//如果是返回-ERESTART_RESTARTBLOCK ,返回用户空间后重新发起
//系统调用.系统调用号为__NR_restart_syscall
//一般用在与timer有关的系统调用中
case -ERESTART_RESTARTBLOCK:
regs->eax = __NR_restart_syscall;
regs->eip -= 2;
break;
}
}
/* if there's no signal to deliver, we just put the saved sigmask
* back */
if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
//清除TIF_RESTORE_SIGMASK 并恢复信号掩码
clear_thread_flag(TIF_RESTORE_SIGMASK);
sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL);
}
}
正好我们在上节发送信号中所论述的一样,信号可能会引起系统调用中断.这里必须要采取必要的措施来使系统调用重启.
关于返回值与重启还是忽略如下表如示(摘自<< understanding the linux kernel >>):
SignalAction
EINTRERESTARTSYSERESTARTNOHANDERESTART_RESTARTBLOCKa
ERESTARTNOINTR DefaultTerminateReexecuteReexecuteReexecute IgnoreTerminateReexecuteReexecuteReexecute CatchTerminateDependsTerminateReexecute有必要关注一下上面的系统调用重启过程:
Regs参数表示用户空的硬件环境.regs->eax是表示返回用户空间后的eax寄存器的值.regs->eip是返回用户空间后执行的指针地址. regs->orig_eax是表示系统调用时eax的值,里面存放着系统调用号.请参阅本站的有关中断初始化的文档.
Regs->eip -= 2 ,为什么eip要减2呢?因为发现系统调用是int 0x80 指令.中断后,eip会指向int 80后面的一条指令.这样,如果要重新执新int 0x80.那就必须要把eip返回两条指令.
转入get_signal_to_deliver():
int get_signal_to_deliver(siginfo_t *info, struct k_sigaction *return_ka,
struct pt_regs *regs, void *cookie)
{
sigset_t *mask = ¤t->blocked;
int signr = 0;
//选择编译函数
try_to_freeze();
relock:
spin_lock_irq(¤t->sighand->siglock);
for (;;) {
struct k_sigaction *ka;
if (unlikely(current->signal->group_stop_count > 0) &&
handle_group_stop())
goto relock;
//从等待队列中取信号
signr = dequeue_signal(current, mask, info);
//信号为空,退出
if (!signr)
break; /* will return 0 */
//当前进程正在被跟踪
if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) {
ptrace_signal_deliver(regs, cookie);
/* Let the debugger run. */
ptrace_stop(signr, signr, info);
/* We're back. Did the debugger cancel the sig? */
signr = current->exit_code;
if (signr == 0)
continue;
current->exit_code = 0;
/* Update the siginfo structure if the signal has
changed. If the debugger wanted something
specific in the siginfo structure then it should
have updated *info via PTRACE_SETSIGINFO. */
if (signr != info->si_signo) {
info->si_signo = signr;
info->si_errno = 0;
info->si_code = SI_USER;
info->si_pid = task_pid_vnr(current->parent);
info->si_uid = current->parent->uid;
}
/* If the (new) signal is now blocked, requeue it. */
if (sigismember(¤t->blocked, signr)) {
specific_send_sig_info(signr, info, current);
continue;
}
}
ka = ¤t->sighand->action[signr-1];
//信号被忽略,不做任何处理
if (ka->sa.sa_handler == SIG_IGN) /* Do nothing. */
continue;
//如果不为默认操作.也就是说用户已经重置了该信号的处理
//这样情况下会调用break退出循环
if (ka->sa.sa_handler != SIG_DFL) {
/* Run the handler. */
*return_ka = *ka;
//如果定义了SA_ONESHOT 标志,指明信号处理完之后,恢复信号的默认处理
if (ka->sa.sa_flags & SA_ONESHOT)
ka->sa.sa_handler = SIG_DFL;
break; /* will return non-zero “signr” value */
}
/*
* Now we are doing the default action for this signal.
*/
//如果是内核所忽略的信号,不做任何处理
//这里注意了.Child信号的默认处理是忽略.这就是形成僵尸进程
//的主要原因
if (sig_kernel_ignore(signr)) /* Default is nothing. */
continue;
/*
* Global init gets no signals it doesn't want.
*/
//判断是否是INIT 进程
if (is_global_init(current))
continue;
//引起进程挂起的信号
if (sig_kernel_stop(signr)) {
//SIGSTOP的处理与其它会引起停止的信号有点不同
//SIGSTOP总是停止进程,而其它信号只会停止不在孤儿进程组
//中的进程
if (signr != SIGSTOP) {
spin_unlock_irq(¤t->sighand->siglock);
/* signals can be posted during this window */
if (is_current_pgrp_orphaned())
goto relock;
spin_lock_irq(¤t->sighand->siglock);
}
//停止进程
if (likely(do_signal_stop(signr))) {
/* It released the siglock. */
goto relock;
}
/*
* We didn't actually stop, due to a race
* with SIGCONT or something like that.
*/
continue;
}
spin_unlock_irq(¤t->sighand->siglock);
//除去内核忽略和引起进程停止的信号之处的所有信号都会让过程
//终止
/*
* Anything else is fatal, maybe with a core dump.
*/
//置进程标志位PF_SIGNALED.表示该信号终止是由信号引起的
current->flags |= PF_SIGNALED;
if ((signr != SIGKILL) && print_fatal_signals)
print_fatal_signal(regs, signr);
//如果是一些会引起核心转储的信号
//建立核心转储文件后退出
if (sig_kernel_coredump(signr)) {
/*
* If it was able to dump core, this kills all
* other threads in the group and synchronizes with
* their demise. If we lost the race with another
* thread getting here, it set group_exit_code
* first and our do_group_exit call below will use
* that value and ignore the one we pass it.
*/
do_coredump((long)signr, signr, regs);
}
/*
* Death signals, no core dump.
*/
//进程组退出
do_group_exit(signr);
/* NOTREACHED */
}
spin_unlock_irq(¤t->sighand->siglock);
return signr;
}
这个函数比较简单,基本上就是遍历信号等待队列.然后处理信号.一直遇到信号处理被重设或者没有等待信号之后才会返回.
信号出列函数为dequeue_signal():
int dequeue_signal(struct task_struct *tsk, sigset_t *mask, siginfo_t *info)
{
int signr = 0;
/* We only dequeue private signals from ourselves, we don't let
* signalfd steal them
*/
//从pending 队列中取出等待信号
signr = __dequeue_signal(&tsk->pending, mask, info);
//如果pending 队列中没有等待信号,则从shared_pending中取
if (!signr) {
signr = __dequeue_signal(&tsk->signal->shared_pending,
mask, info);
//如果是SIGALRM 信号
//重启计时器
if (unlikely(signr == SIGALRM)) {
struct hrtimer *tmr = &tsk->signal->real_timer;
if (!hrtimer_is_queued(tmr) &&
tsk->signal->it_real_incr.tv64 != 0) {
hrtimer_forward(tmr, tmr->base->get_time(),
tsk->signal->it_real_incr);
hrtimer_restart(tmr);
}
}
}
//重新判断是位还有末处理的信号,更新TIF_SIGPENDING 标志
recalc_sigpending();
//会引起进程终止的信号,置SIGNAL_STOP_DEQUEUED 标志
//禁止信号出列,即阻止后续的信号处理
if (signr && unlikely(sig_kernel_stop(signr))) {
if (!(tsk->signal->flags & SIGNAL_GROUP_EXIT))
tsk->signal->flags |= SIGNAL_STOP_DEQUEUED;
}
//__SI_TIMER : 定时器到期
if (signr &&
((info->si_code & __SI_MASK) == __SI_TIMER) &&
info->si_sys_private){
spin_unlock(&tsk->sighand->siglock);
do_schedule_next_timer(info);
spin_lock(&tsk->sighand->siglock);
}
return signr;
}
__dequeue_signal()用于从等待队列中取出信号.代码如下:
static int __dequeue_signal(struct sigpending *pending, sigset_t *mask,
siginfo_t *info)
{
//取位图中为第一个为1的标志位
int sig = next_signal(pending, mask);
if (sig) {
//如果定义了进程通告?
//task->notifier:指向一个函数指针. 设备驱动程序用它来阻塞某些信号
if (current->notifier) {
if (sigismember(current->notifier_mask, sig)) {
if (!(current->notifier)(current->notifier_data)) {
clear_thread_flag(TIF_SIGPENDING);
return 0;
}
}
}
//将信号从等待队列中移除,更新等待信号标志位
if (!collect_signal(sig, pending, info))
sig = 0;
}
return sig;
}
Cllect_signal()代码如下:
static int collect_signal(int sig, struct sigpending *list, siginfo_t *info)
{
struct sigqueue *q, *first = NULL;
int still_pending = 0;
//要处理的信号没有包含在等待队列中,退出
if (unlikely(!sigismember(&list->signal, sig)))
return 0;
/*
* Collect the siginfo appropriate to this signal. Check if
* there is another siginfo for the same signal.
*/
//遍历等待队列.如果不止有一个sig 信号在等待.still_pending为1
list_for_each_entry(q, &list->list, list) {
if (q->info.si_signo == sig) {
if (first) {
still_pending = 1;
break;
}
first = q;
}
}
if (first) {
//如果等待队列中有此信号
//在等待队列中将它删除
list_del_init(&first->list);
//将信号信号copy 到info
copy_siginfo(info, &first->info);
//释放信号
__sigqueue_free(first);
if (!still_pending)
//如果只有一个信号在等待,也就是说该类的等待信号已经处理完了
//从等待位图中删除该位
sigdelset(&list->signal, sig);
} else {
/* Ok, it wasn't in the queue. This must be
a fast-pathed signal or we must have been
out of queue space. So zero out the info.
*/
//如果等待队列中没有此信号,将对应位图置0.
//info信号置空
sigdelset(&list->signal, sig);
info->si_signo = sig;
info->si_errno = 0;
info->si_code = 0;
info->si_pid = 0;
info->si_uid = 0;
}
return 1;
}
返回do_signal()中看看如果信号处理函数被重设会怎么样处理.这也是信号处理中比较难理解的部份.转入具体的处理代码之前,先思考一下:
用户空间的函数地址传递给内核空间之后,可不可以在内核直接运行呢?(即设置好内核堆,再把eip设为fuction address)?
是有可能运行的.因为内核切占不会切换CR3.用户进程切换会切换CR3.因此可以保证进程陷入内核后可以正常的对用户空间的地址进行寻址.但是基于以下几点原因.不建议直接在内核空间运行
1:安全因素.陷入内核空间后,对内核地址空间具有全部访问权限,没有内存保护进制
2:内核堆栈过小,最大只有8KB.
3:用户空间的函数在运行的时候可能会发出系统调用.由于在最高特权级下,导致系统调用/异常处理失败.
既然这样,那怎么运行信号处理函数呢?
我们只需要让它在返回用户空间后马上运行信号处理函数,运行信号处理函数再系统调用返回内核就可以了.
先分析一下有关的数据结构:
struct sigframe
{
//信号处理函数的返回地址,它指向同一个结构中的retcode字段
char __user *pretcode;
//信号数值
int sig;
//保存当前regs的一个结构
struct sigcontext sc;
//保存FPU,MMX,XMM等相关信息
struct _fpstate fpstate;
//被阻塞的实时信号的位数组
unsigned long extramask[_NSIG_WORDS-1];
//信号处理程序运行完后执行的执令
char retcode[8];
}
现在我们转入代码看是如何处理的:
static int
handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
sigset_t *oldset, struct pt_regs * regs)
{
int ret;
//在执行系统调用的时候,可能被信号给中断了.
//要根据返回值判断是否可以重启系统调用
/* Are we from a system call? */
if (regs->orig_eax >= 0) {
/* If so, check system call restarting.. */
switch (regs->eax) {
case -ERESTART_RESTARTBLOCK:
case -ERESTARTNOHAND:
regs->eax = -EINTR;
break;
case -ERESTARTSYS:
if (!(ka->sa.sa_flags & SA_RESTART)) {
regs->eax = -EINTR;
break;
}
/* fallthrough */
case -ERESTARTNOINTR:
regs->eax = regs->orig_eax;
regs->eip -= 2;
}
}
/*
* If TF is set due to a debugger (PT_DTRACE), clear the TF flag so
* that register information in the sigcontext is correct.
*/
//如果处于跟踪状态
//就像进行中断处理程序,关闭中断一样
if (unlikely(regs->eflags & TF_MASK)
&& likely(current->ptrace & PT_DTRACE)) {
current->ptrace &= ~PT_DTRACE;
regs->eflags &= ~TF_MASK;
}
/* Set up the stack frame. */
//SA_SIGINFO:为信号处理提供额外的信息
//建立帧结构
if (ka->sa.sa_flags & SA_SIGINFO)
ret = setup_rt_frame(sig, ka, info, oldset, regs);
else
ret = setup_frame(sig, ka, oldset, regs);
if (ret == 0) {
spin_lock_irq(¤t->sighand->siglock);
sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask);
//SA_NODEFER:在执行信号处理函数的时候,不屏弊信号
if (!(ka->sa.sa_flags & SA_NODEFER))
//如果没有定义SA_NODEFER.那屏弊掉当前信号
sigaddset(¤t->blocked,sig);
//更新TIF_SIGPENDING 标志位
recalc_sigpending();
spin_unlock_irq(¤t->sighand->siglock);
}
return ret;
}
首先,也要恢复被中断的系统调用.然后,再调用setup_frame()或者是setup_rt_frame().setup_rt_frame()是有跟实时信号有关的.在这里以setup_frame()为例进行分析.代码如下:
static int setup_frame(int sig, struct k_sigaction *ka,
sigset_t *set, struct pt_regs * regs)
{
void __user *restorer;
struct sigframe. __user *frame;
int err = 0;
int usig;
//取得在用户空间栈中存放frame的位置
frame. = get_sigframe(ka, regs, sizeof(*frame));
//检查是否是可写的
if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
goto give_sigsegv;
//在有的执行域里.信号的数值可能不一样,因此,需要转换一下
//signal_invmap:信号转换表
usig = current_thread_info()->exec_domain
&& current_thread_info()->exec_domain->signal_invmap
&& sig < 32
? current_thread_info()->exec_domain->signal_invmap[sig]
: sig;
err = __put_user(usig, &frame->sig);
if (err)
goto give_sigsegv;
//保存当前内核栈里保存的用户空间的硬件环境
err = setup_sigcontext(&frame->sc, &frame->fpstate, regs, set->sig[0]);
if (err)
goto give_sigsegv;
//set:这里是表示进程以前的信号掩码
//在extramask里保存以前的信号掩码
if (_NSIG_WORDS > 1) {
//将set->sig的高32位存于extramask中
err = __copy_to_user(&frame->extramask, &set->sig[1],
sizeof(frame->extramask));
if (err)
goto give_sigsegv;
}
if (current->binfmt->hasvdso)
restorer = (void *)VDSO_SYM(&__kernel_sigreturn);
else
restorer = (void *)&frame->retcode;
if (ka->sa.sa_flags & SA_RESTORER)
restorer = ka->sa.sa_restorer;
/* Set up to return from userspace. */
//使frame->pretcode指向 frame->retcode
err |= __put_user(restorer, &frame->pretcode);
/*
* This is popl %eax ; movl $,%eax ; int $0x80
*
* WE DO NOT USE IT ANY MORE! It's only left here for historical
* reasons and because gdb uses it as a signature to notice
* signal handler stack frames.
*/
//frame->retcode:执行完信号处理函数后的下一条指令
//这里构建了一次系统调用.调用号是__NR_sigreturn
err |= __put_user(0xb858, (short __user *)(frame->retcode+0));
err |= __put_user(__NR_sigreturn, (int __user *)(frame->retcode+2));
err |= __put_user(0x80cd, (short __user *)(frame->retcode+6));
if (err)
goto give_sigsegv;
/* Set up registers for signal handler */
//因为regs结构已经保存在frame之中了.这里可以随意的修改
//修改用户空间的栈指针位置,指向frame
regs->esp = (unsigned long) frame;
//返回到用户空间的下一条指令
//即返回到用户空间后,执行信号处理程序
regs->eip = (unsigned long) ka->sa.sa_handler;
regs->eax = (unsigned long) sig;
regs->edx = (unsigned long) 0;
regs->ecx = (unsigned long) 0;
//用户空间的段寄存器都是__USER_DS
//这里是为了防止有意外的修改
regs->xds = __USER_DS;
regs->xes = __USER_DS;
regs->xss = __USER_DS;
regs->xcs = __USER_CS;
/*
* Clear TF when entering the signal handler, but
* notify any tracer that was single-stepping it.
* The tracer may want to single-step inside the
* handler too.
*/
//清除跟踪标志
//就像是处理中断处理程序,清除中断标志位一样
regs->eflags &= ~TF_MASK;
if (test_thread_flag(TIF_SINGLESTEP))
ptrace_notify(SIGTRAP);
#if DEBUG_SIG
printk(“SIG deliver (%s:%d): sp=%p pc=%p ra=%pn”,
current->comm, current->pid, frame, regs->eip, frame->pretcode);
#endif
return 0;
give_sigsegv:
force_sigsegv(sig, current);
return -EFAULT;
}
get_sigframe()代码如下:
static inline void __user *
get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size)
{
unsigned long esp;
/* Default to using normal stack */
esp = regs->esp;
/* This is the X/Open sanctioned signal stack switching. */
//用户指定了栈位置
//sas__ss_flags:判断指定的栈位置是否为于当前栈的下部有效空间
//进程的地址空间中,栈空间占据着最上部
if (ka->sa.sa_flags & SA_ONSTACK) {
if (sas_ss_flags(esp) == 0)
//获得栈顶位置
esp = current->sas_ss_sp + current->sas_ss_size;
}
/* This is the legacy signal stack switching. */
//从Unix中遗留的调用.为了保持兼容性而设置
//不提 倡使用
else if ((regs->xss & 0xffff) != __USER_DS &&
!(ka->sa.sa_flags & SA_RESTORER) &&
ka->sa.sa_restorer) {
esp = (unsigned long) ka->sa.sa_restorer;
}
//为frame结构空出位置
esp -= frame_size;
/* Align the stack pointer according to the i386 ABI,
* i.e. so that on function entry ((sp + 4) & 15) == 0. */
//按照i386 ABI规范.对齐栈指针
esp = ((esp + 4) & -16ul) - 4;
return (void __user *) esp;
}
setup_sigcontext()代码如下:
static int
setup_sigcontext(struct sigcontext __user *sc, struct _fpstate __user *fpstate,
struct pt_regs *regs, unsigned long mask)
{
int tmp, err = 0;
//保存regs
err |= __put_user(regs->xfs, (unsigned int __user *)&sc->fs);
savesegment(gs, tmp);
err |= __put_user(tmp, (unsigned int __user *)&sc->gs);
err |= __put_user(regs->xes, (unsigned int __user *)&sc->es);
err |= __put_user(regs->xds, (unsigned int __user *)&sc->ds);
err |= __put_user(regs->edi, &sc->edi);
err |= __put_user(regs->esi, &sc->esi);
err |= __put_user(regs->ebp, &sc->ebp);
err |= __put_user(regs->esp, &sc->esp);
err |= __put_user(regs->ebx, &sc->ebx);
err |= __put_user(regs->edx, &sc->edx);
err |= __put_user(regs->ecx, &sc->ecx);
err |= __put_user(regs->eax, &sc->eax);
err |= __put_user(current->thread.trap_no, &sc->trapno);
err |= __put_user(current->thread.error_code, &sc->err);
err |= __put_user(regs->eip, &sc->eip);
err |= __put_user(regs->xcs, (unsigned int __user *)&sc->cs);
err |= __put_user(regs->eflags, &sc->eflags);
err |= __put_user(regs->esp, &sc->esp_at_signal);
err |= __put_user(regs->xss, (unsigned int __user *)&sc->ss);
//保存FPU,XMM.MXX等信息
tmp = save_i387(fpstate);
if (tmp < 0)
err = 1;
else
err |= __put_user(tmp ? fpstate : NULL, &sc->fpstate);
/* non-iBCS2 extensions.. */
//mask:即为set->sig 的低32位
err |= __put_user(mask, &sc->oldmask);
err |= __put_user(current->thread.cr2, &sc->cr2);
return err;
}
用下图表示上述的操作:
注意到代码中有以下两条指令:
regs->esp = (unsigned long) frame;
regs->eip = (unsigned long) ka->sa.sa_handler;
第一条把用户的栈指令指向了frame
第二条把返回用户空间的eip设为了信号的处理函数.
这样返回到用户空间后就会执行ka->sa.sa_handler这个函数.注意到上面的堆栈结构,其实它模拟了一次函数调用.函数调用时,先把参数压栈,再把返回地址压栈.在上面的栈中,函数的参数为sig.返回地址为pretcode.这样,在信号处理函数返回之后.就会把pretcode装入eip.而pretcode又是指向retcode.也就是说函数返回之后,会运行retcode对应的指令.
Retcode在上面的代码中是这样被设置的:
err |= __put_user(0xb858, (short __user *)(frame->retcode+0));
err |= __put_user(__NR_sigreturn, (int __user *)(frame->retcode+2));
err |= __put_user(0x80cd, (short __user *)(frame->retcode+6));
代码中的0xb858 0x80cd可能对应的就是指令的机器码.它相应于如下指令:
popl %eax ;
movl $,%eax ;
int $0x80即会产生一个系统调用号为__NR_sigreturn的系统调用.它对应的入口是:
asmlinkage int sys_sigreturn(unsigned long __unused)
{
//第一个参数地址就是栈指针位置
struct pt_regs *regs = (struct pt_regs *) &__unused;
//esp-8是因为在用户空间运行的时候,栈出了两个单元
//即上图中的pretcode出栈.sig出栈
struct sigframe. __user *frame. = (struct sigframe. __user *)(regs->esp - 8);
sigset_t set;
int eax;
//检查对应区域是否可读
if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
goto badframe;
//从frame->sc.oldmask 恢复set.sig的低32 位
if (__get_user(set.sig[0], &frame->sc.oldmask)
|| (_NSIG_WORDS > 1
//从frame->extramask 中恢复set.sig的高32位
&& __copy_from_user(&set.sig[1],frame->extramask,
sizeof(frame->extramask))))
goto badframe;
sigdelsetmask(&set, ~_BLOCKABLE);
spin_lock_irq(¤t->sighand->siglock);
current->blocked = set;
//重新判断是否还有末处理的信号
recalc_sigpending();
spin_unlock_irq(¤t->sighand->siglock);
//从frame->sc中恢复系统调用前的硬件环境
if (restore_sigcontext(regs, &frame->sc, &eax))
goto badframe;
return eax;
badframe.:
if (show_unhandled_signals && printk_ratelimit())
printk(“%s%s[%d] bad frame. in sigreturn frame.:%p eip:%lx”
“ esp:%lx oeax:%lxn”,
task_pid_nr(current) > 1 ? KERN_INFO : KERN_EMERG,
current->comm, task_pid_nr(current), frame, regs->eip,
regs->esp, regs->orig_eax);
force_sig(SIGSEGV, current);
return 0;
}至此,内核栈又回复到以前的样子了.
五:小结
本节中,在Linux内核中跟踪了信号处理函数的设置,信号的发送.信号的处理.涉及到的代码都不是很难理解.在理解了用户自定义的信号函数的运行机制之后,我们也很容易调用用户空间的一个特定操作.另外,虽然内核涉及到的信号处理比较简单,但要在用户空间使用好信号就要看一个人的程序设计功底了.
文档为doc格式