于是我telnet登录异常设备,通过tftp http ftp上传下载文件到PC,发现都正常。
起初怀疑是设备网卡driver出现问题,但是细想网卡driver处于数据链路层,
上层(不管应用层是哪种协议)传下来的数据包对于driver来说是一样的。tftp ftp http能正常工作,说明网卡driver能正常收发数据, ping应该也能正常工作才对。
仔细看ping返回的结果,发现有一些不一样的地方,如下:
# ping 10.0.14.198 PING 10.0.14.198 (10.0.14.198): 56 data bytes 64 bytes from 10.0.14.198: seq=0 ttl=127 time=0.817 ms ^C --- 10.0.14.198 ping statistics --- 1 packets transmitted, 1 packets received, 0% packet loss round-trip min/avg/max = 0.817/0.817/0.817 ms
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define MAXPROCESS 65535
#define SLEEPTIME 1
void main (int argc , char ** argv)
{
pid_t pid;
int count = 0;
int maxprocess = MAXPROCESS;
if (argc == 2) {
maxprocess = atoi(argv[1]);
}
for (count = 0; count < maxprocess; count++)
{
pid = fork();
if (pid < 0) {
perror("fork error");
exit(1);
} else if (pid == 0) {
printf("child %d start\n", count);
sleep(SLEEPTIME);
printf("child %d end\n", count);
exit(0);
}
printf("parent:create %d child\n", count);
}
for (count = 0; count < MAXPROCESS; count++) {
wait();
}
exit(0);
}
# ./fork_test 1 parent:create 0 child child 0 start ^C #发现程序不退出,根据打印发现是子进程sleep没有退出,咦,这是什么情况。
# cat /proc/sys/kernel/pid_max 32768观察正常设备pid,发现系统pid在达到32768后会从0-32768中再找已释放的pid使用。
(4)设备端date系统时间走180s回跳
直觉感觉要从date这个现象入手,首先找date命令实现,嵌入式设备文件系统中使用的是busybox,其中有简化的date命令,也可以找glibc库来查看完整版本的date命令。这里不再详述date实现,date最终是调用gettimeofday来获取时间。
void do_gettimeofday(struct timeval *tv)
{
struct timespec now;
getnstimeofday(&now);
tv->tv_sec = now.tv_sec;
tv->tv_usec = now.tv_nsec/1000;
}
void getnstimeofday(struct timespec *ts)
{
unsigned long seq;
s64 nsecs;
WARN_ON(timekeeping_suspended);
do {
seq = read_seqbegin(&timekeeper.lock);
*ts = timekeeper.xtime;
nsecs = timekeeping_get_ns();
/* If arch requires, add in gettimeoffset() */
nsecs += arch_gettimeoffset();
} while (read_seqretry(&timekeeper.lock, seq));
timespec_add_ns(ts, nsecs);
}
static inline s64 timekeeping_get_ns(void)
{
cycle_t cycle_now, cycle_delta;
struct clocksource *clock;
/* read clocksource: */
clock = timekeeper.clock;
cycle_now = clock->read(clock);
/* calculate the delta since the last update_wall_time: */
cycle_delta = (cycle_now - clock->cycle_last) & clock->mask;
/* return delta convert to nanoseconds using ntp adjusted mult. */
return clocksource_cyc2ns(cycle_delta, timekeeper.mult,
timekeeper.shift);
}do_gettimeofday调用getnstimeofday,最关键的是timerkeeper.xtime,这是kernel的墙上时间,xtime的更新是在kernel下clockevent注册的时钟中断,只要kernel时钟中断正常,xtime时间就会不断被更新。gettimeofday <===获取=== xtime <===更新=== clockevent clocksource
struct timespec {
__kernel_time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};tv_sec和tv_nsec表示了从1970-1-1以来的时间。
unsigned long get_seconds(void)
{
return timekeeper.xtime.tv_sec;
}
EXPORT_SYMBOL(get_seconds);
#include <linux/mm.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/mman.h>
#include <linux/random.h>
#include <linux/init.h>
#include <linux/raw.h>
#include <linux/tty.h>
#include <linux/capability.h>
#include <linux/ptrace.h>
#include <linux/device.h>
#include <linux/highmem.h>
#include <linux/crash_dump.h>
#include <linux/backing-dev.h>
#include <linux/bootmem.h>
#include <linux/splice.h>
#include <linux/pfn.h>
#include <linux/export.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define GET_XTIME 0
static int dev_open(struct inode *inode, struct file *filp)
{
return 0;
}
static ssize_t dev_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
return 0;
}
static ssize_t dev_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
return 0;
}
static long dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int __user *argp = (int __user *)arg;
unsigned long now = 0;
switch (cmd) {
case GET_XTIME :
now = get_seconds();
if (copy_to_user(argp, &now, 4))
return -EFAULT;
break;
default :
return -EFAULT;
}
return 0;
}
static const struct file_operations dev_fops = {
.read = dev_read,
.write = dev_write,
.open = dev_open,
.unlocked_ioctl = dev_ioctl,
};
static struct cdev char_dev;
static int major;
static int __init char_dev_init(void)
{
int rc;
int err;
dev_t devid;
rc = alloc_chrdev_region(&devid, 0, 1, "char_dev");
if (rc != 0)
{
printk("alloc chardev region failed\n");
return -1;
}
major = MAJOR(devid);
cdev_init(&char_dev, &dev_fops);
cdev_add(&char_dev, devid, 1);
return 0;
}
static void __exit char_dev_exit(void)
{
cdev_del(&char_dev);
unregister_chrdev_region(MKDEV(major,0), 1);
}
module_init(char_dev_init);
module_exit(char_dev_exit);
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#define GET_XTIME 0
void main(void)
{
unsigned long now = 0;
struct timeval tv;
struct timezone tz;
gettimeofday(&tv, &tz);
printf("tv.tv_sec = %d\n", tv.tv_sec);
printf("tv.tv_usec = %d\n", tv.tv_usec);
int fd = open("/dev/char_dev", O_RDWR);
if (fd < 0)
{
printf("open failed\n");
return;
}
ioctl(fd, GET_XTIME, &now);
printf("xtime.tv_sec = %d\n", now);
close(fd);
}分别将gettimeofday获取的时间和kernel中xtime的时间打印出来。
# ./dev_tool tv.tv_sec = 1427286832 tv.tv_usec = 617831 xtime.tv_sec = 1427286754 # # # ./dev_tool tv.tv_sec = 1427286835 tv.tv_usec = 17649 xtime.tv_sec = 1427286754 # # # ./dev_tool tv.tv_sec = 1427286840 tv.tv_usec = 281584 xtime.tv_sec = 1427286754很明显可以看出,xtime的时间是停止的,那为什么gettimeofday时间还会走呢?
static cycle_t
timer_get_cycles( struct clocksource *cs )
{
return __raw_readl( IO_ADDRESS( REG_TIMER_TMR2DL ));
}
static struct clocksource timer_clocksource =
{
.name = MTAG_TIMER,
.rating = 300,
.read = timer_get_cycles,
.mask = CLOCKSOURCE_MASK( 32 ),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static u32 notrace
update_sched_clock( void )
{
return __raw_readl(IO_ADDRESS( REG_TIMER_TMR2DL ));
}
static int __init
timer_clocksource_init( void )
{
u32 val = 0, mode = 0;
timer_stop( 2 );
__raw_writel( 0xffffffff, IO_ADDRESS( REG_TIMER_TMR2TGT )); // free-running timer as clocksource
val = __raw_readl( IO_ADDRESS( REG_TIMER_TMRMODE ));
mode = ( val & ~( 0x0f << TIMER2_MODE_OFFSET )) | TIMER2_CONTINUOUS_MODE;
__raw_writel( mode, IO_ADDRESS( REG_TIMER_TMRMODE ));
timer_start( 2 );
setup_sched_clock( update_sched_clock, 32, 24000000 );
if(clocksource_register_hz( &timer_clocksource, 24000000 ))
{
panic("%s: can't register clocksource\n", timer_clocksource.name);
}
return 0;
}
static void
timer_set_mode( enum clock_event_mode mode, struct clock_event_device *evt )
{
u32 val = 0, timermode = 0;
val = __raw_readl( IO_ADDRESS( REG_TIMER_TMRMODE ));
switch( mode )
{
case CLOCK_EVT_MODE_PERIODIC:
timer_stop( 1 );
timermode = ( val & ~( 0x0f << TIMER1_MODE_OFFSET )) | TIMER1_PERIODICAL_MODE;
__raw_writel( TIMER1_TARGET, IO_ADDRESS( REG_TIMER_TMR1TGT ));
__raw_writel( timermode, IO_ADDRESS( REG_TIMER_TMRMODE ));
timer_start( 1 );
break;
case CLOCK_EVT_MODE_ONESHOT:
timer_stop( 1 );
timermode = ( val & ~( 0x0f << TIMER1_MODE_OFFSET )) | TIMER1_ONE_SHOT_MODE;
__raw_writel( TIMER1_TARGET, IO_ADDRESS( REG_TIMER_TMR1TGT ));
__raw_writel( timermode, IO_ADDRESS( REG_TIMER_TMRMODE ));
timer_start( 1 );
break;
case CLOCK_EVT_MODE_UNUSED:
case CLOCK_EVT_MODE_SHUTDOWN:
default:
VLOGD( MTAG_TIMER, "time stop and clr src pnd. mode = %d", mode );
timer_stop(1);
timer_clr_pnd(1);
VLOGD( MTAG_TIMER, "REG_TIMER_TMREN is %u; REG_TIMER_TMRPND is %u", readl(IO_ADDRESS( REG_TIMER_TMREN )), readl(IO_ADDRESS( REG_TIMER_TMRPND )));
break;
}
}
static int
timer_set_next_event( unsigned long cycles, struct clock_event_device *evt )
{
timer_stop( 1 );
__raw_writel( cycles, IO_ADDRESS( REG_TIMER_TMR1TGT ));
timer_start( 1 );
return 0;
}
static struct clock_event_device timer_clockevent =
{
.name = MTAG_TIMER,
.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
.rating = 200,
.set_mode = timer_set_mode,
.set_next_event = timer_set_next_event,
};
static void __init
timer_clockevent_init( void )
{
clockevents_calc_mult_shift( &timer_clockevent, CLOCK_TICK_RATE, 4 );
timer_clockevent.max_delta_ns = clockevent_delta2ns( 0xffffffff, &timer_clockevent );
timer_clockevent.min_delta_ns = clockevent_delta2ns( CLOCKEVENT_MIN_DELTA, &timer_clockevent );
timer_clockevent.cpumask = cpumask_of( 0 );
clockevents_register_device( &timer_clockevent );
}
....
static void
timer_clock_event_interrupt( void )
{
struct clock_event_device *evt = &timer_clockevent;
timer_clr_pnd( 1 );
evt->event_handler( evt );
}
static irqreturn_t
timer_interrupt( int irq, void *dev_id )
{
u32 srcpnd = 0;
struct clock_event_device *evt = &timer_clockevent;
srcpnd = __raw_readl(IO_ADDRESS( REG_TIMER_TMRPND ));
if( srcpnd & TIMER1_EVENT )
{
timer_clock_event_interrupt();
}
__raw_writel( srcpnd, IO_ADDRESS( REG_TIMER_TMRPND ));
return IRQ_HANDLED;
}
static struct irqaction timer_irq =
{
.name = "timer",
.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
.handler = timer_interrupt,
.dev_id = NULL,
};这样中断处理函数退出后就不会再次产生timer intr了。
但是有2个地方我觉得需要验证下:
(1)timer计数达到目标后,状态寄存器是否是stop状态
static irqreturn_t
timer_interrupt( int irq, void *dev_id )
{
u32 srcpnd = 0;
struct clock_event_device *evt = &timer_clockevent;
srcpnd = __raw_readl(IO_ADDRESS( REG_TIMER_TMRPND ));
if( srcpnd & TIMER1_EVENT )
{
timer_clr_pnd( 1 );
evt->event_handler( evt );
}
return IRQ_HANDLED;
}终于解决了这个bug,对于sleep ping的阻塞问题也就可以理解了,sleep ping实现中都使用了定时器,因为xtime不更新,kernel的定时器机制不能工作。
原文:http://blog.csdn.net/skyflying2012/article/details/44623515