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

购物网站建设目标客户分析论文华为荣耀官网手机商城

购物网站建设目标客户分析论文,华为荣耀官网手机商城,搜搜网站收录,做网站鼠标移动 链接变颜色目录 什么是信号 Linux下的信号 信号的记录 信号处理的常见方式 产生信号 使用组合键产生信号#xff08;包含core dump#xff09; 使用系统调用向进程发送信号 由软件条件产生信号 由硬件异常产生信号 阻塞信号 内核表示 sigset_t 信号集操作函数 sigpendin…目录 什么是信号 Linux下的信号 信号的记录 信号处理的常见方式 产生信号 使用组合键产生信号包含core dump 使用系统调用向进程发送信号 由软件条件产生信号 由硬件异常产生信号  阻塞信号 内核表示 sigset_t 信号集操作函数 sigpending sigprocmask 示例 捕捉信号 内核状态与用户状态 内核信号捕捉 sigaction 可重入函数 volatile关键字 SIGCHLD 什么是信号 在我们生活中就有很多的信号比如道路上经常会看到的红绿灯红灯停绿灯行那我们为什么会知道呢那是因为我们记住了什么是信号记住了看到这个信号后应该做什么。 Linux下的信号 Linux下的信号本质上是一种通知机制用户或者操作系统通过发送信号通知进程某件事已经发生了之后就可以处理它了。         进程要处理信号必须具有识别信号的能力有时我们虽然接受到了这个信号但是我可能不会立马处理这个信号无法处理那也要先被临时记住然后在合适的时候处理。         对于信号的产生它和进程运行是异步的。         以前我们运行代码的时候程序是个死循环我们想要关闭它就是用过Ctrl c它是一个热键实际上它就是向进程发送2号信号当然要先识别组合键这种键盘的工作方式是通过中断方式进行的。         使用kill -l命令就可以查看Linux中的信号列表信号编号是1~64在这之中没有32、331~31我们叫做普通信号34~64我们叫做实时信号。 输入man 7 signal就可以查看信号的信息。 信号的记录 既然是操作系统发送的信号那么要不要管理起来呢那是肯定的我们要判断一个信号是否产生进程必须具有保存信号的相关数据结构很明显就是使用位图结构并保存在进程的PCB中这部分属于内核数据结构所以只有操作系统有权利向目标进程写入。         例如Ctrl c操作系统首先要识别组合键再找到进程列表的前台进程然后写入对应的信号到进程内部的位图结构中。 信号处理的常见方式 执行信号的默认处理动作进程自带的。忽略该信号。自定义动作捕捉信号。 产生信号 使用组合键产生信号包含core dump 通过方法捕捉信号修改进程对特定信号的处理动作并不会直接调用对应的处理动作。如果没有产生这个signum函数指针指向的方法就不会被调用。 参数 signum表示要捕捉的信号。参数类型为函数指针传入一个函数指针这个函数要处理这个捕捉到的信号。 #include iostream #include unistd.h #include signal.husing namespace std;void catchSignal(int signum) {cout 捕捉到了一个信号正在处理 signum , pid: getpid() endl; }int main() {signal(SIGINT, catchSignal);while (true){cout 进程运行中... pid: getpid() endl;sleep(1);}return 0; } 信号的默认处理动作更改了并没有终止进程而是执行了特定的处理动作。         键盘上还有一个组合键就是Ctrl \它是向进程发送3号信号也会终止掉进程。         可以看到两个信号对应的动作不一样Term就是terminate表示终止Core在终止进程的时候会进行一个动作核心转储。         在进程控制的章节提到了status那里有一张图。         进程等待返回的输出型参数status被信号杀掉会在0~6这7为重存储终止信号这一位7的位置就存放的是core dump标志。         在云服务器中核心转储是默认被关掉的我们可以通过使用ulimit -a命令查看当前资源限制的设定。         这里表示core文件的大小为0表示核心转储是被关闭的使用ulimit -c 大小来设置core文件的大小这个操作也仅限于本次会话。         设置完了核心转储该怎么用呢就比如信号中有一个8号信号SIGFPE浮点数异常它的操作也是Core。         随便写一个除0错误这时在报错信息后就有了core dumped并且目录下也会多一个文件core.pid。         当运行中代码出错了我们要知道出错原因是什么。这时我们就会用到核心转储核心转储指的是操作系统在进程收到某些信号而终止运行时将该进程地址空间的内容以及有关进程状态的其他信息转而存储到一个磁盘文件当中这个磁盘文件也叫做核心转储文件。而核心转储的目的就是为了在调试。 我们重新编译一下在g选项中加入-g使用gdb调试输入core-file core.pid就可以得到这些信息。         所以core_dump标志表示的就是是否发生核心转储。 int main() {pid_t id fork();if (id 0){sleep(10);int a 10;a / 0;exit(1);}int status 0;waitpid(id, status, 0);cout 父进程: getpid() , 子进程: id , exit sig: (status 0x7F) , is core: ((status 7) 1) endl;return 0; } 这里core dump标志就被设置成1代表需要核心转储并在磁盘中建立了文件。 当我们在这个除0错误之前向他发送其他信号那么core dump就不会被设置 当我们把这个功能关掉也就不会发生核心转储了。         因为在我们平常使用的环境中是默认关闭core dump的原因就是如果出错发生了核心转储那么就要在磁盘上创建一个文件如果一直出错久而久之就会有大量的core dump文件此时操作系统都有可能会挂掉为了节约成本所以要关闭它。 使用系统调用向进程发送信号 作用让操作系统向指定进程发送指定信号。 参数进程pid和信号。 返回值成功返回0失败返回-1。 static void Usage(string proc) {cout Usage:\r\n\t proc signumber pid endl; }// ./mykill 2 pid int main(int argc, char* argv[]) {if (argc ! 3){Usage(argv[0]);}int signumber atoi(argv[1]);int procid atoi(argv[2]);kill(procid, signumber);return 0; } 这就我们自己写的一个程序可以向指定进程发送信号。   作用让操作系统向自己发送指定信号。 参数信号。 返回值成功返回0非0表示失败。   作用让操作系统给自己发送6号SIGABRT信号就是自己终止自己。 由软件条件产生信号 SIGPIPE         上一篇进程间通信中就提到了如果把读端关闭写端一直写管道已经没有人读了这个写入就没有意义操作系统会自动终止对应的写端进程就是向写端进程发送13号SIGPIPE信号。这是一种由软件发现运行条件不满足的时候发送的一种信号管道也是软件。   SIGALRM 参数设置几秒后让操作系统向我发送SIGALRM信号 返回值 若调用alarm函数前进程已经设置了闹钟则返回上一个闹钟时间的剩余时间并且本次闹钟的设置会覆盖上一次闹钟的设置。如果调用alarm函数前进程没有设置闹钟则返回值为0。 #include iostream #include unistd.h #include signal.h #include string #include functional #include vector#include sys/wait.h #include sys/types.husing namespace std;typedef functionvoid() func; vectorfunc callbacks;uint64_t count 0;void showCount() {cout count: count endl; }void catchSig(int signum) {cout 捕捉到了一个信号正在处理 signum , pid: getpid() endl;for (auto f : callbacks){f();}alarm(1); }int main() {// 要捕捉的信号signal(SIGALRM, catchSig);// 设置闹钟alarm(1);// 添加函数callbacks.push_back(showCount);while(true) count;return 0; } 一个简单的累加器通过这个代码我们也可以做到某一段时间后我们可以执行相应的操作。 由硬件异常产生信号  除0错误 void catchSig(int signum) {sleep(1);cout 捕捉到了一个信号正在处理 signum , pid: getpid() endl; }int main() { signal(SIGFPE, catchSig);int a 10;a / 0;while (true) sleep(1);return 0; }         通过上面的代码运行结果就会发现问题代码中有除0错误也捕捉到了信号但是为什么会一直循环的打印呢         在我们的电脑中是谁一直在帮我们计算那就是CPUCPU就是个硬件我们也知道在CPU中有很多寄存器其中就有一个状态寄存器在这之中就有一个溢出标记位操作系统在计算完毕之后就会检测如果溢出标记位为1就代表发生了溢出问题他就会找到当前正在运行的进程向它发送信号这个进程就会在合适的时候进行处理。         所以这种除0文件其实是一个硬件问题一旦出现硬件异常默认是终止进程或者使用try/catch实际也是做不了什么。         那么为什么会出现死循环因为一旦触发了异常是要终止的但是如果捕捉了这个信号就要自定义处理方式处理了但没有终止进程那寄存器中的异常信息没有处理虽然有异常进程切换的时候也会保存进程的上下文数据当恢复的时候操作系统就会发现这个异常然后不断的发送8号信号。解决这个问题那就捕捉到信号的时候直接终止进程就好了。   野指针或越界问题         无论是哪一个错误他们都会通过地址找到目标位置然而我们语言层面的地址都是虚拟地址虚拟地址和物理地址映射的时候是通过页表MMUMemory Manager Unit 内存管理单元这是一个硬件。         MMU是一种处理CPU的内存访问请求的硬件所以映射工作是由它做的 当需要进行虚拟地址到物理地址的映射时先将虚拟地址给MMU它会计算出对应的物理地址然后我们再访问这个物理地址。         而MMU既然是硬件那么它当然也要记录相应的状态信息当出现野指针或者越界访问非法地址时MMU进行虚拟地址到物理地址的转换时就会报错然后将错误信息写入这时硬件上面的信息也会立马被操作系统识别到然后找到当前进程向该进程发送SIGSEGV信号。 阻塞信号 关于信号的其他相关概念 实际执行信号的处理动作称为信号递达Delivery。信号从产生到递达之间的状态称为信号未决Pending。进程可以选择阻塞 Block某个信号。被阻塞的信号产生时将保持在未决状态直到进程解除对此信号的阻塞才执行递达的动作。阻塞和忽略是不同的只要信号被阻塞就不会递达而忽略是在递达之后的一种处理动作。 内核表示 这个pending表就是上述的位图结构0代表没有收到该比特位的信号1表示收到了handler表就是函数指针数组一旦pending表中收到了这个信号那么就拿着信号的编号去handler表中找对应的函数处理方法signal函数需要的参数一个是信号编号另一个就是函数signal就是通过这个信号的编号找到handler表中对应的位置再填入函数地址这就是信号的自定义捕捉。 系统也提供了默认处理的宏SIG_DEF和SIG_IGN。         还有一个block位图和pending位图的结构一模一样这个位图中表示的是信号是否被阻塞。         所以一个信号的处理流程应该是这样的 操作系统向目标进程发送信号其实就是修改pending位图。进程合适的时候处理信号找到pending位图中收到的信号。再看block位图中该信号是否被阻塞。最后再调用处理方法。 sigset_t 一般而言语言会给我们提供头文件或者定义的结构体操作系统也会提供这些因为操作系统不希望我们直接访问这些数据结构所以会给我们提供接口来访问他们。         sigset_t就是操作系统提供的位图结构但是不允许用户直接进行位操作它会给我们提供对应的方法来完成对应的功能。         sigset_t称为信号集pending位图就叫做信号集block信号集叫做信号屏蔽字这个类型可以表示每个信号的“有效”或“无效”状态。 在信号屏蔽字中“有效”和“无效”的含义是该信号是否被阻塞。在信号集中“有效”和“无效”的含义是该信号是否处于未决状态。 信号集操作函数 #include signal.hint sigemptyset(sigset_t *set); // 初始化set信号集所有比特位清零int sigfillset(sigset_t *set); // 初始化set信号集所有比特位置1int sigaddset(sigset_t *set, int signum); // 在set信号集中添加某种有效信号int sigdelset(sigset_t *set, int signum); // 在set信号集中删除某种有效信号// 上面的函数成功返回0失败返回-1int sigismember(const sigset_t *set, int signum); // 判断set信号集中是否包含某种信号若包含则返回1不包含则返回0调用失败返回-1 sigpending int sigpending(sigset_t *set); 作用获取当前进程的pending信号集 返回值成功返回0失败返回-1。 sigprocmask int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); 作用可以读取或更改进程内核中的信号屏蔽字默认进程不会对任何信号block 参数 how SIG_BLOCK添加set到当前信号屏蔽字的信号相当于mask mask | setSIG_UNBLOCK从当前信号屏蔽字中解除阻塞set中的信号相当于mask mask | ~setSIG_SETMASK设置当前信号屏蔽字为set相当于masksetset信号屏蔽字oldset输出型参数拿到更改前的set信号屏蔽字 示例 这样就可以捕捉所有的信号了是不是也就没有任何信号可以杀掉这个进程呢 #include iostream #include signal.h #include unistd.husing namespace std;void catchSig(int signum) {cout 获得信号 signum endl; }int main() { for (int i 1; i 31; i) signal(i, catchSig);while (true) sleep(1);return 0; }         我们发现9号信号没有被捕捉直接杀掉了进程所以9号信号是不能被捕捉的操作系统为了解决所有信号都被捕捉没有信号能让进程退出的问题。 static void showPending(sigset_t pending) {for (int sig 1; sig 31; sig){if (sigismember(pending, sig)) cout 1;else cout 0;}cout endl; }int main() {// 1.定义信号集sigset_t bset, obset; // 信号屏蔽字,阻塞信号集sigset_t pending; // 信号集// 2.初始化信号集sigemptyset(bset);sigemptyset(obset);sigemptyset(pending);// 3.添加进行屏蔽的信号sigaddset(bset, 2); // SIGINT// 4.设置到内核中int n sigprocmask(SIG_BLOCK, bset, obset);assert(n ! -1);(void)n;cout 阻塞2号信号成功, pid: getpid() endl; // 5.重复打印pending信号集while (true){// 5.1 获取进程的pending信号集sigpending(pending);// 5.2 显示pending信号集中没有被递达的信号showPending(pending);sleep(1);}return 0; } 这样就屏蔽了2号信号如果我们想要过一段时间解除阻塞2号就可以修改一下。 // ...// 5.重复打印pending信号集int count 0;while (true){// 5.1 获取进程的pending信号集sigpending(pending);// 5.2 显示pending信号集中没有被递达的信号showPending(pending);sleep(1);count;if (count 10)sigprocmask(SIG_SETMASK, obset, nullptr); // 使用修改之前的信号集} // ... 当我们解除阻塞之后2号信号确实被递达了直接执行了默认处理动作——终止进程。  如果不想让进程终止也可以使用signal函数捕捉信号。 捕捉信号 内核状态与用户状态 在讲解信号如何被捕捉之前我们再来处理一下上面的问题前面我们说信号产生后无法立即处理可以在合适的时候处理那什么是合适的时候呢         信号的相关数据结构都放在PCB中也就是在内核中的所以普通用户无法直接访问或者说必须要处于内核状态才能访问。         所以处理的时机就是从内核态返回用户态的时候检测和处理信号那我们怎么进入内核态呢         原来我们使用系统调用的时候就处于内核态执行我们自己的代码的时候就叫做用户态。内核态是一种执行系统代码的状态优先级很高用户态是一个受管控的状态。         内核态和用户态是如何切换的呢         内核态到用户态的转换 进行系统调用的时候进程时间片到了要进程切换发生异常、中断interrupt 80、陷阱等         用户态到内核态的转换 系统调用返回下次调度该进程继续执行时异常处理完成         原来说过的进程地址空间中0~3G是用户空间3~4G是内核空间。进程有多个但是操作系统只有一个原来的页表就是用户级页表每个进程要进行虚拟地址和物理地址的映射还要有一张内核级页表可以简单理解为这张内核级页表是所有进程共享的这张页表也要进行虚拟地址到物理地址的映射。         当我们在代码中使用系统调用的时候这个系统调用的接口也在地址空间中直接跳转到地址空间中再通过页表映射找到物理内存中系统调用。         当进程的时间片到了需要进程切换操作系统底层就会发生时钟中断它会找到正在执行的进程通过地址空间找到进程切换的函数再把上下文数据放到PCB中下一个进程开始运行的时候操作系统会通过内核空间恢复上下文数据。         那么我们为什么会有执行内核代码的权利呢这也要用到CPU中某个寄存器这个寄存器的某一个位置就表示当前的执行权限是内核态还是用户态。所以中断的时候就要从用户态转为内核态。 内核信号捕捉 当我们执行代码时可能因为一些原因变成内核态例如系统调用或者进程切换既然进入了内核操作系统就要知道为什么进入内核。处理完问题后准备回到用户态前可以顺手处理一下信号检测当前的pending位图看看有没有收到信号如果有再检查block位图看看是否阻塞了这个信号如果没有阻塞就去查看handler表。         handler表中信号处理有忽略忽略就把pending位图中该信号由1置0还有默认默认情况下大多是终止不再调度这个进程释放地址空间和PCB不用返回用户态。如果发生核心转储在内核中也可以把相关数据通过IO放到磁盘中。         还有一种状态就是暂停就是把R状态改为T状态这也不用恢复到用户态直接执行调度算法放到等待队列中在内核态中这些默认和忽略动作都很好做。         下面我们就要谈一下捕捉了当我们从pending中检测到某一个信号此时的状态还是内核态此时的内核状态也可以执行用户中的代码但是操作系统不想这样做如果有一些非法操作就会造成严重的问题所以操作系统不会执行用户写的代码或者说操作系统不能用内核态执行用户态代码此时就要从内核态转变为用户态再来执行handler方法。         执行完handler方法后也不能直接回到最开始进入内核的位置因为要回到内核态将pending位图的该信号置为0也不知道是从哪里进入内核的所以必须要通过特殊的系统调用sigreturn进入内核这样才能拿到进入内核的用户代码的位置。 箭头所向就是状态转换方向几次状态转换就看流程图跨越了“边界线”。 sigaction 除了signal还有一个函数sigaction也可以对信号捕捉。 int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); 返回值成功返回0失败返回-1 参数 signum表示捕捉哪个信号act如果act不是nullptr根据act修改信号的处理动作输入型参数oldact如果oldact不是nullptr返回原来的信号处理方法输出型参数  其实这个sigaction也是一个结构体只是它的名字和函数是一样的。 struct sigaction {void(*sa_handler)(int);void(*sa_sigaction)(int, siginfo_t *, void *);sigset_t sa_mask;int sa_flags;void(*sa_restorer)(void); }; 成员变量 sa_handler设为SIG_DFL为默认设为SIG_IGN为忽略设为函数指针就执行对应的自定义方法要填入handler表中。sa_mask信号屏蔽字使用前要初始化。sa_flags选项现在设为0就行。其他没有说的参数没有用到。 #include iostream #include signal.h #include unistd.h #include cassertusing namespace std;void handler(int signum) {cout 获取了一个信号 signum endl; }int main() {// signal(2, SIG_IGN); 也可以这样修改信号的默认处理为忽略// 内核数据类型用户栈定义struct sigaction act, oact;act.sa_flags 0;sigemptyset(act.sa_mask); // 初始化信号屏蔽字act.sa_handler handler; // 捕捉方法// 设置进当前进程的PCB中sigaction(2, act, oact);// 默认处理动作cout default action: (int)(oact.sa_handler) endl;while (true) sleep(1);return 0; } 那我们再来说一下sa_mask这个参数         当某个信号的处理函数被调用内核会自动将当前信号加入进程的信号屏蔽字当信号处理函数返回时自动恢复为原来的信号屏蔽字 这就保证了在处理某个信号时如果这种信号再次产生那么它会被阻塞到当前处理结束为止。         如果调用信号处理函数时除了当前信号被屏蔽外还想屏蔽其他信号这就可以在sa_mask中设置当信号处理处理函数返回时自动恢复原来的信号屏蔽字。         就比如要捕捉的信号没收到之前系统中没有设置信号屏蔽字捕捉的信号到了把该信号添加到信号屏蔽字的同时也要添加sa_mask中设置的信号屏蔽字函数返回的时候就会自动恢复原来的信号屏蔽字原来的没有设置信号屏蔽字在sa_mask中设置的也就不会再阻塞了。         下面来演示一下多设置了几个信号屏蔽字。 // sigaddset(act.sa_mask, 3); // sigaddset(act.sa_mask, 4); // sigaddset(act.sa_mask, 5); // sigaddset(act.sa_mask, 6); // sigaddset(act.sa_mask, 7);void showPending(sigset_t* pending) {for (int i 1;i 31; i){if (sigismember(pending, i)) cout 1;else cout 0;}cout endl; }void handler(int signum) {cout 获取了一个信号 signum endl;sigset_t pending;int i 0;while (i 10){sigpending(pending);showPending(pending);sleep(1);} }         这就可以看出如果一个信号正在被处理就会阻塞这个信号sa_mask中的也会被阻塞调用返回的时候就会处理这些信号这个illegal instruction就是4号信号被递达了。 可重入函数 什么是重入如果有两个执行流同时访问了某一个函数并且完成了某些任务。         如果我们在main函数中对一个结构做了修改此时因为一些原因从用户态转变为内核态假如在内核态中也对这个结构做了修改这就可能会导致这个结构出现问题比如出现内存泄漏。         我们原来使用的大多数函数都是不可重入的。 volatile关键字 这是C语言的一个关键字有了信号的知识就可以更好的理解它了。 #include iostream #include signal.husing namespace std;int flag 0;void catchSig(int signum) {cout 捕捉到一个信号 signum endl;flag 1; }int main() {signal(2, catchSig);while (!flag);cout 进程退出 endl;return 0; } 这个代码也很好理解只要有2号信号发来我就修改flag的值这样进程就可以退出了。         但是如果编译器的优化级别高的话它就会自动检测在main函数中的flag没有任何修改每次都得让CPU从内存中读取这个值判断后再放回内存这样太慢了所以编译器直接就把flag0这个值放到了CPU的寄存器中。         我们看到的现象就是这样编译器在编译的时候就已经决定好了并不是在运行的时候才发现flag没有修改CPU只是执行指令你怎么编我就怎么执行。         所以在运行的时候检测的都是CPU寄存器中的flag0那循环就不会退出即使我们看到flag已经被修改了。         当我们使用volatile关键字修饰flag的时候就没有问题了。         volatile就是说明flag可能会发生变化每次使用它的时候必须从内存中读取所以说volatile是保持了内存的可见性。 SIGCHLD 子进程在终止时会给父进程发SIGCHLD信号该信号的默认处理动作是忽略父进程可以自定义SIGCHLD信号的处理函数这样父进程只需专心处理自己的工作不必关心子进程了子进程终止时会通知父进程父进程在信号处理函数中调用wait清理子进程即可。 void handler(int signum) {cout 捕捉到了信号 signum endl; }int main() {signal(SIGCHLD, handler);pid_t id fork();if (id 0){int n 5;while (n--){cout I am child, pid: getpid() endl;sleep(1);}exit(1);}return 0; }         上面的代码我们没有等待子进程退出一定会让子进程进入僵尸状态父进程收到了SIGCHLD信号但是又不想管那么就可以直接设置为SIG_IGN。 int main() {signal(SIGCHLD, SIG_IGN);if (fork() 0){cout child pid: getpid() endl;sleep(5);exit(1);}while (true){cout parent pid: getpid() endl;sleep(1);}return 0; }         虽然操作系统设置的SIGCHLD的默认处理动作就是忽略但是并不是真的忽略只是让子进程进入僵尸状态一旦我们signal中设置了忽略那就是告诉操作系统我不想管直接帮我回收子进程就行。         这个信号不止子进程退出的时候会发送子进程暂停也会发送这个信号所以在进程等待中我们就说过waitpid要设置成WNOHANG非阻塞等待因为子进程可能是退出也可能是暂停。         所以SIGCHLD这种做法是Linux操作系统采用的其他的操作系统可能就不是这样的了。
http://www.pierceye.com/news/923468/

相关文章:

  • 东营远见网站建设公司聊城网站建设服务好
  • 品牌网站建设j小蝌蚪j网站管理建设的总结
  • 怎么做直播网站刷弹幕外链发布软件
  • 网站建站合同淘宝运营跟做网站哪种工资高
  • 网站建设导向百度秒收录
  • 海南省建设执业资格管理中心网站跨境电商资讯网
  • 天河公司网站建设公司编程是什么课程内容
  • 南宁门户网站有哪些不利于优化网站的因素
  • 鄱阳做网站来个黑黑的网站
  • wordpress 4 漏洞深圳专门做seo的公司
  • wordpress网站防伪查询模板东坑网站建设公司
  • 做网站的应用高端网站建站公司
  • 遵义网站开发制作公司服装外贸是做什么的
  • 国外网站 服务器网络营销是什么专业的
  • 微官网与网站的区别网站建设及网络推广
  • 百度推广官方网站登录入口一个人制作网站
  • 重庆市建设公共资源交易中心网站首页当地人做导游的旅游网站
  • 北京网站建设收费龙溪网站制作
  • 佛山小企业网站建设郑州做网站销售怎么样
  • 招考网站开发如何创建一个自己的网页
  • 做网站一般链接什么数据库wordpress 504错误
  • 网站阵地建设江门网站建设工作
  • 汽车网站建设策划方案24小时永久有效在线观看
  • 潍坊做网页的公司潍坊网站排名优化
  • 中建铁路建设有限公司网站微信群营销工具
  • 单位网站建设意见建议百度关键词检测工具
  • 重庆网站建设方案书免费网上咨询医生是哪个网
  • 临沂市开发区可以做分销的网站网络域名综合查询
  • 建设银行企业网银网站过期银州手机网站建设
  • 导航网站 win8风格企业服务官网