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

英文网站模板改成中文医疗器械招商网站大全

英文网站模板改成中文,医疗器械招商网站大全,我想建个网站怎么建,网站首页布局的设计一 初识linux线程 1 线程由来 我们之前说创建一个进程#xff0c;要创建进程控制块pcb#xff0c;进程地址空间#xff0c;页表#xff0c;而且我之前的博客中都有意无意的说明这个pcb是描述进程的#xff0c;是os用来管理进程的#xff0c;而有了线程后#xff0c;就要…一 初识linux线程 1 线程由来 我们之前说创建一个进程要创建进程控制块pcb进程地址空间页表而且我之前的博客中都有意无意的说明这个pcb是描述进程的是os用来管理进程的而有了线程后就要纠正pcb是描述进程这个说法。 不过要说清楚线程由来这得先介绍介绍进程地址空间进程地址空间实际上是进程的资源窗口因为进程地址空间能让进程看到自己所有的资源例如我们可以看到堆栈范围代码段的大小就像是一个表格记录你家里房产多少地多少亩所以说进程地址空间是进程的资源窗口不过通过这个表格我们并不知道资源在哪里但是我们知道我们有这些资源而要访问这些资源得靠页表。 父进程创建子进程时子进程也会创建自己的地址空间结构体(mmstruct)页表等结构可以认为父进程会直接把自己的资源拷贝一份给子进程为了效率这种拷贝是一种写实拷贝但我们为了方便理解就认为此时子进程已经有一份自己的资源了可是linux中有一种进程我们父进程创建时只需要创建一个pcb对象这种特殊的进程就是线程此时线程的资源显然是直接用父进程的资源那怎么用父进程的资源呢通过父进程资源窗口和页表不就能看到父进程的资源了吗。 当我们知道了进程内是有多个线程pcb的时候此时就要更正一个观点了这个pcb就不是描述进程的而是描述一个线程又称为执行流的之前都是一个进程内就一个线程这个线程等价于进程那表示线程的pcb说是表示进程的也没错。后面我们学线程创建的时候就知道线程是被我们调用某个函数创建出来的显然最开始的时候进程一定会自带一个pcb后面创建一次进程多一个pcb但是第一个pcb代表主线程后面的均认为是次线程。          既然是多个线程共享进程资源那大家如何执行不同的代码呢后面我们就知道是创建线程的函数让线程指向不同的代码块其实也就是让pcb指向不同的代码。 2 线程特性 前面的线程概念说了linux创建线程就只创建一个pcb需要的资源和原先的进程分享接下来就详细谈谈线程的特性这个特性是一般认为操作系统中的线程应该是具有什么样的特点但是并非是规定具体的os应该如何实现。 1 线程在进程内部执行什么意思呢 在linux看来线程在进程内部执行其实是在进程的地址空间内运行。因为线程就是一个执行流是执行流就必须要有代码和数据吧它的代码从哪来呢我们之前说了线程是和主线程共享这个地址空间如下图。 显然线程共享主线程的所有资源所以线程用的资源就是原来主线程的资源线程运行不就是个访问资源的过程吗那不就是只能访问地址空间内的资源这不就是只能在进程的地址空间内运行吗。 2 线程的执行粒度比进程细 为什么说线程的执行粒度比进程细实际上是主线程将自己要执行的代码分给了其它线程那这些线程的代码一般量比较小所以说执行粒度更细。创建线程的时候我们应该能更好理解如何分代码给其它线程。        二 重新定义进程线程 1 线程 线程是调度的基本单位以前说进程是 task_struct代码和数据现在来看好像单一的PCB代码和数据不算一个完整的进程因为一个进程内会有多个线程多个线程也会有多个PCB这也是属于进程的资源如下图所以完整的进程包括内部的执行流分支进程地址空间页表和代码和数据所占用的内存。由此得创建进程是什么也就是准备线程的pcb准备执行流资源创建进程地址空间创建页表申请内存。 还有个观念就是我以前一直认为cpu是调度进程的并且认为调度进程就是调度进程的pcb前面已经说了多线程下进程不仅仅只有一个pcb所以此时要更正了由线程是cpu的调度单位得cpu调度的主要是进程的执行流(又叫线程)的pcb对象不是仅仅调度进程对于cpu来说: cpu执行的时候不区分进程线程都是执行流但是切换调度的时候肯定是要看情况是切换线程还是进程的不然怎么知道什么时候只要切换pcb什么时候页表等数据结构都要被切换。 噢此时我感觉对cpu调度的理解更深刻了一点在大O(1)调度算法中是所有的pcb在排队这些都是线程都是执行流此时有一个主线程被cpu执行了过了一会创建次线程我想cpu会把这个新创建的次线程抢先放到运行队列安排下一个切换成这个次线程执行此时是线程切换。我这里提及到线程抢先因为你想想如果主线程a执行完下一个就是次线程b此时就是线程切换就比较省事如果是其它进程的线程c那就要进程切换要切成次线程b又是进程切换会比较浪费时间所以cpu为了效率一定会尽可能地把主次线程放在一起。 有个补充概念是线程的时间片是来自进程的因为时间片也是资源在主线程内有一个总的时间片当线程的时间片到了要切换线程还要对主线程时间片--当主线程时间片归零说明整个进程的时间片到了此时就是切换整个进程的了。 不过上述概念还有点不好理解要再理一理线程时间片到了如何找到主线程对它的时间片做--? 我们目前就认为是线程库提供了将所有执行流联系起来的方法才能让cpu不断地找到这个主线程的时间片做更新和记录这样os才知道什么时候是让cpu切换线程什么时候是切换进程。 2 进程 进程是os分配资源的基本单位因为线程使用的都是进程分配的资源所以说进程是os分配资源的基本单位。创建新的线程现在来看就是创建一个pcb到了后面我们会补充还会在共享区创建一个属于线程的堆栈空间但是这个资源还是在进程的地址空间内再次验证线程用的还是进程的资源。 而以前的进程就是个单执行流那这个执行流需要线程库管理吗肯定不用啊是先有进程才有的线程概念才有了线程库来维护多余的执行流。 3 linux实现线程方案 显然一个线程要访问什么资源执行到哪了等等这些都是描述线程的属性进程的内核数据结构中的pcb不就保存着进程的状态等属性吗难道可以复用没错linux就是直接复用了原先进程的内核数据结构体pcb用来描述线程的结构体所以os只要继续管理调度pcb即可多了个线程概念对os来说也就是多了点调度和管理的pcb没什么改变。这就是linux的方案因为设计linux的人认为描述进程和线程几乎是一样的没必要多写一份代码。 也就是说而随着线程的出现linux借用进程的pcb来描述线程使得进程的含义更加丰富原先的一个pcb就从表示进程转而变成线程了。难道只提供个pcb就是线程了吗当然不是linux设计者开发了一个线程库对pcb做了封装才是我们使用的线程。 4 线程库由来 综上当os中有进程线程概念时从cpu视角来看它调度的执行流有如下关系: 线程执行流进程因为执行流可能是一个进程也可能是一个线程所以执行流大于等于线程小于等于进程所以线程又被称为轻量级进程至于为什么更轻量化第四点线程周边概念会提及。 所以Linux中没有明确的线程概念。只有轻量级进程的概念所以不会提供线程的系统调用只会提供轻量级线程的系统调用也就是只提供创建pcb的接口。 为什么这么奇怪只提供创建pcb接口还要封装成库才为外部提供线程功能一般不都是直接在系统搞一个线程吗让os内有一个线程说得倒容易实际上有很多工作要做的。 如果linux中有线程的概念此时我们就要描述线程然后再用一些数据结构组织管理线程哪怕这个结构可以复用但是我们很多时候可能就是单线程根本就没有多线程然后os用来组织线程的数据结构是已经创建好了就等着维护你创建的线程了结果没有虽然多线程有用但不想设计到linux中让linux代码变得冗余。那线程又有用显然最后得提供吧所以linux只提供创建轻量级进程的接口然后封装成动态库这样用户要用线程就包含对应的的库这样的好处就是可以几乎不增加os的管理代码而且动态库代码是共享的这样就不会使得一个进程一份代码非常冗余。 可是我们后面时候的时候却发现gcc还要带-l选项指定库名pthread库不是linux自己做的库吗怎么会变成第三方库呢?这其实是由于pthread库在发展中逐渐脱离了linux内核维护者的维护而变成由第三方去维护这个库所以在后面的linux发布版本中用的都是第三方设计的pthread库。 c对这个线程库又做了封装使用时同样要带-lthread这就证明了底层是封装了pthread库。 c语言写线程创建的代码在linux和在windows都可以跑应该是安装的语言库在底层实现了两份代码分别调用了不同的原生线程库又调用了不同的系统调用。但是提供给用户接口是一样的保证在不同的平台下使用是一致的。不过最重要的是g编译时要以c11标准编译。 void threadRun() {int num 0;while(1){std::cout我是次线程,id: getpid() g_val:g_val g_val g_valstd::endl;if(num 10)break;sleep(1);} } int main() {thread t1(threadRun);t1.join();return 0; } 5 windows实现线程方案 Windows实现线程方案则是用一个特定的tcb结构描述线程然后实现管理tcb的数据结构本来一堆pcb的调度已经挺麻烦的了现在每个pcb还跟着一堆线程的tcb调度的时候就不是简简单单的管理pcb了现在还要对pcb对应的一堆线程tcb做增删改查显然要对原先系统的代码做比较大的改动原先代码经过多次测试和修改已经没有太大问题了现在大改势必会出现更多的bug显然linux这种不大改原先代码也提供线程的方案是更卓越的。 三 再谈地址空间 1 虚拟地址如何转为物理地址 首先虚拟地址是32位的再来看看页表的结构首先肯定不是一整块的然后左边放虚拟地址右边放物理地址还要权限位假设一行十个字节而虚拟地址有2^32个那此时页表需要2^32个映射行一行十个字节那总共就需要40个G这说明首先肯定不是一个一个地址去映射。 那之前是把内存按4kb划分的那就只需要2^20行只要来一个虚拟地址取高位到低位的20位用来映射对应行找到内存块的起始地址后12个比特位用来映射具体地址即可最后占用空间也就40mb但是还是有一点点不足因为进程申请内存是这里分散的不可能遍布2^20个内存块也就不需要那么多行毕竟有些已经是内核的了所以没必要一下子定义一个这么大的数组实际结构如下图页表由两级页表构成。 首先虚拟地址有32个比特位前10个比特位是转为一级页表的下标而一级页表内存的是二级页表的地址中间10个比特位转为二级页表的下标二级页表保存页框的起始地址后12个比特位用于给页框起始地址转为具体物理地址的偏移量。保证了精确对应某个物理地址。 求页表总大小一个二级页表也就占4kb对应4mb的物理内存而最多会有1024个二级页表那就需要4mb页目录预计只占1kb。而且一个进程不可能一下子用完全部内存所以对于一个进程来说二级页表是不全的而且进程只管理3G所以二级页表不可能达到1024个则页表总大小可能连1mb都没有。这种映射方式首先减少了映射的行数就是因为将页表拆分了如果用一个有2^20个元素的数组来映射内存会造成浪费因为有一大部分内存可能都不属于进程但我们还是留了映射行。 那能拿到物理首地址如何拿到所有数据如何知道这个数据有多少个字节呢所以访问变量要指定类型就是为了连续读数据显然这个类型是给cpu先找到物理地址然后cpu根据类型天然知道要读取几个字节也就会控制硬件读取多少字节。 页目录其内可以先不填数据也就是说二级目录可以没有cpu内有个寄存器保存引起异常的虚拟地址因为有些时候是数据暂时还未加载到内存此时引起缺页中断然后要申请内存创建二级目录再访问此时上次的虚拟地址是肯定短时间内要用的当然要保存到cpu内。 当我们了解了页表就可以理解给线程分配资源本质就是给虚拟地址范围然后通过页表找到资源例如划分代码如何让线程执行不同的代码块肯定是pcb内的某个成员记录了各自代码的地址后面创建线程的时候我们就知道是直接把不同的函数给线程不同函数地址天然不同线程也就可以指向不同的代码了函数执行完也就结束了终止地址都不用保存了。如果有些资源没有划分例如全局数据区那就所有线程共享。当然每个线程执行的代码访问的数据可能重复这可能出现问题这不就是需要线程同步和互斥了吗。 四 线程与进程周边概念 1 为什么线程比进程更轻量化 (1) 因为创建释放更轻量化只需要创建释放一个pcb就可以了。 (2) 切换更轻量化为什么说切换更轻量化呢?因为切换的上下文少了页表地址空间都不需要从cpu放下来恢复时就恢复得更少不过就几个寄存器为什么说线程切换效率更高呢?因为在cpu中有一个cache区域是用于缓存进程高频使用的数据而要把进程高频使用的数据缓存起来称为加热这个是比较耗费时间的而切换线程不需要重新缓存数据到cache了因为你线程高频数据不就是进程高频数据的一部分吗所以线程切换不需要加热所以比进程切换快。         2 线程优点 12.18 健壮性降低是指: 线程之间并非是独立的进程间才是独立的当一个线程出问题整个进程也就是进程内的所有线程都会挂掉。缺乏访问控制也是健壮性降低的原因。 3 线程的独立数据 线程id和线程的上下文以及线程的栈栈只有一个如何独立后提。        据前面所说线程是调度单元那每个线程就一定要能被区分所以那每个线程就要有自己的id。这个id存哪呢感觉存在pcb内。 五 线程创建 1 pthread_create 参数1 输出型参数用来返回线程的id注:这个id并非是线程标识符 参数2 用来设置线程的属性一般不手动设置传NULL默认即可。 参数3 指明线程要执行的函数这个是线程的入口函数可以控制线程执行不同的代码此时就让该线程变成了新的执行分支。 探究传入函数的参数和返回值都是void*类型的就是为了能接收各种类型(linux下指针大小为8字节)之所以返回值是void*也是没办法因为不知道你函数会return 什么类型的数据又不能写个void就只能写个void*让外部获取后自行处理。 至于参数4则是给线程执行入口函数传的参数。 2 pthread_create使用 由主线程创建新线程创建新线程后将来新线程会执行传入的函数主线程继续往后执行。创建成功返回0失败返回出错原因不设置errno。 void* threadRotiune(void*) {while(true){std::cout我是次线程,id: getpid()std::endl;sleep(1);} } int main() {pthread_t id;pthread_create(id,NULL,threadRotiune,NULL);while(true){std::cout我是主线程,id: getpid()std::endl;sleep(1);}return 0; } 这两个线程的执行是并发的。编译时给次线程传了threadRotiune函数而主线程继续往后执行这两个执行流的pcb就指向了不同的代码块主次线程执行完各自的函数就结束了这就实现了执行代码的分离。 但是我们g编译时却会报出链接时报错因为pthread库是第三方库g编译要加-l库名选项。但是由于已经安装到系统路径下了所以不用指明库和头文件的所在路径。 单执行流下这两个死循环肯定是不可以同时跑的所以必然是多个执行流分开执行两个死循环代码而且有os在帮忙切换两个执行流。 但是ps ajx查却发现只有一个进程要用ps aL查启动的轻量级进程又或者说是执行流。如下图能查到两个执行流他们的pid都一样显然多线程下执行流间不用pid来区分tty是终端名CMD可能是进程名LWP是什么?主线程的pid咋和LWP一样不过主次线程的LWP却不一样显然LWP就是执行流间的标识符。 os调度凭借lwp来分辨根本不看pid没有了解线程的时候为什么都是用pid来区分呢因为之前没有其它线程的时候此时只有一个主线程主线程的pid等于lwp,所以说用pid来标识也没有问题。kill -9 LWP可以给指定线程发信号反正os能通过LWP找到线程有意思的是任一个线程收到信号所有线程都会退出显然这里任意一个线程都能找到所有的执行流我先前也提过进程内的执行流肯定是有联系的。      主次线程都调用某个show函数。 此时显示结果也很有意思之所以new thread和main thread一同显示我认为是次线程打印到new thread就被切换了然后就到了主线程打印main thread换行刷新缓冲区才会一同显示此时show函数就被重入了。 线程间共用地址空间能看到同一份资源一个修改另一个也能看见天然可以通信。 六 线程控制 1 线程id 在线程创建时第一个参数就是返回线程id这个线程id是什么呢打印如下图怎么和lwp和pid都不一样前面说过为动态库的线程库在共享区会管理新创建的线程会保存一些有关线程的属性结构体而这个线程id就是线程属性在共享区的地址。 主新线程谁先被调度不知道但是主不退新线程退出应该也会出现类似僵尸进程的情况但查不到这种情况当然常理来看我们肯定是要等待回收的因为我们需要知道这个线程有没有做好我交代的事情。 2 线程等待函数 会阻塞等待指定线程的出错码是直接返回的而不是设置在errno中免得多线程对一个变量进行覆盖。 参数1 线程的id又叫tid        参数2 首先我们知道次线程执行的函数是会返回void*的而参数2就是外部用来获取这个函数的返回值是个输出型参数因为返回值是void*返回值存在线程内可能是pcb也可能在共享区phread_join要获取这个只能传个变量的地址也就是二级指针。join的时候一般不考虑异常因为真出异常的话主线程也没了更无法考虑异常了。 3 非阻塞join等待 而让线程分离就是告诉系统我不关心这个线程的返回值线程退出后直接回收就好了。而且不管是可以线程对自己进行分离操作也可以对其它线程进行分离操作 分离了后就不可以join了join会返回22号错误码。注意:分离线程只是修改线程的属性表示线程是否可以被join此时被分离的线程还是共享着进程资源而不是直接把线程分出去了。 七 线程终止 1 终止线程 不能用exit这是终止进程的如果在线程内用会直接终止进程而不是仅仅终止当前线程。不过为什么pthread_exit的参数是void*呢显然和函数返回值的意义是一样的。不过次线程的return 只会终止当前线程而main函数退出了则整个进程都会退出了。 2 取消运行中的线程 参数还是线程的tid前提是该进程已经启动。若线程以这种方式结束此时join接受的退出码为-1。 在进程那里我们学过进程替换线程也有程序替换在任意的线程内做程序替换所有线程都被销毁按照新程序重新分配线程资源。 3 再谈线程执行函数 前面只是大致说了线程的执行函数的参数是void*那我们就可以发散一下思维例如传个类对象也可以返回一个类对象指针。 class Request { public:Request(int start,int end,const string threadname):_start(start),_end(end),_threadname(threadname){;}int _start; 求和起始数int _end; 求和终止数string _threadname; 线程名 }; class Response 返回类型 { public:Response(int result,int exitcode):_result(result),_exitcode(exitcode){;}int _result;记录计算结果int _exitcode;记录退出码 };void* sumcount(void* argv) {Request* req static_castRequest*(argv);Response* res new Response(0,0);//在堆上new一个对象for(int start req-_start;start req-_end;start){res-_resultstart;}return (void*)res; 返回计算结果 }int main() {pthread_t id;Request* req new Request(1,100,thread-1 );int num 0; while(true){std::cout我是主线程,id: getpid()std::endl;if(num 5)break;sleep(1);}cout开始等待次线程endl;void* ret;pthread_join(id,ret);Response* res static_castResponse*(ret);cout回收成功: 计算结果为:res-_result 退出码为: res-_exitcodeendl;delete req;delete res;return 0; } static_cast用于非多态类型间的转换。一般转换的两种类型最好是有联系的例如int和double都是整型家族的。 显然堆空间也是共享的因为在主线程new的对象次线程可以使用而且次线程new的对象主线程也可以delete处理。后面我们在讲线程id的时候会再提及线程的栈不是共享的。 4 线程id真面目 先来尝试获取一下pthread_self可以获取调用该函数的线程的tid。 linux创建轻量级进程的系统调用clone。 fn为函数指针这就验证了前面所说在pcb是会记录自己的执行流的执行函数所以各个线程pcb记录了不同的函数也就能执行不同代码child_stack是要传入一个自定义栈保证每个pcb都指向自己的栈还有其它参数不关心。 因为每个线程都会有自己的调用函数链所以它们的栈帧结构是必须独立的否则压栈出栈不就乱套了那这个栈在哪呢?共享区那为什么要在共享区呢?因为线程库是要维护线程概念的也就是管理线程的属性而线程的栈也是线程的属性也要被管理所以都在线程库里也就都在共享区了。 还有就是我们先前说了linux中没有线程概念只有轻量级进程线程是库来维护的由于会有多个线程也就会有多个线程的属性要被管理。实际上动态库内部是维护了一个数组每个元素是包含线程的属性局部存储地址栈地址这些的集合这个集合就可以看做是库级别的线程tcb。而线程tid就是对应线程属性存放位置的地址 可是什么是线程的属性呢线程属性不是已经复用pcb管理完了吗简单理解就是线程有些属性还是和进程属性不同的所以需要库维护例如线程的栈。线程库就是一些代码也就是在共享区申请了一些空间用于维护栈不用维护线程执行流线程执行流就可以看做是底层的pcb结构体这个有os管理就可以了。 由此得虽然线程又被称为轻量级进程但是轻量级进程是内核层面的而线程是对这一内核做封装的所以这两个并不能完全划等号这一点要注意。 既然我们要用这个库里的代码因为线程库是动态库显然线程库就要加载到内存中。为什么不是静态库呢因为每个进程都要创建线程也就都要执行创建方法没必要一个进程创建一次就把代码拷贝到自己的程序中所以动态库只保存一份在内存其它的进程映射使用即可。噢总而言之是在线程库内对线程做的描述也是线程库内对线程的属性做管理但是内存中只有一个线程库由此说明其实所有进程创建的线程都在一个库内被管理。        八 线程补充 1 LWP和线程id LWP和线程id是分别给os区分执行流线程库管理线程的。但是线程库要对自己管理的线程属性做管理为了方便搜索线程属性设计者就把线程属性存放位置的虚拟地址返回给了用户。那为什么不用LWP呢反而整出个线程id假如用LWP这个时候有个人对linux不太了解但是想使用linux的线程此时他发现这个LWP是什么呢你说它是线程id吧进程也有LWP使用者要了解了轻量级进程才知道为什么进程线程都有LWP这会增添很多负担。 2 演示多线程创建 #include pthread.h #include iostream #include sys/types.h #include unistd.h #includevector using namespace std; #define NUM 3 struct threadData {string threadname; }; string toHex(pthread_t tid)将地址转为16进制并存入buf数组中,%x只格式化32位而tid转为地址却有64位所以这里会发生截断应该用%lx或者%llx来格式化 {char buf[128] {0};sprintf(buf,0x%llx,tid);return buf; } void *threadRun(void *arg) {threadData* td static_castthreadData*(arg);int num 0;while (true){cout getpid() td-threadname toHex(pthread_self())endl;sleep(1);if(num 10)break;}return nullptr; } int main() {vectorpthread_t vt;for (int i 0; i NUM; i){pthread_t tid;threadData* td new threadData;这个变量必须在堆上td-threadname thread- to_string(i);pthread_create(tid, nullptr, threadRun, td); 不然会给次线程传一个局部变量的地址会造成野指针vt.push_back(tid);}for(int i 0; i NUM;i){pthread_join(vt[i],nullptr);}return 0; } 显示结果如下: 3 栈独立验证 我们修改一下上面的一个函数。 void *threadRun(void *arg) {threadData* td static_castthreadData*(arg);int num 0;while (true){cout getpid() td-threadname 线程id: toHex(pthread_self()) num:num numendl;sleep(1);if(num 10)break;}return nullptr; } 在线程调用函数内定义一个变量每个线程都对其进行修改打印如下显然每个线程的栈帧是独立的。 前面说了线程栈主要是为了维护线程调用函数的栈帧而且每个执行流有一个栈看似是独立的但是不意味着进程间的执行流不可以互相访问栈上元素接下来就验证互相访问。 用一个全局指针获取线程局部变量的地址即可验证是否可以访问不过我觉得如果次线程都运行完了主线程才访问这个局部变量应该会野指针。 当一个全局变量被多个线程访问时这个全局变量就是共享资源访问时是会出问题的要在线程同步互斥再讨论那如何给线程定义一个私有的变量呢?        定义时加上_ _thread,这就是线程的局部存储。 __thread int g_val 0; 打印结果如下图。只能用于内置类型。 所以在共享库内一个线程属性就包括一个属性结构体一个栈和一个局部存储。简单理解动态库管理线程属性就是定义了一个比较大的数组所有进程的线程属性都在这里统一管理。 减少系统调用的使用这样每个线程都会保存自己的线程id如下代码。 __thread int number 0; __thread int pid 0; 最大的用处就是在线程的调用函数中都可以直接使用线程局部存储的变量这样如果在调用函数中我们还要获取线程id就可以直接用而不需要再次获取了。
http://www.zqtcl.cn/news/125014/

相关文章:

  • 用dw做的网页怎么连到网站上企业网站备案资料填写单
  • 中文 网站模板企业怎么建设网站
  • 建设户外腰包网站哪个网站注册域名好
  • 六安网站建设价格小学生编程网课前十名
  • 绵阳网站建设信赖辉煌wordpress多账号权限
  • 网站外链快速建设网站维护要学多久
  • 做网站都是用ps吗郑州网站设计培训
  • wordpress 多站点教程厦门做网站维护的公司
  • 婚纱网站建设需求分析wordpress js图片
  • seo网站怎么优化有哪些企业网站平台
  • 响应式中文网站欣赏wordpress 带分页的主题
  • 什么样的网站可以做站内站房地产的设计网站建设
  • 成都住房和城乡建设局 网站首页深圳西乡建网站
  • 商城类的网站一般怎么做开发app软件的步骤
  • 招聘网站做销售怎么样做网站后台学什么专业
  • 帮别人做彩票网站餐饮网站建设需求分析
  • 企业服务平台工程建设云深圳网站建设专业乐云seo
  • 怎么建立小公司网站抖音运营推广
  • 无锡地区做网站嵌入式软硬件开发
  • 网站建设框架怎么写企业网站本身应该就是企业( )的一部分
  • 如果做公司网站WordPress出现归档
  • 温州开发网站公司阿里云 拦截网站
  • 网站建设与管理实践实践报告南宁小程序建设
  • 网站后台功能技术要求网站建设 手机和pc
  • 嘉兴住房和城乡建设厅网站仿网站被封怎么办
  • 设计君seo查询怎么查
  • 购物网站ppt怎么做网站建设的申请理由
  • 美食网站要怎么做背景墙素材高清图片免费
  • 广东专业网站优化制作公司做编辑器的网站
  • 优惠券怎做网站自己注册网站