当前位置: 首页 > news >正文

现代网站开发建设正规的电商平台有哪些

现代网站开发建设,正规的电商平台有哪些,wordpress主题更换头部媒体,中国最好的猎头公司排行榜文章目录 1. 前言2. 背景3. 为什么不应该在内核代码中使用 volatile #xff1f;4. 参考资料 1. 前言 限于作者能力水平#xff0c;本文可能存在谬误#xff0c;因此而给读者带来的损失#xff0c;作者不做任何承诺。 2. 背景 本文基于 Linux 内核文档 Why the “volati… 文章目录 1. 前言2. 背景3. 为什么不应该在内核代码中使用 volatile 4. 参考资料 1. 前言 限于作者能力水平本文可能存在谬误因此而给读者带来的损失作者不做任何承诺。 2. 背景 本文基于 Linux 内核文档 Why the “volatile” type class should not be used 进行翻译加上了笔者的理解后整理而成。本文并非对原文一对一的翻译这一点提请读者注意。 3. 为什么不应该在内核代码中使用 volatile C 程序员通常认为 volatile 意味着变量可以在当前执行线程之外进行更改因此当使用共享数据结构时他们有时会试图在内核代码中使用它。换句话说C 程序员通把 volatile 类型变量视为一种原子变量但事实并非如此。在内核代码中使用 volatile 几乎从来都不是正确的本文将介绍原因。 关于 volatile需要理解的关键点是它的目的是抑制优化而这几乎从来都不是人们真正想要做的事情。在内核中必须保护共享数据结构免受不必要的并发访问防止不必要的并发过程还将以更有效的方式避免几乎所有与优化相关的问题。 与 volatile 一样使并发访问数据安全的内核原语自旋锁、互斥锁、内存屏障等旨在防止不必要的优化。如果使用得当也无需使用 volatile 。如果仍然需要 volatile那么几乎可以肯定代码中的某个地方存在错误。在正确编写的内核代码中volatile 只能减慢速度。 考虑如下内核代码片段 spin_lock(the_lock); do_something_on(shared_data); do_something_else_with(shared_data); spin_unlock(the_lock); 如果所有对共享数据 shared_data 访问的代码都进行上锁操作则在持有 the_lock 锁时shared_data 的值不会出现意外更改。在 the_lock 锁持有期间任何其他想要使用 shared_data 数据的代码都要等待锁 the_lock 的释放。自旋锁原语充当内存屏障的角色 - 它们被显式的编写成这样 - 这意味着数据访问不会在自旋锁覆盖的代码段之间进行优化。因此编译器可能认为“记住”了变量 shared_data 中的内容(如将数据内容缓存到寄存器中)但 spin_lock() 调用它会起到内存屏障的作用所以将迫使编译器“忘记”它所知道的任何内容因此访问 shared_data 数据时不会出现优化问题。我们来看一下 spinlock 的实现(这里只看特定于 ARMv7 架构的“叫号(tickets)”实现版本spinlock 经历很多代的变化实现各有不同但不管怎么实现都需保持 Linux 设定的相同语义)理解它为什么可以充当内存屏障的角色 /* include/linux/spinlock.h */ #define raw_spin_lock(lock) _raw_spin_lock(lock)static __always_inline void spin_lock(spinlock_t *lock) {raw_spin_lock(lock-rlock); }/* kernel/spinlock.c */ #ifndef CONFIG_INLINE_SPIN_LOCK void __lockfunc _raw_spin_lock(raw_spinlock_t *lock) {__raw_spin_lock(lock); } EXPORT_SYMBOL(_raw_spin_lock); #endif/* include/linux/spinlock_api_smp.h */ static inline void __raw_spin_lock(raw_spinlock_t *lock) {preempt_disable(); /* 禁用当前 CPU 上的抢占 */spin_acquire(lock-dep_map, 0, 0, _RET_IP_); // 不用关注LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock); // do_raw_spin_lock() }/* include/linux/spinlock.h */ static inline void do_raw_spin_lock(raw_spinlock_t *lock) __acquires(lock) {__acquire(lock); // 不用关注arch_spin_lock(lock-raw_lock); }/* arch/arm/include/asm/spinlock.h */ static inline void arch_spin_lock(arch_spinlock_t *lock) {unsigned long tmp;u32 newval;arch_spinlock_t lockval;prefetchw(lock-slock);/* lock-next 1 */__asm__ __volatile__( 1: ldrex %0, [%3]\n /* lockval { .slock lock-slock } */add %1, %0, %4\n /* newval lockval.slock (1 TICKET_SHIFT) */strex %2, %1, [%3]\n /* lock-slock newval lock-slock 1 */teq %2, #0\n /* if (tmp ! 0) // tmp ! 0 表示strex写没有成功需继续尝试 */bne 1b /* goto 1b; */: r (lockval), r (newval), r (tmp): r (lock-slock), I (1 TICKET_SHIFT) /* I: 立即数 */: cc);/** 条件* lockval.tickets.next ! lockval.tickets.owner* 表示锁已被占所以此次上锁请求需等待锁占有者释放* 直到轮到自己的请求号牌ID next。** 从这里我们可以理解到为什么要使用临时变量 lockval* 来复制锁 lock 的内容主要是保存的请求号牌ID next * 因为每个上锁请求都会更新 lock 的 next 所以每个请* 求者得使用临时变量记录自己的号牌ID (next)。*/while (lockval.tickets.next ! lockval.tickets.owner) {wfe();/* 读取当前的叫号ID: * arch_spin_unlock() 会更新它相当于叫号机器 */lockval.tickets.owner ACCESS_ONCE(lock-tickets.owner);}/** 插入一个内存屏障使得之前对 spinlock 的读写操作立马对系统中其它 CPU 可见* 宣告 spinlock 已被持有同时保证临界区的存储操作不会跨越到锁前锁前的存储* 操作也不能跨过锁进入临界区内也即防止了锁定前后存储操作的乱序。*/smp_mb(); } 从上面看到spin_lock() 调用的最后插入了内存屏障 smp_mb()宣告了 spinlock 已被持有同时保证临界区的存储操作不会跨越到锁前锁前的存储操作也不能跨过锁进入临界区内也即防止了锁定前后存储操作的乱序。对应的spin_unlock() 调用的最后也会有内存屏障操作宣告了 spinlock 已经释放其它用户可以来竞争 spinlock 了同时保证让临界区内的存储操作对系统中其它 CPU 核可见 spin_unlock()...arch_spin_unlock()/* arch/arm/include/asm/spinlock.h */ static inline void arch_spin_unlock(arch_spinlock_t *lock) {/** 保证在 spinlock 锁释放前让 spinlock 锁定的* 临界区内的读写操作对系统中其它 CPU 可见。 */smp_mb();lock-tickets.owner; /* 叫号下一位: 轮到下一个排队的了 *//** 所有因发起了占锁请求而等待的CPU都会因此需要* 刷新 lock-tickets.owner 所在的 cache line * 而事实上只有轮到叫号的请求者才能获取到锁* 所以其他没轮到的叫号的CPU完全是没必要刷新自己* 的 cache 这会造成不必要的开销。* 对此的改进就是 spin lock 的下一代: MCS Lock 。*/dsb_sev(); } 如果 shared_data 被声明为 volatile锁仍然是必须的。如果所有对 shared_data 的访问都遵循持锁操作的规则那么当处于被锁保护的临界代码段中对数据 shared_data 进行访问时不可能出现有其它地方并发访问 shared_data 的情形这个时候编译器可以优化对 shared_data 的访问但是因为shared_data 被声明为 volatile这会阻止编译器优化对临界代码段中 shared_data 的访问这会造成不必要性能损失。在处理共享数据时适当的锁定会使 volatile 变得不必要并且可能有害。 volatile 最初是为内存映射的 I/O 寄存器设计的。在内核中寄存器访问也应该受到锁的保护但也不希望编译器在临界代码段优化寄存器访问。但是在内核中I/O 内存访问始终通过特定访问函数接口完成直接通过指针访问 I/O 内存是不恰当的并且不适用于所有架构。编写这些 I/O 内存访问接口函数是为了防止不必要的优化因此再一次不需要 volatile。常见的 I/O 内存访问接口函数 有(以 ARM 架构为例) /* IO barriers */ #ifdef CONFIG_ARM_DMA_MEM_BUFFERABLE #include asm/barrier.h #define __iormb() rmb() #define __iowmb() wmb() #else #define __iormb() do { } while (0) #define __iowmb() do { } while (0) #endif/** IO port access primitives* -------------------------** The ARM doesnt have special IO access instructions; all IO is memory* mapped. Note that these are defined to perform little endian accesses* only. Their primary purpose is to access PCI and ISA peripherals.** Note that for a big endian machine, this implies that the following* big endian mode connectivity is in place, as described by numerous* ARM documents:** PCI: D0-D7 D8-D15 D16-D23 D24-D31* ARM: D24-D31 D16-D23 D8-D15 D0-D7** The machine specific io.h include defines __io to translate an IO* address to a memory address.** Note that we prevent GCC re-ordering or caching values in expressions* by introducing sequence points into the in*() definitions. Note that* __raw_* do not guarantee this behaviour.** The {in,out}[bwl] macros are for emulating x86-style PCI/ISA IO space.*/ #ifdef __io #define outb(v,p) ({ __iowmb(); __raw_writeb(v,__io(p)); }) #define outw(v,p) ({ __iowmb(); __raw_writew((__force __u16) \cpu_to_le16(v),__io(p)); }) #define outl(v,p) ({ __iowmb(); __raw_writel((__force __u32) \cpu_to_le32(v),__io(p)); })#define inb(p) ({ __u8 __v __raw_readb(__io(p)); __iormb(); __v; }) #define inw(p) ({ __u16 __v le16_to_cpu((__force __le16) \__raw_readw(__io(p))); __iormb(); __v; }) #define inl(p) ({ __u32 __v le32_to_cpu((__force __le32) \__raw_readl(__io(p))); __iormb(); __v; })#define outsb(p,d,l) __raw_writesb(__io(p),d,l) #define outsw(p,d,l) __raw_writesw(__io(p),d,l) #define outsl(p,d,l) __raw_writesl(__io(p),d,l)#define insb(p,d,l) __raw_readsb(__io(p),d,l) #define insw(p,d,l) __raw_readsw(__io(p),d,l) #define insl(p,d,l) __raw_readsl(__io(p),d,l) #endif/** Memory access primitives* ------------------------** These perform PCI memory accesses via an ioremap region. They dont* take an address as such, but a cookie.** Again, these are defined to perform little endian accesses. See the* IO port primitives for more information.*/ #ifndef readl #define readb_relaxed(c) ({ u8 __r __raw_readb(c); __r; }) #define readw_relaxed(c) ({ u16 __r le16_to_cpu((__force __le16) \__raw_readw(c)); __r; }) #define readl_relaxed(c) ({ u32 __r le32_to_cpu((__force __le32) \__raw_readl(c)); __r; })#define writeb_relaxed(v,c) __raw_writeb(v,c) #define writew_relaxed(v,c) __raw_writew((__force u16) cpu_to_le16(v),c) #define writel_relaxed(v,c) __raw_writel((__force u32) cpu_to_le32(v),c)#define readb(c) ({ u8 __v readb_relaxed(c); __iormb(); __v; }) #define readw(c) ({ u16 __v readw_relaxed(c); __iormb(); __v; }) #define readl(c) ({ u32 __v readl_relaxed(c); __iormb(); __v; })#define writeb(v,c) ({ __iowmb(); writeb_relaxed(v,c); }) #define writew(v,c) ({ __iowmb(); writew_relaxed(v,c); }) #define writel(v,c) ({ __iowmb(); writel_relaxed(v,c); })#define readsb(p,d,l) __raw_readsb(p,d,l) #define readsw(p,d,l) __raw_readsw(p,d,l) #define readsl(p,d,l) __raw_readsl(p,d,l)#define writesb(p,d,l) __raw_writesb(p,d,l) #define writesw(p,d,l) __raw_writesw(p,d,l) #define writesl(p,d,l) __raw_writesl(p,d,l)#else...#endif /* readl */ 看到了吧这些 I/O 内存访问接口在必要的时候添加了 __iormb() (实现为 rmb()) 和 __iowmb() (实现为 wmb()) 内存屏障 。这些 I/O 内存访问接口是硬件架构强相关的每个架构的实现都有不同编程时应使用内核提供的统一接口保证代码在任何架构都能正确工作以及可移植性。 另一种可能倾向于使用 volatile 的情况是当处理器忙等变量的值时执行繁忙等待的正确方法是 while (my_variable ! what_i_want)cpu_relax(); cpu_relax() 调用可以降低 CPU 功耗它也恰好充当编译器屏障因此再一次volatile 是不必要的。当然忙等通常是一种反人类的行为。 在极少数情况下volatile 在内核中是有意义的 . 上述 I/O 接口函数可能在直接 I/O 内存访问确实有效的架构上使用 volatile及使用 *(volatile int *)io_addr 的形式进行读写。此时从本质上讲每个I/O 接口函数调用本身都(因为 volatile)变成了一个微小的临界区并确保访问按程序员的预期进行。 . 更改内存但没有其他可见副作用的内联汇编代码可能会被 GCC 删除。将 volatile关键字添加到 asm 语句将阻止此类删除。 . jiffies 变量的特殊之处在于它每次被引用时都可以有不同的值但它可以在没有任何特殊锁定的情况下读取。因此jiffies 可能是不稳定的但添加其他此类变量是强烈反对的。在这方面jiffies 被认为是一个“愚蠢的遗产”问题Linus 的原话。修复它会比它的价值更麻烦。 . 指向一致性内存中数据结构的指针可能会被 I/O 设备修改此时使用 volatile 修饰它们可能是需要的。如网络适配器使用的环形缓冲区该适配器更改指针以指示已处理的描述符是此类情况的一个示例。 对于大多数代码上述使用 volatile 的理由都不适用。因此使用 volatile 通常会被视为一个错误需要对代码进行额外的审查。想要使用 volatile 的开发人员应该退后一步想想他们真正想要实现的目标。 删除 volatile 变量的补丁通常是受欢迎的 - 只要它们带有一个理由表明并发问题已经过适当的考虑。 4. 参考资料 [1] https://lwn.net/Articles/233481/ [2] https://lwn.net/Articles/233482/
http://www.pierceye.com/news/913898/

相关文章:

  • 内蒙古工程建设协会网站seo文章外包
  • 网站建设免费的服务器商丘推广公司
  • 网站设计大概价格设计网络平台的公司
  • phpcms中的网站介绍页中文去掉wordpress
  • 网站的建设心得怎么做网站淘宝转换工具
  • 做网站1天转多钱wordpress 分享插件
  • 淘宝网站建设单子好接吗朝阳区办公
  • html5网站建设平台怎么给网站添加图标
  • 中文域名网站网站制作wap页面
  • wordpress站内全文检索一个人完成网站建设
  • wordpress自定义重用结构北京seo怎么优化
  • 苏州做网站专业的公司vs2008怎么做网站
  • 效果好的徐州网站建设休闲食品网站建设策划书
  • 如何发布自己的网站wordpress 4.9.6 下载
  • 织梦网站打开慢企业咨询师资格证
  • 蓬莱网站建设哪家好广州白云网站建设
  • 网站建设得花多少钱做购物网站能否生存
  • 多语言网站 用什么cms深圳Wordpress网站
  • 常州市建设工程质量监督站网站开发公司设计管理岗位述职
  • 国外网站顶部菜单设计谷歌seo文章
  • 梅州网站开发外国自适应企业网站
  • 大型网站建设动力无限哪个公司的装饰设计公司
  • 网站建设与管理自考wordpress替换默认的编辑器
  • 企业在网站推广咸阳做网站开发公司
  • 从美洲开始做皇帝免费阅读网站wordpress嵌入优酷视频播放
  • 长沙 外贸网站建设国际足联世界排名
  • 辽宁省建设执业继续教育协会网站深圳网站制作建设服务公司
  • 个人网站备案都需要什么运营seo是什么意思
  • 局域网内部网站建设app下载苏州网络推广企业
  • 重庆美邦 网站建设保定最新消息发布