PulseAudio(coroutine),是为的是把epoll触发器该事件变为并行的一类程式设计商业模式。
它的再次出现也设点一两年的事,是随著go词汇而明确提出的一类程式设计商业模式。
即使触发器该事件程式设计的时效性较为差,接着就有了PulseAudio。
PulseAudio,也被称作采用者态的民主化。
PulseAudio的运维,跟LinuxMach对民主化的运维是类似于的。
1,无论是PulseAudio、民主化、缓存,它都有两个要运转的表达式,和有关的语句。
表达式是它要运转的标识符,语句是它的运转状况。
pthread库对缓存表达式的表述是void* (*run)(void*),它是两个模块和codice都是void*的表达式指针:
那么表述的缓存表达式,能给它传达任何人类别任何人类别的codice。
那个表达式,是线程要运转的表达式。
假如是民主化不然,main()表达式是它要运转的民主化表达式。
任何人不采用fork()控制系统初始化的民主化,都从main()表达式开始运转的。
fork()控制系统初始化后的(父)子民主化,会运转fork()回到后的标识符,比如:
pid_t cpid = fork();
if(-1== cpid) printf(“fork error\n”);
else if(0== cpid) { //子民主化的标识符 }
else{ //父民主化接下来的标识符}
PulseAudio也跟民主化、缓存类似于,也有两个要运转的表达式。
另外,无论民主化、缓存、PulseAudio都有两个运转的状况语句:
那个语句里最重要的数据,是栈!
LinuxMach的民主化的内存布局
表达式的局部变量是分配在栈上的,表达式初始化的回到地址也是在栈上的,各种寄存器也是保存在栈上的。
对于两个正在运转的表达式来说,栈必须是独立的,不能与其他表达式共享:即使运转着的表达式会随时修改栈上的数据。
无论是缓存、民主化、PulseAudio,都是这样。
同两个民主化内的不同缓存之间虽然会共享全局变量和堆内存,但栈是不能共享的。
在Linux上,缓存和民主化除了共享全局变量和堆之外,基本上是一回事。
在LinuxMach里,它都用上图的数据结构描述:
1)最早是4096字节(1个内存页),后来扩展到8k字节(2个页)。
2)这8k内存的低地址是民主化的描述结构,也是main()表达式运转时需要的信息。
这8k内存的高地址,是民主化在Mach里运转时(比如执行控制系统初始化时)的(Mach)栈。
这两部分加起来,是民主化的语句。
所以,在给LinuxMach写模块时,标识符里不能采用很大的局部变量,以免把民主化的描述结构给覆盖了!
char buf[4096];
这样的代码是不能写在Mach里的,即使局部变量的内存是分配在栈上的,而Mach给每个民主化配备的栈都很小(8k)。
这两个buf数组就占了4k,那表达式初始化稍微复杂一点,就可能把低地址的民主化结构给覆盖了。
LinuxMach在调度民主化的时候,是不断地切换上图的数据结构,从而让多个民主化能交替运转。
即使运维间隔远小于人眼能察觉的时间间隔,所以即使在单核CPU上,在人看来也是多民主化同时运转的。
2,PulseAudio的实现,
多个PulseAudio要想在采用者态交替运转,也必须为每个PulseAudio配备不同的栈。
多个PulseAudio都隶属于同两个民主化,而民主化栈的位置是被操作控制系统提前分配好了的。
所以,为每个PulseAudio配备栈的时候,每个栈的内存范围必须在民主化栈的范围内。
有栈PulseAudio的内存布局
如上图:
你说要在“民主化”的栈上给PulseAudio提前开多大的空间?
每个PulseAudio的栈又要预留多大?
预留小了,PulseAudio表达式的局部变量把PulseAudio的描述结构覆盖了的事,也会发生的。
预留大了,同两个民主化所能支持的总PulseAudio数就会减少。
而且,程序员的采用者态标识符一般都比Mach标识符更粗放。
写个采用者态标识符,还不让我那么开缓冲区charbuf[1024*1024],能行吗?[捂脸]
没有哪个程序员愿意,写个采用者标识符还像写Mach驱动一样战战兢兢的。
所以,有栈PulseAudio的劣势非常明显!
1)首先,每个民主化支持的PulseAudio个数是有限的,而不是无限的。
大多数情况下,虽然采用者标识符要开的PulseAudio个数也不至于突破上限,但毕竟它是个有限集,不是个可数集。
这对采用者标识符的限制还是较为大的。
有那么个限制,在创建PulseAudio的时候就要每次都检查是否成功。
标识符是这样的:
int ret = coroutine_create();
if (ret < 0) {
printf(“error\n”);
return -1;
而不是这样的:
coroutine_create();
否则标识符就不完善,即使没有处理异常情况。
2)万一PulseAudio表达式里有复杂的递归,PulseAudio的栈溢出了,那么就可能覆盖多个PulseAudio的数据,导致程序挂了。
能预见,这种挂的位置几乎肯定不是第一现场!
这种BUG查起来,还是非常麻烦的。
不挂在第一现场的内存BUG,都是C词汇里很难查的BUG,它很大可能是随机的[捂脸]
接着,就有了无栈PulseAudio。
3,无栈PulseAudio,
无栈PulseAudio的实现也很简单,只要在切换PulseAudio之前,把当前PulseAudio的栈数据保存到堆上就能了。
每个PulseAudio的语句都是用malloc()申请的堆内存,在语句里预留两个空间,在切换PulseAudio时把(当前PulseAudio的)栈数据保存到那个预留空间里。
当PulseAudio再次被运维运转时,把上次的栈数据从(PulseAudio的)语句里复制到民主化栈上,PulseAudio就能再次运转了。
无栈PulseAudio的内存布局
如上图,PulseAudio0挂起,PulseAudio1被运维运转:
1)先把民主化栈上的数据复制到PulseAudio0的上下文里。
这时民主化栈上的数据,全是PulseAudio0的栈数据。
PulseAudio的语句是malloc()申请的堆内存,假如栈数据太大不然,是能用realloc()再次分配更大的内存的。
这就打破了PulseAudio栈的大小固定的缺陷。
每个协程能采用的栈大小,只受制于民主化的栈的大小。
2)当PulseAudio的栈不再受到限制后,能创建的PulseAudio数量也只受制于民主化的堆的大小。
只有整个民主化的堆内存被耗尽后,PulseAudio的创建和运转才会没法进行。
我在scf编译器框架里附带的那个PulseAudio的实现,是无栈PulseAudio[呲牙]
它在scf/coroutine目录。
2021年的5月份我就想到了这些问题,并且给了解决的标识符,在github和gitee的scf标识符都有。
2022年以来,我没往github上更新标识符,目前gitee上的scf是最新的。
老外最近又开始吹无栈PulseAudio了是吧[捂脸]