在计算机系统中,异常有中断、陷阱、故障和终止四种类别,具体的内容我们不在此处展开,但是无论如何,当异常出现时,计算机系统都会对其进行处理, 其大致流程如下图所示:
作为初学者,我们平常练习时所编写的代码其实是极为朴素简单的,在代码中加入进行错误处理也不会导致程序难以阅读。但实际上,我们日常生活、工作生产中使用的大部分程序其实都是非常复杂的。
当Unix系统级函数遇到错误时,它们通常会返回-1,并设置全局整数变量errno来表示什么出错了。程序员应该总是检查错误,但是不幸的是,许多人都忽略了错误检查,因为它使代码变得臃肿,而且难以读懂。比如,下面是我们调用Unix fork函数时会如何检查错误:
if ((pid = fork()) <0){
fprintf(stderr, "fork error: %s\n",strerror(errno)) ;
exit(0) ;
}
strerror函数返回一个文本串,描述了和某个errno值相关联的错误。通过定义下面的错误报告函数,我们能够在某种程度上简化这个代码:
void unix_error(char *msg)/ * Unix-style error */{
fprintf(stderr,"%s: %s\n", msg,strerror(errno));
exit(0);
}
给定这个函数,我们对fork的调用从4行缩减到2行:
if ((pid - fork()) < 0)
unix_error ( "fork error");
通过使用错误处理包装函数,我们可以更进一步地简化代码。对于一个给定的基本函数foo,我们定义一个具有相同参数的包装函数Foo,但是第一个字母大写了。包装函数调用基本函数,检查错误,如果有任何问题就终止。比如,下面是fork函数的错误处理包装函数:
pid_t Fork(void){
pid_t pid;
if ((pid = fork()) < 0)
unix_error("Fork error");
return pid;
}
给定这个包装函数,我们对fork的调用就缩减为1行:
pid = Fork();
每个进程都有一个唯一的正数(非零)进程ID(PID)。getpid函数返回调用进程的PID。getppid函数返回它的父进程的PID(创建调用进程的进程)。
#include <sys/types.h>
#include <unistd.h> pid_t getpid(void); pid_t getppid(void);
//返回:调用者或其父进程的PID。
getpid和getppid函数返回一个类型为pid_t的整数值,在Linux系统上它在types.h中被定义为int。
从程序员的角度,我们可以认为进程总是处于下面三种状态之一:
exit函数以status退出状态来终止进程(另一种设置退出状态的方法是从主程序中返回一个整数值)。
父进程通过调用fork函数创建一个新的运行的子进程。
#include <sys/types.h>#include <unistd.h>pid_t fork(void);
*返回:子进程返回0,父进程返回子进程的PID,如果出错,则为-1。
新创建的子进程几乎但不完全与父进程相同。子进程得到与父进程用户级虚拟地址空间木同的(但是独立的)一份副本,这就意味着当父进程调用fork时,子进程可以读写进程中打开的任何文件。父进程和新创建的子进程之间最大的区别在于它们有不同的 PID。
fork函数它只被调用一次,却会返回两次:一次是在调用进程(父进程)中,一次是在新创建的子进程中。在父进程中,fork返回子进程的PID。在子进程中,fork返回0。因为子进程的PID总是为非零,返回值就提供一个明确的方法来分辨程序是在父进程还是在子进程中执行。
下面展示了一个使用fork创建子进程的父进程的示例。当fork调用在第6行返回时,在父进程和子进程中x的值都为1。子进程在第8行加一并输出它的x的副本。相似地,父进程在第13行减一并输出它的x的副本。
int main(){
pid_t pid;
int x = 1;
pid = Fork();
if (pid == 0) {
/*Child */
printf ( "child : x=%d\n",++x);
exit(0);
}
/*Parent*/
printf ( "parent: x=%d\n",--x);
exit(O);
}
程序运行得到如下结果:
linux> ./fork
parent : x=0
child : x=2
通过这个例子,我们可以直观地体会到 fork() 的几个特点:
linux> ./fork
child : x=2
parent : x=0
int main(){
Fork();
Fork();
printf ("hello\n");
exit(0);
}
这次我们在fork后的子进程中又再次进行fork(),这显然是可行的。就像树枝生长会长出新的枝干一样,子进程也可以创建它的子进程。于是我们可以得到这样的结果:
linux> ./fork
hello
hello
hello
hello
也许这样谈论fork非常的抽象,我们不妨画张进程图进行理解:
进程图是直观而形象的,多画进程图显然能够帮助更好地理解fork函数。
当一个进程由于某种原因终止时,内核并不是立即把它从系统中清除。相反,进程被保持在一种已终止的状态中,直到被它的父进程回收(reaped)。当父进程回收已终止的子进程时,内核将子进程的退出状态传递给父进程,然后抛弃已终止的进程,从此时开始,该进程就不存在了。一个终止了但还未被回收的进程称为僵死进程(zombie)。
如果一个父进程终止了,内核会安排init进程成为它的孤儿进程的养父。init进程的PID为1,是在系统启动时由内核创建的,它不会终止,是所有进程的祖先。如果父进程没有回收它的僵死子进程就终止了,那么内核会安排init进程去回收它们。不过,长时间运行的程序,比如shell或者服务器,总是应该回收它们的僵死子进程。即使僵死子进程没有运行,它们仍然消耗系统的内存资源。
一个进程可以通过调用wait或者waitpid函数来等待它的子进程终止或者停止。
#include <sys/types.h>#include <sys/wait.h>
pid_t wait(int *statusp);
//返回:如果成功,则为子进程的PID,如果出错,则为-1.
父进程执行到wait(),就如我们之前提到过的进程的三种状态之一,会被挂起,等待其子进程结束后,自己才结束。
void fork()
{
int child_status;
if (fork() == 0) {
printf("HC: hello from child\n");
exit(0);
} else {
printf("HP: hello from parent\n");
wait(&child_status);
printf("CT: child has terminated\n");
}
printf("Bye\n");
}
运行结果如下:
linux> ./fork
HP: hello from parent
HP: hello from parent
CT: child has terminated
Bye
从输出结果来看,父进程执行到wait时,不再继续进行后面的printf("CT: child has terminated\n");
,而是直接被挂起,等待自己的子进程结束。
流程图大致如下:
· ————>printf———————— ·
| HC |
| v
main————>fork————>printf————>wait·————>printf————>printf
HP CT Bye
实际上,wait是waitpid的延伸与简化,调用wait(&status)
等价于调用waitpid(- 1,&status,0)
。但是篇幅有限,我们在此不进行展开。
至此,我们已经对fork函数和wait函数有了基础的认识。能够记清楚fork中父进程与子进程的相同点和不同点、进程的特性和状态、wait函数的返回值参数等,对之后的学习会很有帮助。希望大家变得更强,也欢迎大家留言讨论,一起进步。
Ubuntu20.04下搭建FTP服务器这是一个新手小白的搭建流程,使用的是Ubuntu20.04桌面版,小伙伴们可以参考一下哦~ 下面就是作者小白经过一番折腾后总结出来的搭建流程啦~一、安装1.下载VSFTPD终端。(ps:如何你在这里没有使用root用户下面的sudo不能少哦,否则会提示权限不够哒~)sudo apt-get install vsftpd二、配置VSFTPD服务器1.修改配置文件可以用你自己喜欢的编辑器进行编辑修改(我用的是gedit编辑器):
在做Android BLE的应用程序时,我们发出广播数据是调用BluetoothLeAdvertiser的startAdvertising方法,如下所示:mBluetoothLeAdvertiser.startAdvertising(advertiseSettings, advertiseData, myAdvertiseCallback);那么我打算写的BLE总结之_1671465600
近段时间,亚马逊官方网站要求所有的销售电子电器的商家提供FCC认证。众所周知,如果商家在亚马逊要求的时间内不能提供FCC认证,那么宝贝就要下架了。怎么在短时间内拿到FCC认证呢?有如下几个解决方案:1,普通的电子产品第一步:提供产品说明书给检测机构第二步:根据产品说明书报价第三步:填写FCC认证申请表(电子档即可)第四步:双方签订合作协议第五步:邮寄样品第六步:收到样品支付款项,安排测试第七步:测试完成出具检测报告第八步:电邮报告2,无线产品FCC-ID解决办法第一步:申请App
欢迎来到9秒:www.9miao.com 关于开发环境的搭建,之前的相关文档已经很详细的说明,对环境的搭建请参考以前的相关文档,如有问题,及时在论坛里提问,会有管理人员快速解答,此文档主要针对eclipse下安卓编译. 首先肯定是把暗黑世界的源码导入eclipse,源码导入进来后,就开始进行编译的准备工作,这里首先需要将Classes里面的.cpp文件和和相关的文件夹路径写
1. Spark Connector 介绍Spark Connector 是一个 Spark 的数据连接器,可以通过该连接器进行外部数据系统的读写操作。Spark Connector 包含两部分,分别是 Reader 和 Writer。DataSource的API的代码位于spark_sql【如spark-sql_2.11-2.4.4.jar】 模块中的org.apache.spark.sql.sources包下,定义了如何从存储系统进行读写的相关 APIAPI分为v1、v2两个版本,v2的代码
项目编号:BS-XX-054运行环境:开发工具:IDEA / ECLIPSE数据库:MYSQL5.7应用服务器:TOMCAT8.5.31开发技术:JSP/SERVLET/Jquery/easyUI本系统基于Javaweb技术开发完成一套商品的进销存管理系统,前端采用jquery+easyUI开发,界面简洁大方,用户体验较好。主要实现完成了货品、供应商、分公司的基本信息管理,以及商品出入库和库存管理,并在系统管理模块中实现了权限和角色的管理及分配操作,可以实现不同的人员分配不同角.
<br />提子上的白粉: 弄点牙膏混在洗提子的水里。(未测试)<br />瓷杯上的茶垢:(有盖的瓷杯)放个柠檬片在水杯里,倒满热水盖上盖浸泡一会儿,在水没变温前,用力摇晃瓷杯(使水在瓷杯里晃来晃去,此时应该有部分茶垢脱落),然后可以那拿个柠檬片把剩下的茶垢擦掉。遗留问题:接近杯子上沿的茶垢不易清楚,不知是水位太低还是什么原因。
String regEx = "(((^([0-9]|[0-5][0-9])(\\,|\\-|\\/){1}([0-9]|[0-5][0-9]) )|^([0-9]|[0-5][0-9]) |^(\\* ))((([0-9]|[0-5][0-9])(\\,|\\-|\\/){1}([0-9]|[0-5][0-9]) )|([0-9]|[0-5][0-9]) |(\\* ))((([0-9]|[01
注,本文转载自:http://www.cnblogs.com/eyu8874521/archive/2012/10/17/2727882.html 开发中很常见的一个问题,项目中的listview不仅仅是简单的文字,常常需要自己定义listview,自己的Adapter去继承BaseAdapter,在adapter中按照需求进行编写,问题就出现了,可能会发生点击每一个item的时候没有
1.打开depends.exe2.将所要查看的库拖入depends中3.图中红色的为需要依赖的库
原文链接:https://www.cnblogs.com/ivictor/p/4654267.html前面刚说了wm_concat函数,没过几天用的时候,就报这个函数找不到了。于是有找到了新的替代函数,listagg,而且用起来更好用了。下面我们来看看其具体用法。用法:对其作用,官方文档的解释如下:For a specified measure, LISTAGG ord...
一、drozer安装包把Drozer工具包drozer-installer-2.3.4.zip解压后看到的目录如下图,其中setup.exe文件是安装在PC机上面的,agent.apk是安装在手机模拟器或者移动手机里面的。二、PC机上面安装在pc机器上面直接点击setup.exe进行傻瓜式安装就行了,程序默认安装在 “C:\drozer”下,安装好后如下图所示:三、安装ag...