于是我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