ARM平台下数据类型char的误区

Char类型自然是Cprogramming中最为常用的数据类型之一,我们都认为再简单不过了,所以使用的时候会如果信手拈来可能会产生不必要的麻烦,甚至是严重的bug,这类错误往往又是比较难debug的。

笔者当前使用的LinuxKernel2.6.27.8,我们先看该版本下JFFS2 file system源代码中的一个例子。这里为了方便理解,列出其源代码:

static int jffs2_garbage_collect_metadata(structjffs2_sb_info *c, struct jffs2_eraseblock *jeb,

                                                            structjffs2_inode_info *f, struct jffs2_full_dnode *fn)

{

            structjffs2_full_dnode *new_fn;

            structjffs2_raw_inode ri;

            structjffs2_node_frag *last_frag;

            unionjffs2_device_node dev;

            char *mdata = NULL, mdatalen = 0;

            uint32_talloclen, ilen;

            int ret;

 

            if(S_ISBLK(JFFS2_F_I_MODE(f)) ||

                S_ISCHR(JFFS2_F_I_MODE(f)) ) {

                        /*For these, we don't actually need to read the old node */

                        mdatalen= jffs2_encode_dev(&dev, JFFS2_F_I_RDEV(f));

                        mdata= (char *)&dev;

                        D1(printk(KERN_DEBUG"jffs2_garbage_collect_metadata(): Writing %d bytes of kdev_t\n",mdatalen));

            } else if(S_ISLNK(JFFS2_F_I_MODE(f))) {

                        mdatalen = fn->size;

                        mdata= kmalloc(fn->size, GFP_KERNEL);

                        if(!mdata) {

                                    printk(KERN_WARNING"kmalloc of mdata failed in jffs2_garbage_collect_metadata()\n");

                                    return-ENOMEM;

                        }

                        ret= jffs2_read_dnode(c, f, fn, mdata, 0, mdatalen);

                        if(ret) {

                                    printk(KERN_WARNING"read of old metadata failed in jffs2_garbage_collect_metadata():%d\n", ret);

                                    kfree(mdata);

                                    returnret;

                        }

                        D1(printk(KERN_DEBUG"jffs2_garbage_collect_metadata(): Writing %d bites of symlinktarget\n", mdatalen));

 

            }

 

            ret = jffs2_reserve_space_gc(c, sizeof(ri) + mdatalen,&alloclen,

                                                JFFS2_SUMMARY_INODE_SIZE);

            if (ret) {

                        printk(KERN_WARNING"jffs2_reserve_space_gc of %zd bytes for garbage_collect_metadata failed:%d\n",

                               sizeof(ri)+ mdatalen, ret);

                        gotoout;

            }

 

            last_frag =frag_last(&f->fragtree);

            if(last_frag)

                        /*Fetch the inode length from the fragtree rather then

                         * from i_size since i_size may have not beenupdated yet */

                        ilen= last_frag->ofs + last_frag->size;

            else

                        ilen= JFFS2_F_I_SIZE(f);

 

            memset(&ri,0, sizeof(ri));

            ri.magic =cpu_to_je16(JFFS2_MAGIC_BITMASK);

            ri.nodetype= cpu_to_je16(JFFS2_NODETYPE_INODE);

            ri.totlen =cpu_to_je32(sizeof(ri) + mdatalen);

            ri.hdr_crc= cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4));

 

            ri.ino =cpu_to_je32(f->inocache->ino);

            ri.version= cpu_to_je32(++f->highest_version);

            ri.mode =cpu_to_jemode(JFFS2_F_I_MODE(f));

            ri.uid =cpu_to_je16(JFFS2_F_I_UID(f));

            ri.gid =cpu_to_je16(JFFS2_F_I_GID(f));

            ri.isize =cpu_to_je32(ilen);

            ri.atime =cpu_to_je32(JFFS2_F_I_ATIME(f));

            ri.ctime =cpu_to_je32(JFFS2_F_I_CTIME(f));

            ri.mtime =cpu_to_je32(JFFS2_F_I_MTIME(f));

            ri.offset =cpu_to_je32(0);

            ri.csize =cpu_to_je32(mdatalen);

            ri.dsize =cpu_to_je32(mdatalen);

            ri.compr =JFFS2_COMPR_NONE;

            ri.node_crc= cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));

            ri.data_crc= cpu_to_je32(crc32(0, mdata, mdatalen));

 

            new_fn =jffs2_write_dnode(c, f, &ri, mdata, mdatalen, ALLOC_GC);

 

            if(IS_ERR(new_fn)) {

                        printk(KERN_WARNING"Error writing new dnode: %ld\n", PTR_ERR(new_fn));

                        ret= PTR_ERR(new_fn);

                        gotoout;

            }

            jffs2_mark_node_obsolete(c,fn->raw);

            jffs2_free_full_dnode(fn);

            f->metadata= new_fn;

 out:

            if(S_ISLNK(JFFS2_F_I_MODE(f)))

                        kfree(mdata);

            return ret;

}

 

这个函数的目的就是garbage collect那些只有meta datafiles,这些文件通常有:character device fileblock device filesymbol link fileJFFS2定义meta data的最大长度为254。我们这里不去讨论file system的结构、所以我们也不去解释为什么是254基本上也就够了。

首先这里我们看到定义:        char *mdata = NULL, mdatalen = 0;

mdatalenmeat data的长度,这里定义为char

讲到这里我们也许就已经知道答案了。char只有8 bits,如果char缺省是signed char的话,它的范围为[-128, 127)。

这样:mdatalen = fn->size;ret =jffs2_reserve_space_gc(c, sizeof(ri) + mdatalen, &alloclen, JFFS2_SUMMARY_INODE_SIZE);就会有问题:当fn->size >=128&& fn->size < 255时,mdatalen其实就是一个负数。然而jffs2_reserve_space_gc函数的函数原型为:intjffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t*len, uint32_t sumsize)

第二个参数是unsigned int minsize,这样相当于传入了一个非常大的数。

 

这个问题现在看来似乎很简单,而且奇怪的是JFFS2的设计者为什么会犯这样的错误?

其实这里面还有一些东西值得探讨,通常我们要定义无符号的整数需要加unsigned关键字,如:unsigned int/long/short等,如果没有unsigned的,就是有符号整数,也就是说int/long/short缺省为有符号。然后对于char就不是这么简单了。其实这种缺省取决于我们用的编译器和平台,ARMGCCchar做了相反的缺省定义,当我们定义char ch;时ch是一个unsigned char。为什么会这样定义我没有去细想,如果有人知道请留言告诉我。

这样来说,之前的代码应该没有问题才对呀?但是这里面还是至少有两个问题:

1.     会有跨平台移植的问题,这个比较简单,不多说。

2.     即便是ARM平台下,我们也是可以通过-fsigned-char编译参数设定char的缺省模式为signed char。笔者现在正在使用LTIB就定义了-fsigned-char

 

这里我们也可以看出许多公司定义coding rule的意义。在实践中我们通常都定义:

typedef unsigned char uint8_t;

typedef signed char int8_t;

之后我们就不要直接使用char了。

 

这里还有一个例子,来自LinuxKernel 3.7.6drivers\tty\serial\max3100.c: 

static void max3100_work(struct work_struct *w)

{

            structmax3100_port *s = container_of(w, struct max3100_port, work);

            intrxchars;

            u16 tx, rx;

            int conf,cconf, rts, crts;

            struct circ_buf *xmit = &s->port.state->xmit;

 

            dev_dbg(&s->spi->dev,"%s\n", __func__);

 

            rxchars =0;

            do {

                        spin_lock(&s->conf_lock);

                        conf= s->conf;

                        cconf= s->conf_commit;

                        s->conf_commit= 0;

                        rts= s->rts;

                        crts= s->rts_commit;

                        s->rts_commit= 0;

                        spin_unlock(&s->conf_lock);

                        if(cconf)

                                    max3100_sr(s,MAX3100_WC | conf, &rx);

                        if(crts) {

                                    max3100_sr(s,MAX3100_WD | MAX3100_TE |

                                                   (s->rts ? MAX3100_RTS : 0), &rx);

                                    rxchars+= max3100_handlerx(s, rx);

                        }

 

                        max3100_sr(s,MAX3100_RD, &rx);

                        rxchars+= max3100_handlerx(s, rx);

 

                        if(rx & MAX3100_T) {

                                    tx= 0xffff;

                                    if(s->port.x_char) {

                                                tx= s->port.x_char;

                                                s->port.icount.tx++;

                                                s->port.x_char= 0;

                                    }else if (!uart_circ_empty(xmit) &&

                                                   !uart_tx_stopped(&s->port)) {

                                                tx= xmit->buf[xmit->tail];

                                                xmit->tail= (xmit->tail + 1) &

                                                            (UART_XMIT_SIZE- 1);

                                                s->port.icount.tx++;

                                    }

                                    if(tx != 0xffff) {

                                                max3100_calc_parity(s,&tx);

                                                tx|= MAX3100_WD | (s->rts ? MAX3100_RTS : 0);

                                                max3100_sr(s,tx, &rx);

                                                rxchars+= max3100_handlerx(s, rx);

                                    }

                        }

 

                        if(rxchars > 16 && s->port.state->port.tty != NULL) {

                                    tty_flip_buffer_push(s->port.state->port.tty);

                                    rxchars= 0;

                        }

                        if(uart_circ_chars_pending(xmit) < WAKEUP_CHARS)

                                    uart_write_wakeup(&s->port);

 

            } while(!s->force_end_work &&

                         !freezing(current) &&

                         ((rx & MAX3100_R) ||

                          (!uart_circ_empty(xmit) &&

                           !uart_tx_stopped(&s->port))));

 

            if (rxchars> 0 && s->port.state->port.tty != NULL)

                        tty_flip_buffer_push(s->port.state->port.tty);

}

  

请看红色部分,简单说明一下tx = xmit->buf[xmit->tail];  

这里tx定义为u16,而xmit->buf的定义是char

struct circ_buf {

            char *buf;

            int head;

            int tail;

}; 

其结果为:在ARM平台下,如果我们定义了-fsigned-char,那么这个驱动就再也无法发送0xFF了,因为当xmit->buf[[xmit->tail]为0xff时,tx就被赋值为0xffff, 这样max3100_sr(s, tx, &rx);就会fail而无法调用max3100_sr(s, tx, &rx);去发送0xff了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值