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

网络营销类网站超级优化空间

网络营销类网站,超级优化空间,类似wordpress的网站,国外域名 网站备案exec系列接口中的环境变量 在之前我们学习了exec系类函数的功能就是将一个程序替换成另外一个程序。 然后就会出现下面的问题#xff1a; 首先父进程对应的环境变量的信息是从bash中来的#xff0c;因为我们自己写的父进程在运行的时候首先就要成为bash的子进程。这里我们将…exec系列接口中的环境变量 在之前我们学习了exec系类函数的功能就是将一个程序替换成另外一个程序。 然后就会出现下面的问题 首先父进程对应的环境变量的信息是从bash中来的因为我们自己写的父进程在运行的时候首先就要成为bash的子进程。这里我们将bash称为祖父进程我们自己写的父进程和父进程创建的进程为孙子进程。这三位其实使用都是一套环境变量bash的环境变量。在这里我们在bash中导入一个myval环境变量然后父进程会继承bash的环境变量而孙子进程也会继承父进程的环境变量。那么在孙子进程处打印环境变量我们应该可以看到MYVAL的环境变量。 下面就是在孙子进程中打印了所有的环境变量然后就发现了这个环境变量存在于孙子进程中。 果然这个环境变量会被孙子进程拿到。那么还有一个验证方法就是在父进程中手动导入一个环境变量然后再去看孙子进程是否会打印这个环境便量的值。 这个父进程自己导入的环境变量bash是看不到的因为这个导入的环境变量是导入在父进程自己的进程地址空间上的所以bash无法看到这个新导入的环境变量。 但是这个新的环境变量应该由子进程继承下去那么怎么在代码中实现呢 需要使用下面的函数 这样就将MYVAL2给导出去了这样子进程也应该继承这个环境变量如果这个环境变量被成功的导入到了父进程的上下文这个新增不会影响bash因为这里是bash先将自己环境变量继承给了父进程父进程在自己添加所以不会影响到bash而bash是先添加环境变量再将环境变量继承给子进程所以再bash中添加才会影响到子进程包括孙子进程而在父进程中新增不会影响到bash 果然在最终的子进程也能够看到这个父进程导入的环境变量。 而在bash这里没有看到这个MYVAL2的环境变量值 故验证了上面的结论 这里我们能够得出的初步结论就是 上图中右边的圆圈看作是bash原本的环境变量而蓝色的方框是bash新增的环境变量红色的方框是父进程新增的环境变量。父进程新增的环境变量不会影响到bashbash新增会影响到父进程但是这两个进程的新增都会影响到孙子进程孙子进程会包含前面所有的新增。 下面还有一个结论: 首先这个结论也就帮助我们理解了为什么我们的子进程在替换成为其它程序之后还能够打印出环境变量。即使我们在父进程这里使用execl没有传递任何有关环境变量的值。 那么这里的原理是什么呢 原因如下 首先父进程自己的task_struct指向自己的进程地址空间而在进程地址空间中的地址都经过页表映射映射到了物理内存中而在父进程栈区的上方我们可以看到一个储存命令行参数和环境变量的空间。而此时父进程创建了子进程子进程就会将父进程中的task_struct全部拷贝一份页表也是如此除了如果发生写入会引起写时拷贝以外子进程会完全拷贝一份父进程的task_struct和页表由此父进程的环境变量也就被子进程继承下来了。 但是还有一个问题这里的环境变量和命令行参数也是父进程/子进程的数据啊而进程的程序替换会替换进程的代码和数据 而根据我们一开始的结论环境变量的继承不会受到程序替换的影响我们能够知道一个事实就是 即无论存在多少子进程这些子进程的环境变量都会从父进程处获取即使子进程执行了程序替换也没有关系程序替换不会影响环境变量。所以如果要将环境变量传递给子进程很简单在父进程中导入新的环境变量后直接fork创建子进程即可。这里即使你是在子进程中使用environ来获取环境变量也能够获得从父进程那里新增的幻境变量。 以上我们都是在验证一个结论那就是环境变量是具有全局属性的无论你的子进程是否进行了程序替换子进程中的环境变量一定是从父进程那里继承得来的。 在子进程获得环境变量的时候具有两种情况 第一种子进程要原封不动的获得父进程的环境变量 有两种方法第一种就是直接用默认就是原封不动的传递 第二种使用execle 这里代码也能解释为什么程序替换后的新程序能够得到我们在bash中输入的命令行因为在替换程序的时候这个exec接口就会将命令行参数环境变量等信息传递给替换后的新程序。所以在替换后的新程序里面能够打印出我们传递给新程序的命令行参数可以打印因为这些命令行参数都被传递到了下图中的argv数组中打印数组中的内容就能够看到我们传递给新程序的命令 这样也完成了将父进程的环境变量原封不动的传递给了子进程其实还可以使用main函数的参数但这里我选择了使用environ这个全局的变量。 然后下面是test.cc的文件也是形成mytest的源文件 运行结果 由此就能得到结论 还有第二种情况 这也很简单我们首先创建一个我们自己的环境变量列表 这里出现报错的原因是数组中的内容是const char*的内容但是数组是一个char* const的数组不替换成const char*因为execle这个函数最后需要的环境变量列表就是一个char* const的列表所以这里才这么写。 然后下面是运行 然后此时的子进程能够拿到的环境变量就是我们新创建的环境变量表了。 这里还能得出一个结论就是 这里传递环境变量不是新增式的传递而是覆盖式的传递环境变量。 还有第三种情况 此时我们只需要在父进程中导入新的环境变量 然后执行下面的 这样我们的子进程就能够得到新增的环境变量了包含原来的环境变量 此时无论你是显示的传递环境变量还是隐式的传递环境变量都能够让子进程得到原来的环境变量和新增的环境变量。 由此就能得到下面的三种交给子进程环境变量的方式 而环境变量在传递的时候以覆盖式方式传递。 下面我们再来认识一下exec*系列 然后会发现在2号手册中还存在一个execve的函数为什么它会单独在一个手册中呢因为计算机在底层实现的时候就是将左边的这些函数的参数都替换成了右边函数的参数然后再去执行execve的所以即使你不传环境变量底层也会自动的传递父进程的环境变量给替换后的程序。而存在左边的这些函数原因主要式为了满足各种调用的场景。 下面我们如果将子进程中的程序替换修改成子进程会去执行ls命令那么如果我们调用父进程是不是就相当于父进程创建了一个子进程而这个子进程则帮助父进程去执行了ls命令。如果我们能够将这个父进程一直运行下去那么也就意味着父进程能够一直去接收我们用户输入的指令然后父进程将指令打散成字符串数组fork后传递给子进程让子进程能够去运行这个命令此时我们的父进程是不是就相当于一个bash。而不进行循环那么父进程就相当于一个只执行一次的shell。 模拟实现shell 打印命令行提示符 下面我们就来设计一个属于我们自己的简单版的shell。 首先就是 这个其实就是一个字符串在bash进程将这一个字符串打印出来之后bash进程会一直等待我们用户输入指令 。 首先第一步我们要来创建Makefile文件和.c文件。 然后我们要来打印一下上面的LHY那些提示信息用户名主机名工作目录。 首先上面的这些信息都存在于环境变量中 其实在系统中是存在获取这些信息的系统调用的但是这里我就不去调用这些系统调用了而是自己写一下 这些信息都存在于环境变量中下面我们要使用一个函数接口帮助我们从环境变量中获取这些信息。 这里使用getenv来根据环境变量名字获取环境变量内容。 写好后的代码 下面我们保存退出来运行一下 此此时我们就成功的构建了一个我们自己的命令行提示符。$是os的#是我自己写的。 那么下一个步骤自然是要让命令行停止之后让我们的用户能够输入命令。 获取用户输入的命令 首先假设最长的命令也不可会超过1024那么就先定义一个1024字节大小的命令储存数组。 运行后就会发现不对标准的命令行光标是在命令行提示符的后面停止的但是我们写的却是在命令行提示符的下一行停住的。 原因就是在打印的后面带上了一个\n 把他去除就可以了。 这样就符合标准了。下面我们需要检测一下我输入的这个命令有没有保存在这个数组中呢 这里我们先做一个打印检测一下 然后就会发现在usercommand中没有空格以后的内容原因则是scanf在输入命令的时候遇到空格和换行都会停止输入。 所以这里的解决方法就是不使用scanf输入。存在一个按行获取的接口就是fgets 其中第一个和第二个对应的是你要将读到的内容放到哪一个缓冲区中而第三个则是对应你要从哪一个文件中读取。 那么为什么要挑选这一个接口呢 而这里键盘其实就是标准的输入流所以这里的fgets就从stdin中获取信息读到缓冲区中。如果fgets获取成功了那么返回的就是你所获得的这个字符串储存位置的起始地址获取失败了就是NULL。 但是这里出现了一个问题那就是我在下面打印数组中的内容的时候明明没有将\n放到printf中但是为什么最后打印完后还是换行了呢 原因是我们在输入完成一个命令行后肯定会按一下enter键这个键就是换行的意思所以这里即使我在打印的地方没有加入\n也会多出一个\n 啊啊啊啊啊但是我们并不想要这个\n怎么解决呢我们知道的是这个\n肯定是在usercommand最后一个字符的后面所以我们可以这么修改一下 这里strlen遇到\n也会让个数1计入到有效字符中除非遇到\0才会结束 这里会出现越界访问吗很明显不会因为就算你什么都不输入最后还是会按一个enter键这个按键就会被输入进去此时的strlen为1减去一个1之后为0此时就将uset中的内容修改成空串了。那么也就不会出现越界的情况。 下面就不会出现这个换行了。 而我们现在在上面写的所有内容都可以被称作为获取命令行输入所以这里我们就来封装一下 那么下面我们就是需要将我们所得到的字符串做判断和分割。分割完后就要使用子进程去执行命令了。 分割用户输入的命令 那么首先来解决分割的问题如果是在c中可以使用substr和find函数来解决但是c中没有substr和find。 这里的方法可以使用两个常量记录一个有效字符的长度首先begin和end都指向了l然后让end。直到end指向空格将这之中的字符赋值给一个新的字符串。这是一个方法但是过于麻烦。还有一个方法就是遍历或者字符串将空格变成\0,然后使用指针分别指向第一个/第二个/第三个字符串这样也能得到分割后的字符串。这也是很麻烦的。并且我们被打散的字符串也是需要我们去将其保存起来的那么在c中存不存在一个接口能够做到打散字符串并将其保存呢 为了解决上面的问题我们首先肯定是要先拥有一个能够保存分割后字符串的空间。 这里我们就是创建一个指针数组并且保证让第一个位置指向的是开始命令的开头其余在后面排序分布。最后当将命令行集齐完整之后以NULL结尾。为了保证分割后的子串是一个独立的子串所以这里还需要保证ls后面-a后面和-l后面都是一个\0。 所以我们这里采用的方式是将每一个空格的位置都变成\0但是这个工作由我们去做吗 这里可以采用字符串分割函数c中的字符串分割函数strtok。 这个函数在使用的时候需要注意首先在第一次分割的时候需要指明要分割的是哪一个字符串以及分割符号然后这个函数会返回分割后字符串的开始位置的指针。 然后在下一次使用strtok的时候就不要指明要分割的字符串了只需要写明分割符号然后当strtok将原字符串分割完毕之后会返回NULL。 下面是第一种将字符串分割然后储存的方法。 这里存在一个错误就是在打印argv数组中的内容的时候不应该等于argc应该是小于 还有一种写法不要放了strtok的返回值失败后是一个NULL所以我们可以使用下面的写法。 这两种写法都是可行的这里我选择下面的这种下面自然是要去完成这种写法的封装了。 需要注意的是这里我将空格#defiine了SEP就是字符串空格 然后是运行的结果 运行结果 由此我们就完成了对字符串的分割。 创建子进程使用程序替换执行命令 下面就是要去执行对应的命令了首先执行命令的时候父进程肯定是不能去运行命令的内建命令除外因为如果及那个父进程替换成新的程序那么我们之前写的代码都白写了因为程序替换会替换代码和数据所以这里我们是使用一个子进程去执行对应的命令。 在创建完子进程之后的下一步自然就是选择合适的程序替换接口了。 那么那么多的程序替换接口这里我们选择哪一个呢 这里选择的是execvp原因如下首先就是分割完的字符串已经被放到一个agrv中了其次我们不知道绝要替换的程序所在绝对文件路径只能让操作系统自己去环境变量中寻找我们只需要提供给一个程序名字即可。所以 这里选择的程序替换接口就是execvp。 程序如下 在不进行封装的情况下就完成了。但是上面的代码还存在一个问题那就是我们的shell只会跑一次所以这里我们还需要在做一个操作那就是让shell死循环即可 此时我们就可以去执行lstop等等简单的命令了而且我们的shell并不会执行完一次之后就退出了。 下面我们来解决一种极端的情况那就是如果用户什么命令都不输入那么这个时候我们的shell虽然不会报错但是如果用户什么命令都没有输入那么我们的shell也不应该去创建子进程所以我们修改一个getusercommad的返回值。 这样也就能够解决输入空串或者是读取数据失败的情况了。 但是这么写的话会显得很不好看所以这里我们依旧将执行命令的这些语句做一下封装。 以上我们就能够完成一个比较简单的shell了这里我们也知道了os中的shell也是一个进程它能够不同的运行的原理是因为它是一个死循环所以为了防止shell挂了所以shell会创建子进程去完成用户输入的命令如果shell不创建子进程执行命令除了shell只能执行一次之外还有shell会存在挂掉的风险如果shell挂掉了就没有软软件做命令行解释了而因为每一个进程都是具有独立性的所以即使子进程挂了也不会影响shell的运行。 下面我们再来完善一下我们这个简单shell的遗留问题。 实现内建命令 cd命令 第一个遗留问题 会发现pwd命令不会更新我们当前所在的路径原因在于这个cd命令是在子进程上执行的也就是cd一直在修改子进程的工作目录而父进程则一直没有变化所以为了让父进程能够修改工作目录cd这个命令只能由shell来完成不能创建子进程去执行这种只能由shell来执行的命令也就是内建命令 所以我们需要修改一下我们的bash让其在执行命令之前检查一下这个命令是否是内建命令 程序如下 如果此时这里的这个命令是内建命令那么我就会去执行cd函数那么我们如何做到将mybash的工作目录做出修改呢 很简单在c中存在一个函数接口就是chdir。这个函数就能够帮助我们切换到指定的工作目录。 由此我们就能够完成第一个内建命令。 下面我们再来理解什么是内建命令呢 使用易于理解的话表示就是所谓的内建命令也就是shell内部的一个函数。由此内建命令没有创建子进程。 下面是运行结果 现在能够完成在父进程中修改工作目录了但是现在还存在一个问题那就是我们的命令行提示符中的路径依旧没有改变。 那么我们在完成一次cd命令之后肯定要更新一下环境变量PWD。 然后在mybash运行获取pwd的时候就会获得新的文件路径了。 那么这里我们既然要修改环境变量中的pwd首先就要获取到新的环境变量值。 可以使用sprintf()这个函数 最后是运行截图 可以看到我们自己的shell中的命令行提示符确实是改变了。 但是这里是存在几个问题的。 首先第一个问题在我们将cwd这个环境变量导入到mybash中时当cd这个函数执行完毕之后cwd这个空间就会被回收但是这个空间中保存着我需要的环境变量啊所以这里第一个需要改变要将cwd这个数组变成一个全局的数组要让其一直存在只要myshell这个进程不结束那么这个cwd就不应该消失 下面一个问题就是当我们输入的是一个绝对路径的时候我们的命令行提示符确实是将绝对的路径打印出来了但是如果我们输入的是相对1路径(.和“..)呢 从上图可以看到因为用户输入的path只是一个.也就导致了环境变量导出错误了所以这里需要再使用一个c函数接口 在C语言中可以使用getcwd函数来获取当前工作目录即pwd。getcwd函数的原型如下 char *getcwd(char *buf, size_t size); 该函数将当前工作目录的绝对路径存储在buf指向的缓冲区中并返回指向该缓冲区的指针。size参数指定了缓冲区的大小。 下面是代码的修改 这里的cwd就是我在全局设定的一个字符数组大小依旧是1024字节 最后是运行的截图 可以看到此时我们的这个shell就是一个完善的shell了命令行提示符中的这个文件路径也会一直在修改。  export命令 下面我们在来实现一个内建命令export这个命令很明显是需要mybash这个父进程自己进行环境变量的导入。在c中是存在一个接口函数能够导出环境变量putenv 如果这么去导出环境变量是会存在问题的。 我现在导入了一个myval1然后下面我们再来执行一下其它的命令再来查看以下环境变量。 这里我执行了一个错误的ls命令然后再去查看env就发现我们新导入的那个环境变量不见了。 原因是如果我们使用的是下面的这个方法 此时的argv中的指针指向的是下面的这个数组 但是每一次循环输入命令都会重现给usertcommand赋值对这个数组usercommand进行划分之后的各个字符串就被储存在字符串数组argv中了重新赋值之后我们储存myval的那个空间就被新的值覆盖了所以再次查看环境变量的时候我们就看不到这个环境变量了。 所以我们需要再mybash中设定一个二维数组用于储存新的环境变量。然后在拷贝了argv[1]中的内容之后再将这个环境变量导出。 创建新增环境变量的空间。 下面是运行的截图 在执行完新的命令后再去看这个环境变量 myval依旧存在。 echo $?/(环境变量命令) 下面我们再来实现一个echo $PATH/?的命令 首先这个命令echo $?输出的是 上一次进程的退出码所以这里我们需要完善一下这个myshell.c。 所以我们在这里先创建一个全局变量用于记录 最后一次进程的退出码 对于内建命令因为一直都是执行成功所以这里就不修改退出码让这个退出码一直都是0但是对于非内建命令我们需要获取到子进程的退出码 所以这里我们需要这么写。 然后再来修改echo的内建命令 这里需要注意的是argv是一个指针数组而我们要获得的?/PATH是在#这个字符串的后面所以我们首先让一个指针指向$,然后让其1然后判断这个字符是否是?. 运行截图 这里我首先就导致了ls常出现错误然后打印上一次的退出码139然后如果我想要打印环境变量自然也是可以打印的。 完整的代码 #includestdio.h #includeunistd.h #includesys/types.h #includesys/wait.h #includestring.h #includestdlib.h// getenv需要的头文件 #define NUM 1024 // 这里就是假设最长的命令大小 #define SIZE 64 // 假设最长的一条命令能够被划分成为下面的一部分 #define SEP // 这里依旧是设定一个分割的符号 char cwd[1024];// 创建一个全局的字符数组用于储存文件路径 char env[100][100];int n 0;// 这里使用一个二维数组去储存环境变量 //这里能够储存100个新增的环境变量每一个环境变量的大小最多也就是100个字节然后n代表的就是新增的环境变量的个数 int lastcode; // 这个全局变量里面保存的就是最后一次进程的退出码 #define debug 1// 用于激活分割字符串中的打印代码 const char* getUsername() {const char* name getenv(USER);// 获取用户名if(name){return name;}return none;// 如果用户名字获取失败则返回一个none } const char* getHostname() {const char* hostname getenv(HOSTNAME);if(hostname){return hostname;// 主机名如果获取成功则直接返回}return none;// 主机名获取石板返回none } const char* getCwd() {const char* cwd getenv(PWD);if(cwd){return cwd;// 路径获取成功}return none;// 路径获取失败 } int getUsercommand(char* command,int num) {printf([%s%s %s]# ,getUsername(),getHostname(),getCwd());char* ret fgets(command,num,stdin);// 这就就是从标准输入流中读取数据然后放到usercommand数组中// 下面检测一下输入的命令究竟存进去没有if(ret NULL){// 这里就代表着从标准输入流处获得的命令失败了return -1; // 如果获取信息失败了返回一个-1}// 运行到这里代表数组中肯定是存在内容了command[strlen(command)-1] \0; // 首先使用strlen获取usercommand中有效字符的个数然后字符的个数减去1就是\n所在的位置// 因为strlen在计算长度的时候\n也会被计入到计算中return strlen(command); } void commandSpilt(char* argv[],char* command)// 这个函数的功能就是将usercommadn中的内容进行分割并放到argv中 {int argc 0;argv[argc] strtok(command,SEP);while(argv[argc] strtok(NULL,SEP)); #ifdef debug // 这里就是一个条件编译如果#define debug设置的值是非0就会执行下面的代码for(int i 0;argv[i];i){printf(%d:%s\n,i,argv[i]);} #endif } int excute(char* argv[])// 创建子进程失败返回-1正常执行返回0 {pid_t id fork();if(id 0)// 如果创建子进程失败了那么就让其直接退出{return -1;}// 因为在我们现在执行的情况下不可能出现子进程创建失败的情况所以这里我们就不考虑创建子进程失败的退出码了else if(id 0){// childexecvp(argv[0],argv);// 要执行的命令储存在argv数组中的第一个位置而选项自然就在后面exit(1);//子进程在完成替换之后直接退出即可}else{// fatherint status;pid_t rid waitpid(id,status,0);// 使用阻塞等待的方式等待子进程去执行命令if(rid0){//等待成功之后去获取子进程的退出码lastcode WEXITSTATUS(status);// 这里通过一个宏来得出,上一个子进程的退出码}return 0;} } void cd(char* path) {// 在完成了切换路径之后我们需要修改一下main函数中的环境变量chdir(path);char tmp[1024];// 创建一个临时的数据空间getcwd(tmp,sizeof(cwd));// 这里将当前的绝对路径写入到tmp中sprintf(cwd,PWD%s,tmp); // 再将当前的绝对路径写到cwd中putenv(cwd);// 这里就是将PWD中的内容导出到环境变量中去因为在shell的原来的环境变量中本来就包含存在一个PWD所以这里就会将PWD中的值覆盖了 } int doBuildin(char* argv[]) // 这个函数的功能就是检查当前的命令是否是内建命令如果是内建命令那就执行不是那就直接退出即可 {if(strcmp(argv[0],cd) 0){//此时代表此时要执行的是cd命令而cd则是内建命令char* path NULL;if(argv[1] NULL){path .;cd(path); // 这里去执行cd命令}else{path argv[1];cd(path);// 不然就转换工作目录到argv[1]中}return 1;}else if(strcmp(argv[0],export)0)// 这里就是需要执行将一个环境变量导入到mybash中的环境变量表中{// 但是不要忘了我们现在将一个环境变量导出去之后需要使用一个全局的空间去储存这个新增的环境变量strcpy(env[n],argv[1]);// 拷贝当前要新增的环境变量于env的一个空间中putenv(env[n]);// 再将这个环境变量导出之后让n}else if(strcmp(argv[0],echo)0)// 这里我们再完成一个内建命令echo{// 下面我们需要获取的就是echo 后面的?这里$和是在一个字符串中的char* str argv[1];// 让str指向$,if(strcmp(str1,?)0)// 让char*的指针1,跳过一个字符之后自然就让其指向了?这个字符或者是非问号的path{printf(%d\n,lastcode);// 打印上一个进程的退出码lastcode 0;// 此时的echo也是一个命令退出码自然要修改}else{// 这里代表要打印环境变量的值printf(%s\n,getenv(str1));}}else if(0)// 这里是其它的内建命令{}return 0;// 不是内建命令返回0 } // 上面的三个函数能够帮助我们获取用户名主机名和当前的路径 int main() {char usercommand[NUM];// 创建一个命令储存数组// 打印命令行提示符获取用户输入的命令成功while(1){int k getUsercommand(usercommand,sizeof(usercommand)); // 在这里调用一下获取用户输入的函数然后将我们提供的缓冲区命令储存数组传递过去if(k0){continue; // 当我们什么都不输入或者是读取信息错误的时候就不再往下执行}// 下面我们需要将从用户处获得到的命令分割成符合要求的子串char* argv[SIZE];// 储存分割后的字符串的空间commandSpilt(argv,usercommand); // 完成字符串的分割int l doBuildin(argv);if(l){continue;// 然后就是内建命令的返回值设定}// 下面就是需要父进程去创建子进程执行命令excute(argv);// 首先对于需要创建子进程执行的命令这里统一退出码为0}return 0; }这个文件我还没有完全的完成以后完善了之后会继续在这篇博客上更新。 希望这篇博客能对你有所帮助写得不好请见谅。如果发现了任何的错误欢迎指出。
http://www.tj-hxxt.cn/news/221845.html

相关文章:

  • 网站建设新闻 常识网站建设五年发展规划
  • 四平网站制作网站权重排行榜
  • 淄博学校网站建设定制宿州做网站的公司有哪些
  • 怎么封闭网站WordPress挂文件下载
  • 正能量晚上看的网站2021wordpress设置自定义连接打不开
  • 网站后台怎么传图片网站建设的步骤
  • 网站友情链接的作用上海知名装修公司排行
  • 帮人做网站好挣吗多模室内设计网
  • 郑州网站建设彳汉狮网络计算机应用软件开发
  • php网站模块潍坊做外贸网站
  • 中国工信部官网查询网站备案网站开发成功案例
  • 网站对话窗口怎么做网络推广公司官网
  • 书法网站建设建站案例
  • 网站平台建设缴纳什么税普通电脑怎么做网站服务器
  • 网站有中文源码加英文怎么做全网平台整合营销推广
  • 网站推广需要域名迁移优化大师安卓版
  • 卫辉网站建设网站建设公司湖南
  • 建网站郑州网站建设 新手从
  • 机械行业网站建设北京望京企业网站建设
  • 工信部的网站备案信息查询医疗微网站建设计划书
  • 网站做蜘蛛池有用吗广州网站排名优化
  • 手机网站大全免费成都到西安
  • 烟台企业自助建站系统温州外贸网站推广
  • 网站建设费用大全app应用公司
  • 局域网网站怎样做数据库企业名称注册查询官网入口
  • 网站建设要学编程吗企业邮箱怎么获取
  • 长春地区网站建设专业建设验收网站
  • 168网站建设临沂专门做网站的
  • 网站logo做h1标签网站 页面风格 建设
  • 建设银行e房通网站建设部执业考试中心网站