Blog

把恶劣的外部环境转化成内心丰富自由的精神生活

Content #

在集中营里,人的身体和思想由于受到压迫而处于原始状态,但深化人们的精神生活是可能的。有丰富的精神生活且比较敏感的人在这里会承受更多痛苦(他们身体也会更弱),但对内心的伤害相应也会少许多。他们能把恶劣的外部环境转化成内心丰富自由的精神生活,只有这样才能解释集中营中身体羸弱的一些人比看似强壮的人生存能力更强。

From #

活出生命的意义

任务的内核态栈

Content #

每个任务(进程)在内核态运行时都有自己的内核态栈。任务的数据结构与其内核态的栈在同一内存页中。

代码路径:linux/kernel/sched.c

union task_union {
        struct task_struct task;
        char stack[PAGE_SIZE];
};

static union task_union init_task = {INIT_TASK,};

include/linux/sched.h头文件中的INIT_TASK宏会初始化进程0,其中tss中的 esp会被初始化成:

PAGE_SIZE+(long)&init_task

From #

Linux内核设计的艺术

初始化过程中的内核栈

Content #

代码路径:linux/kernel/sched.c

long user_stack [ PAGE_SIZE>>2 ];
struct {
    long * a;
    short b;
} stack_start = {&user_stack[PAGE_SIZE>>2], 0x10};

定义用户堆栈,共1K项,容量4K。注意,esp指在user_stack数组最后一项的后面,这是因为Intel CPU总是先递减栈指针,然后再入栈。

在内核初始化过程中用作内核栈:代码路径:boot/head.s

内核初始化完成后,它被用作任务0的用户态堆栈。参看 进程0由0特权级翻转到3特权级

初始化过程中的内核栈与任务的内核态栈并不是同一块内存。进程0在执行时用的是user_stack指向的栈,只有在切换到进程1以后,才开始使用每个任务自备的内核栈。

From #

Linux内核设计的艺术

创新扩散模型

Content #

埃弗雷特·罗杰斯(E.M.Rogers)提出创新扩散模型,如下图所示。

在这个模型里,会把针对一个新的想法的接受程度分成了 5 类人。

  1. 创新者(Innovators)他们是勇敢的先行者,自觉推动创新。创新者在创新交流过程中,发挥着非常重要的作用。

  2. 早期采用者(EarlyAdopters)他们是受人尊敬的社会人士,是公众意见领袖,他们乐意引领时尚、尝试新鲜事物,但行为谨慎。

  3. 早期大众(EarlyMajority)他们是有思想的一群人,也比较谨慎,但他们较之普通人群会更愿意、更早地接受变革。

  4. 后期大众(LateMajority)他们是持怀疑态度的一群人,只有当社会大众普遍接受了新鲜事物的时候,他们才会采用。

  5. 迟缓者(Laggards)他们是保守传统的一群人,习惯于因循守旧,对新鲜事物吹毛求疵,只有当新的发展成为主流、成为传统时,他们才会被动接受。

Viewpoints #

From #

24 | 实践你的理论:数据驱动最终就是用结果说话

意义疗法的目的

Content #

意义疗法的目的是帮助患者找到他生命的意义。在这个意义上讲,意义疗法也是个分析过程,因此它类似于心理分析法。不过,虽然意义疗法试图唤醒患者潜意识中的某种东西,但其方法不仅仅限于将患者的活动限制在个人潜意识中的本能事实,而且还关注诸如存在的潜在意义以及对意义的追求。然而,尽管任何心理分析师都会避免将意识层面包含在其治疗过程中,他们仍会试图使患者意识到他内心深处渴望的东西。意义疗法把人看成这样一种存在:他主要的担忧是实现某种意义,而不仅仅是满足欲望和本能的需求,或者是调和本我、自我与超我之间欲望的冲突抑或适应社会和环境,在这一点上,它与心理分析分道扬镳。

From #

活出生命的意义

mem_map初始化

Content #

//代码路径:mm/memory.c:


#define LOW_MEM 0x100000                       //1 MB
#define PAGING_MEMORY (15*1024*1024)           //只管理1MB以上的空间,只有15MB
#define PAGING_PAGES (PAGING_MEMORY>>12)       //15 MB的页数
#define MAP_NR(addr) (((addr)-LOW_MEM)>>12)
#define USED 100

static long HIGH_MEMORY= 0;

static unsigned char mem_map [PAGING_PAGES]= {0,};  //注意每个元素一个字节

void mem_init(long start_mem, long end_mem)  //start_mem传进来的值为buffer_memory_end
{
    int i;
    HIGH_MEMORY= end_mem;
    for (i=0;i<PAGING_PAGES;i++)
        mem_map[i]= USED;
    i= MAP_NR(start_mem);                        //start_mem为6 MB(虚拟盘之后)
    end_mem -= start_mem;
    end_mem >>= 12;                              //16 MB的页数
    while (end_mem-->0)
        mem_map[i++]=0;
}

系统通过mem_map[]对1 MB以上的内存分页进行管理,记录一个页面的使用次数。 mem_init()函数先将所有的内存页面使用计数均设置成USED(100,即被使用),然后再将主内存中的所有页面使用计数全部清零,系统以后只把使用计数为0的页面视为空闲页面。

From #

Linux内核设计的艺术

...

stos(Store String)

Content #

将AL、AX或EAX的值存入es:di指定的位置,不能使用segment override prefix。然后根据D标记递增或递减di的值。

stosb es:[di]=al; di=di \(\pm\) 1 stosw es:[di]=al; di=di \(\pm\) 2 stosd es:[di]=al; di=di \(\pm\) 4

stos之前可使用repeat prefix(rep),rep前缀每次会将cx减1,重复执行stos指令,直到cx值为0。lods指令之前使用rep是没有任何意义的。

下面的代码用stosw指令清除Buffer指定的区域:

void ClearBuffer (int count, short* buffer)
{
    _asm{
        push edi  ;save registers
        push es
        push ds
        mov ax,0
        mov ecx, count
        mov edi, buffer
        pop es    ;load ES with DS
        rep stosw ;clear Buffer
        pop es    ;restore registers
        pop edi
    }
}

From #

The Intel Microprocessors

...

lods(Load String)

Content #

将ds:si内存位置的数据加载到AL、AX或EAX,再根据D标记位递增或递减si的值。

lodsb al=ds:[si]; si=si \(\pm\) 1 lodsw al=ds:[si]; si=si \(\pm\) 2 lodsd al=ds:[si]; si=si \(\pm\) 4

From #

cmps(compare strings instruction)

Content #

compsb - 按字节比较两块内存区别 compsw - 按字(Word)比较两块内存区别 compsd - 按双字(Double Word)比较两块内存区别内存区域分别由ds:si与es:di指定。

下面的代码逐字节比较两块内存区域,找到不同或cx为0时停止。

mov si, offset LINE ;address LINE
mov di, offset TABLE ;address TABLE
cld ;auto-increment
mov cx, 10 ;load counter
repe cmpsb

cx为0或相等标记位置位,表示两个字符串相等。 cx不为0或者相等标记位没有置位,表示两个字符串不相等。

From #

The Intel Microprocessors

scas(string scan instruction)

Content #

scasb - 将内存中某字节与AL的内容做比较。 scasw - 将内存中某字节与AX的内容做比较。 scasd - 将内存中某字节与EAX的内容做比较。

寄存器内容与内存中的内容都不会发生变化,只影响标记位。内存的位置由DI指定,并且使用默认段寄存器ES,不能用SEGMENT OVERRIDE PREFIX. scas指令通过D标记来确定DI的方向。

​结束后状态: CX:剩余未扫描的次数。 EDI:指向匹配位置的下一个地址。 ZF:指示是否找到匹配项。

假定某内在区域共有100字节,开始地址为BLOCK,要测试该区域是否有值为00H 的字节:

mov di, offset BLOCK ;DI作为目的变址寄存器,指向待扫描的内存起始位置
cld  ;auto-increment 清除方向标志(DF = 0)
mov cx, 100
xor al, al
repne scasb

跳过ASCII码中的空格,其中DI已经指向字符串起始位置:

cld ;auto-increment
mov cx, 256 ;load counter
mov al, 20h ;get space
repe scasb

From #

The Intel Microprocessors