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

网站建设大概多少费用做乡镇网站

网站建设大概多少费用,做乡镇网站,温州集团网站建设公司,谷歌优化 网站建设一、信号的概念 信号是一种向目标进程发送通知消息的机制 信号的特性(可以结合红绿灯、防空警报等生活样例来理解) 1、在信号没有出现之前#xff0c;我们就已经知道如何去处理信号#xff0c;即我们认识信号 2、信号是异步产生的#xff0c;即我们不知道它具体何时产生 3、…一、信号的概念 信号是一种向目标进程发送通知消息的机制 信号的特性(可以结合红绿灯、防空警报等生活样例来理解) 1、在信号没有出现之前我们就已经知道如何去处理信号即我们认识信号 2、信号是异步产生的即我们不知道它具体何时产生 3、当信号产生时我们可以对它暂时忽略不做处理(比如我们外卖到了但是你正在和朋友开黑就会将外卖暂时搁置) 4、由于我们有时不会立即去执行信号所以我们需要能储存信号 信号列表如下 一些补充的知识 二、信号的产生 1、通过键盘进行信号的产生 解释如下 该系统调用接口可以自定义捕捉信号的行为将signum信号的默认行为改为handler //process.cc #include iostream #include unistd.h #include signal.h #include stdlib.hvoid handler(int signo) {std::cout发送了一个2号信号std::endl;exit(1); }int main() {signal(2,handler);std::cout pid: getpid() std::endl;while(1){std::cout running std::endl;sleep(1);}return 0; }对信号的进一步理解 man 7 singal  查看信号的默认行为 既然我们能通过signal系统调用将信号的默认方法改变那么我们能否将所有能杀死进程的信号改掉使得进程无法被终止呢  很显然是不行的有些信号是无法被自定义捕捉比如9号信号保证了OS的安全 2、通过系统调用进行信号的产生 功能向pid进程发送sig信号kill命令就是调用的该系统调用 举个例子(写一个自己的kill命令) //test.c #include stdio.h #include unistd.h int main() {printf(%d\n,getpid());while(1){printf(running\n);sleep(1);}return 0; }//mykill.cc #include iostream #include unistd.h #include signal.h #include stdlib.h #include sys/types.hint main(int argc,char* argv[]) {if(argc!3){std::cout\nUsage: argv[0] -signnumber processid std::endl;return 0;}int signnumber std::stoi(argv[1]1);int pid std::stoi(argv[2]);kill(pid, signnumber);return 0; }功能向本进程发送sig信号 举个例子 #include iostream #include unistd.h #include signal.h #include stdlib.h #include sys/types.hvoid handler(int signo) {std::cout发送了一个 signo 号信号std::endl; }int main() {signal(2,handler);while(1){raise(2);sleep(1);}return 0; } 功能向本进程发送6号信号且进程一定会终止 举个例子 #include iostream #include unistd.h #include signal.h #include stdlib.h #include sys/types.hvoid handler(int signo) {std::cout发送了一个 signo 号信号std::endl; }int main() {std::cout getpid() std::endl;signal(6,handler);// abort();while(1){std::cout running std::endl;sleep(1);}return 0; }3、通过硬件异常进行信号的产生 原理如下 那么除0异常是发送的哪个信号呢是8号信号验证如下 根据上图发送的确实是8号信号但现在还有一个问题进程出异常OS发送8号信号可以理解但是为什么它一直在打印呢明明我的代码没有循环啊 因为原本的8号信号被自定义捕捉成了打印语句导致进程无法退出所以进程依旧会在等待队列中等待CPU调度所以各种寄存器中存放的该进程相关的数据(即进程上下文)都会被保留包括状态寄存器而一旦该进程被调度那么OS就又会检测到硬件错误向进程发送8信号如此反复故有上面的现象发生。 (一旦进程退出它的相关数据就会被丢弃因为我们不在需要调度该进程了) OS杀死进程就是处理问题的方式之一 程序运行出现异常如何做取决于用户但一般都是要让进程退出的注意异常的处理很多时候只是打印错误 顺便说一下*nullptr发送的信号是11号信号本质是页表中没有该地址的物理地址的映射关系引发的硬件错误 4、通过软件条件进行信号的产生 这个其实在之前的博客中就讲过一些示例比如管道中只要读端关闭写端还在写OS就会向写端发送SIGPIPE终止写端它本质是因为OS的内核数据中发现该管道只被一个进程打开所以发信号终止写端不是硬件异常而是软件条件产生的信号。又比如调试程序用的gdb向进程发送的SIGSTOP和SIGCONT都是软件条件产生的信号。 这里再介绍一个alarm函数 #include unistd.h unsigned int alarm(unsigned int seconds); 调用alarm函数可以设定一个闹钟也就是告诉内核在seconds秒之后给当前进程发送SIGALRM信号(14号信号)该信号的默认处理动作是终止当前进程 演示如下 上面两段代码都是设置了1秒的闹钟确实时间到了进程就终止了但是两段代码cnt的运行次数却是天差地别唯一的区别就是有没有与外设进行IO交互也证明了IO的效率很低 多次使用alarm函数就是刷新闹钟并返回之前闹钟剩余的时间注意alarm(0)不是设置0秒的闹钟这个相当于清空之前的闹钟并返回之前闹钟剩余的时间 了解(扩展)---帮助理解alarm 这里讲讲alarm是如何实现的首先每个进程都能设置闹钟也就是说OS中可以存在多个闹钟所以需要管理即先描述在组织所以我们要设计一个闹钟结构体里面包含闹钟所属进程id时间用时间戳记录等等根据需求往里面加属性。然后就是如何组织即选择什么样的数据结构进行管理这里可以选择用小堆按照时间大小存放如果时间堆顶闹钟的时间没到说明所有的都没到不做处理如果时间到了就拿出来处理。(当然这不一定是Linux中alarm的实现这里只是提供思想具体如何实现得根据需求) core dump(核心转储)  介绍 分析core dump是Linux应用程序调试的一种有效方式core dump又称为“核心转储”是该进程实际使用的物理内存的“快照”。分析core dump文件可以获取应用程序崩溃时的现场信息如程序运行时的CPU寄存器值、堆栈指针、栈数据、函数调用栈等信息。 Core dump是Linux基于信号实现的。Linux中信号是一种异步事件处理机制每种信号都对应有默认的异常处理操作默认操作包括忽略该信号Ignore、暂停进程Stop、终止进程Terminate、终止并产生core dumpCore等 这里也简单说一下为什么有的信号终止进程需要核心转储而有的不需要 我们可以看一下那些不需要核心转储的终止信号如SIGKILL它其实并不是进程出现异常而将进程杀死更类似于用户强制终止进程也就是说进程本身并没有问题(或者出错原因很明显)所以我们不需要core dump再去分析异常但是像SIGSEGV信号即段错误信号我们只能知道是内存出现问题但是具体是数组越界还是其他什么问题引发的我们并不清楚所以我们需要core dump帮助我们去分析。 但其实通过上面我们的示例代码和结果截图我们会发现Term/Core的功能好像都一样只是终止进程并没有产生core dump文件这是什么原因呢 因为核心转储的文件太大了我们用的是服务器默认将core dump大小设置为0即不生成核心转储防止服务器被写满虚拟机应该是开启的当然可以通过指令打开如下 注意核心转储只能在对应的shell中生成即哪个shell设置了core dump的大小哪个shell跑程序收到异常才会生核心转储文件 上面的短短5行代码就需要生成55万字节的文件如果代码在多一点文件只会更大所以为了保证服务的安全系统默认将core dump文件大小设置为0 那么核心转储的文件有什么用呢 三、信号存储 信号的相关概念 实际执行信号的处理动作称为信号递达(Delivery)信号从产生到递达之间的状态称为信号未决(Pending)---即在信号位图中进程可以选择阻塞 (Block )某个信号---未决之后暂时不递达 被阻塞的信号产生时将保持在未决状态直到进程解除对此信号的阻塞才执行递达的动作 注意阻塞和忽略是不同的只要信号被阻塞就不会递达而忽略是在递达之后可选的一种处理动作 如何在OS中体现上面说的三个概念---递达、未决、阻塞 每个信号都有两个标志位分别表示阻塞(block)和未决(pending)还有一个函数指针表示处理动作。信号产生时内核在进程控制块中设置该信号的未决标志直到信号递达才清除该标志。在上图的例子中SIGHUP信号未阻塞也未产生过当它递达时执行默认处理动作。SIGINT信号产生过但正在被阻塞所以暂时不能递达。虽然它的处理动作是忽略但在没有解除阻塞之前不能忽略这个信号因为进程仍有机会改变处理动作之后再解除阻塞。SIGQUIT信号未产生过一旦产生SIGQUIT信号将被阻塞它的处理动作是用户自定义函数sighandler。如果在进程解除对某信号的阻塞之前这种信号产生过多次将如何处理?POSIX.1允许系统递送该信号一次或多次。Linux是这样实现的:常规信号在递达之前产生多次只计一次,而实时信号在递达之前产生多次可以依次放在一个队列里。不讨论实时信号 四、信号阻塞 sigset_t 未决和阻塞标志可以用相同的数据类型sigset_t来存储sigset_t称为信号集这个类型可以表示每个信号的“有效”或“无效”状态在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞而在未决信号集中“有效”和“无效”的含义是该信号是否处于未决状态。阻塞信号集也叫做当前进程的信号屏蔽字(Signal Mask)这里的“屏蔽”应该理解为阻塞而不是忽略 信号集操作函数 int sigemptyset(sigset_t *set);将所有信号的对应bit清零表示该信号集不包含任何有效信号int sigfillset(sigset_t *set);将所有信号的对应bit置1表示该信号集的有效信号包括系统支持的所有信号int sigaddset (sigset_t *set, int signo);在该信号集中添加某种有效信号int sigdelset(sigset_t *set, int signo);在该信号集中删除某种有效信号int sigismemberconst sigset_t *set, int signo);用于判断一个信号集的有效信号中是否包含某种信号若包含则返回1不包含则返回0出错返回-1 注意在使用sigset_ t类型的变量之前一定要调用sigemptyset或sigfillset做初始化使信号集处于确定的状态 sigprocmask 功能介绍如果oset是非空指针则读取进程的当前信号屏蔽字通过oset参数传出。如果set是非空指针则更改进程的信号屏蔽字参数how指示如何更改。假设当前的信号屏蔽字为mask下表说明了how参数的可选值 SIG_BLOCKset包含我们希望添加到当前信号屏蔽字的信号相当于mask mask|setSIG_UNBLOCKset包含我们希望从当前信号屏蔽字中解除阻塞的信号相当于mask mask~setSIG_SETMASK设置当前信号屏蔽字为set所指向的值相当于maskset 如果调用sigprocmask解除了对当前若干个未决信号的阻塞则在sigprocmask返回前至少将其中一个信号递达 演示如下 当然可能有人会说既然能屏蔽信号我们能不能将所有的信号全部屏蔽 当然不行跟有些信号无法被自定义捕捉是一个道理如9号信号这里就不验证了 sigpending 读取当前进程的未决信号集通过set参数传出。调用成功则返回0出错则返回-1 演示从阻塞到递达的过程如下 #include iostream #include unistd.h #include signal.h #include sys/types.hvoid handler(int signo) {std::cout 接收到 signo 信号 std::endl; }void Printf_Pending(const sigset_t pending) {for (int i 31; i 1; i--){if (sigismember(pending, i))std::cout 1;elsestd::cout 0;}std::cout \n; }int main() {std::cout pid: getpid() std::endl;signal(2, handler);sigset_t set;sigemptyset(set);sigaddset(set, 2);sigprocmask(SIG_BLOCK, set, nullptr); // 屏蔽2号信号std::cout 屏蔽了2号信号 std::endl;int cnt 0;while (1){sigset_t pending;sigpending(pending);Printf_Pending(pending);sleep(1);cnt;if (cnt 10){std::cout 解除对2号信号的阻塞 std::endl;sigprocmask(SIG_UNBLOCK, set, nullptr);}}return 0; } 这里有一个问题pending位图的置0操作和信号的递达谁先发生 我们可以在handler方法中打印pending位图如果已经为0则置0操作先发生反之相反 显然我们是先将pending位图的对应信号比特位置0在执行信号的递达操作 五、信号的捕捉 信号在什么时候被处理 --- 进程从内核态返回用户态的时候进行信号的检测和递达 用户态是一种受控的状态能够访问的资源是有限的内核态是一种OS的共工作状态能访问大部分系统资源 系统调用的背后就包含了身份的变化 补充一些进程地址空间的内容 内核空间对应的页表和OS资源只需要一份因为所有的进程都需要就像动态库资源我们只要加载一份就行需要的就去映射。所以CPU在任何时间都能访问OS 可能有人会觉得如果我的程序中只是在死循环的打印语句没有进行系统调用那么信号是不是就被处理了 当然不是进程是要被OS调度切换的而当进程被放在CPU上执行时本质就已经完成了从OS(内核)到用户的转换所以信号有无数次机会被检测处理 Sigaction #include signal.h int sigaction(int signoconst struct sigaction *actstruct sigaction *oact); 功能可以读取和修改与指定信号相关联的处理动作。调用成功返回0出错返回-1参数signo是指定信号的编号。若act指针非空则根据act修改该信号的处理动作。若oact指针非空则通过oact传出该信号原来的处理动作。act和oact指向sigaction结构体 只关注被框出来的两个成员 将sa_handler赋值为常数SIG_IGN表示忽略信号赋值为常数SIG_DFL表示执行系统默认动作赋值为一个函数指针表示用自定义函数捕捉信号。和signal函数的第二个参数类似 当某个信号的处理函数被调用时内核自动将当前信号加入进程的信号屏蔽字当信号处理函数返回时自动恢复原来的信号屏蔽字这样就保证了在处理某个信号时如果这种信号再次产生那么它会被阻塞到当前处理结束为止。 如果在调用信号处理函数时除了当前信号被自动屏蔽之外还希望自动屏蔽另外一些信号则用sa_mask字段说明这些需要额外屏蔽的信号当信号处理函数返回时自动恢复原来的信号屏蔽字。sa_flags字段包含一些选项这里我们不关心所以把sa_flags设为0sa_sigaction是实时信号的处理函数 演示如下 #include iostream #include unistd.h #include signal.h #include sys/types.h void Printf_Pending(const sigset_t pending) {for (int i 31; i 1; i--){if (sigismember(pending, i))std::cout 1;elsestd::cout 0;}std::cout \n; }void handler(int signo) {std::cout 接收到 signo 信号 std::endl;while (1){sigset_t pending;sigpending(pending);Printf_Pending(pending);sleep(1);} }int main() {std::cout pid: getpid() std::endl;struct sigaction act, oact;act.sa_handler handler;sigemptyset(act.sa_mask);sigaddset(act.sa_mask, 3);sigaction(2, act, oact);while (1)sleep(1);return 0; } 显然sigaction函数不仅将自定义捕捉的2号信号在运行时自行屏蔽而且可以通过sa_mask同时将其他的信号也屏蔽注意这些屏蔽会在2号信号处理结束返回后解除 并且返回后它还是会去检测是否有信号需要被递达至于信号被处理的顺序和信号优先级有关(这里就不演示了) 六、信号的补充问题(了解) 可重入函数 即可以重复进入的函数什么意思举个简单的例子我们学过用fork创建子进程当父进程和子进程同时执行printf语句时就有可能在屏幕上出现交替打印的情况导致输出的数据不符合预期这就说明printf语句是不可重入函数。反之如果多个执行流可以同时进入一个函数且不发生错误就是可重入函数。 注意可重入和不可重入是函数的一个特征并不能作为评判函数好坏的标准。一般来说使用公共资源的函数都是不可重入函数。 volatile---保持内存的可见性 #include iostream #include unistd.h #include signal.h #include sys/types.h int flag 0; void handler(int sigo) {flag 1;std::cout flag changed to : flag std::endl; }int main() {signal(2, handler);std::cout flag: flag std::endl;while (!flag);std::cout flag: flag std::endl;return 0; } 如果是正常编译程序不会有问题一旦优化(相当于release版)就会出bug为什么 因为flag变量被优化后直接放到了CPU的寄存器中在信号处理时我们改变的flag是内存中的flag并不会改变寄存器中的flag所以进程无法结束 我们可以给flag加volatile关键字保证内存的可见性即保证该变量一直从内存中读取得到 SIGCHLD信号(17号信号) 子进程在终止时会给父进程发SIGCHLD信号该信号的默认处理动作是忽略父进程可以自 定义SIGCHLD信号的处理函数这样父进程只需专心处理自己的工作不必关心子进程了,子进程终止时会通知父进程父进程在信号处理函数中调用wait清理子进程即可 1、验证子进程终止是否会向父进程发送SIGCHLD信号 #include iostream #include unistd.h #include signal.h #include wait.h #include sys/types.hvoid handler(int signo) {std::cout get signo sign std::endl; }int main() {signal(SIGCHLD, handler);pid_t id fork();if(id0){std::coutchild runningstd::endl;sleep(5);exit(1);}wait(nullptr);return 0; } 2、在handler函数中回收子进程 #include iostream #include unistd.h #include signal.h #include wait.h #include sys/types.hvoid handler(int signo) {std::cout get signo sign std::endl;wait(nullptr); }int main() {signal(SIGCHLD, handler);std::cout father pid: getpid() std::endl;pid_t id fork();if (id 0){std::cout child pid: getpid() std::endl;std::cout child running std::endl;sleep(5);exit(1);}return 0; } 上面的是针对只有一个子进程的情况但是如果有多个进程呢 我们知道如果多个子进程同时退出发送17号信号父进程的pending表只会记录一次17号信号也就只会执行一次wait函数所以有的子进程就会处于僵尸状态那我们该如何做 有人可能觉得用循环就行如下 #include iostream #include unistd.h #include signal.h #include sys/wait.h #include sys/types.hvoid handler(int signo) {std::cout get signo sign std::endl;pid_t id;while ((id wait(nullptr))){std::cout child pid: id exit std::endl;if (id 0)break;} }int main() {signal(SIGCHLD, handler);for (int i 0; i 5; i){pid_t id fork();if (id 0){std::cout child is running std::endl;sleep(5);exit(1);}}while(1) sleep(1);return 0; }事实证明也确实行但是这是所有子进程都结束的情况如果有一部分子进程终止另一部分子进程一直在运行那么我们就无法跳出循环因为wait是阻塞等待它可以去查看是否有进程还没终止这样就会一直在阻塞。 所以用非阻塞等待才是最恰当的如下 void handler(int signo) {std::cout get signo sign std::endl;pid_t id;while ((id waitpid(-1, nullptr, WNOHANG)) 0){std::cout child pid: id exit std::endl;} } 当然如果不关心子进程的返回信息也可以直接忽略该信号子进程会自动被清理。如下 #include iostream #include unistd.h #include signal.h #include sys/wait.h #include sys/types.h int main() {signal(SIGCHLD, SIG_IGN);for (int i 0; i 5; i){pid_t id fork();if (id 0){std::cout child is running std::endl;sleep(5);exit(1);}}while(1) sleep(1);return 0; }
http://www.zqtcl.cn/news/211023/

相关文章:

  • 国际空间站vs中国空间站做网站在哪里接活
  • 怎样宣传网站营销外包公司
  • 工程网站模板制作教程具有价值的专业网站建设平台
  • 用wex5可以做网站吗邯郸seo快速排名
  • 高端品牌网站建设兴田德润可信赖网络运营方案怎么写
  • 新公司网站建设合肥关键词排名优化
  • 网站排名优化+o+m西安网络推广平台公司
  • 找网站建设公司需要注意什么常州网站建设公司好么
  • 不备案的网站很慢网站双线主机优势
  • 南京电子商务网站建设23个营销专业术语
  • 建设银行官网官方网站学习网页制作的网站
  • 开发网站需要什么硬件今年最流行的装修风格
  • 门户网站建设中标结果百度资讯指数
  • 定制企业网站开发公司网站建设的6个基本步骤
  • 网站建设与维护案列网站作品怎么做
  • 茂名放心营销网站开发seo收费
  • 旅游网站品牌建设本地使用宝塔安装wordpress
  • 专门做外链的网站制作论坛类网站模板免费下载
  • 靖江建设行业协会网站投资做网站
  • 做网站视频背景潍坊网站制作建设
  • 深圳市官网网站建设哪家好百度抓取网站登录
  • 免费做cpa单页网站友情链接买卖代理
  • 免费网站建站排名中国最大的软件公司
  • 码云pages做静态网站广西建设培训网
  • 建设网站需要花钱吗网站seo方案策划书
  • 德阳网站怎么做seo陈木胜个人资料
  • 电子规划书商务网站建设wordpress主机推荐
  • wordpress设置多站点html5开发手机app
  • 移动互联和网站开发哪个好做推广便宜的网站有哪些
  • 极速网站建设定制价格微信公众号运营助手