【Linux】进程的生命之旅——诞生、消逝与守候(fork/exit/wait)
🎬 个人主页:谁在夜里看海.
📖 个人专栏:《C++系列》《Linux系列》《算法系列》
⛰️ 一念既出,万山无阻
目录
📖一、进程创建
1.fork函数
📚高层封装特性
📚fork返回值
2.写时拷贝
3.调用失败
📚资源耗尽
📚进程数限制
📚内核限制
📖二、进程终止
1.退出场景
2.status退出码
3.退出方法
📚exit函数
📚_exit函数
📚main函数返回
📖三、进程等待
1.wait方法
📚语法
📚总结
2.waitpid方法
📚语法
📚总结
📖一、进程创建
1.fork函数
操作系统中进程的创建通常是通过系统调用实现的,在Linux中是通过fork(),它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。
#include<unistd.h> // 使用需包含头文件unistd.h
pid_t pid = fork(); // 在子进程中返回0,父进程中返回子进程pid,出错返回-1
fork是操作系统提供的一种高层封装,它抽象了进程创建的复杂过程,fork将底层的一系列操作封装在一个简单的系统调用中,屏蔽了许多复杂的细节:
📚高层封装特性
① 简化进程创建的步骤:
fork的调用接口非常简洁,只需要调用一次,系统会自动创建一个子进程并返回。父进程和子进程共享相同的代码,子进程可以继续从父进程的当前执行点运行。
② 屏蔽底层细节:
底层需要分配新的内存空间、复制父进程的状态、初始化子进程的资源,fork函数将这些细节全部封装起来。
③ 依赖操作系统:
fork的具体实现以来于操作系统内核,它负责管理进程表等关键数据结构,系统调用fork,当控制转移到内核中的fork代码后,内核做:
1. 分配新的内存块和内核数据结构给子进程
2. 将父进程部分数据结构内容拷贝给子进程
3. 添加子进程到系统进程列表当中
4. fork返回,开始调度器调dan'ddandan
当一个进程调用fork之后就会有两个二进制代码相同的进程,并且都能运行到相同的地方,各自开始往下走:
int main() {printf("Before: pid is %d, ppid is %d\n",getpid(),getppid());fork();printf("After: pid is %d, ppid is %d\n",getpid(),getppid());return 0;
}
❓这里为什么只有三行输出,子进程共享父进程的代码,并各自独立执行,应该是打印两次Before才对。分析打印结果,10226应该是父进程,确实打印了Before,而子进程10227没有打印Before,说明子进程并没有执行Before的代码,正如上面所说的,子进程继续从父进程的当前执行点运行,也就是从fork代码处往下执行,而fork之前的不会被执行:
所以,在一个进程调用fork之前,该进程单独执行,调用fork之后,父子两个进程执行流各自执行
📚fork返回值
在子进程中,fork返回0;
在父进程中,frok返回子进程pid。
❓这里提出一个问题,在父进程中fork返回值没有异议,因为fork函数是父进程调用的,自然会有返回值,但是在子进程中fork也有返回值,那么是不是子进程也调用了fork函数呢? 这不肯定,因为一个进程调用fork函数之后会创建出它的子进程,而子进程再调用fork函数再创建。。。这显然不对,所以子进程并没有调用fork函数,但是为什么会有fork函数的返回值呢?
✅上面说到,子进程是从父进程的执行点开始往下执行的,所以对上述问题合理的解释是:父进程创建子进程时的执行点在fork函数调用之后,返回之前,所以子进程往下执行也会有返回值产生
2.写时拷贝
父子进程的代码是共享的,所以它们往后执行相同的操作,那么它们的数据也是共享的吗?的确,在没有进行写入时,父子进程的数据也是共享的,只有当一方尝试对共享数据进行写入时,系统才会拷贝一份数据用于写入,这样既确保了资源的高效利用,又保证了父子进程间的独立性 。
3.调用失败
fork()调用失败通常于系统资源、权限、或操作系统限制有关,下面是常见的原因:
📚资源耗尽
当系统的资源不足时(如内存或进程表项不足),fork()会失败:
内存不足:操作系统需要为每个新进程分配内存,如果系统内存耗尽,
fork()
就会失败。进程表已满:每个进程都有一个进程控制块(PCB),操作系统维护一个进程表。如果系统中运行的进程数量已经达到限制,无法再为新进程分配进程控制块时,
fork()
会失败。堆栈空间不足:如果子进程的堆栈空间无法分配(尤其在某些嵌入式或资源受限的环境中),
fork()
也会失败。
📚进程数限制
大多数操作系统对一个用户或系统总共能创建的进程数有限制。若当前用户或系统已经达到了此限制,调用 fork()
时就会失败。
可以通过 ulimit -u
查看单个用户的最大进程数:
📚内核限制
内核的资源,如文件描述符和信号等,也可能导致 fork()
失败。例如,如果父进程持有太多打开的文件句柄,可能会达到系统文件描述符的限制。
📖二、进程终止
1.退出场景
进城退出场景无非下面三种:
①:代码运行完毕,结果正确
②:代码运行完毕,结果不正确
③:代码异常终止(没有运行完)
第一种情况自然是最好的,但是如果是另外两种情况,我们就需要进行额外处理,但是我们怎么才能知道进程退出是哪种情况呢(什么时候需要处理,什么时候不需要呢)?
这个时候就需要进程退出时,做一些标记(返回退出码),告知操作系统或程序员具体的退出情况
2.status退出码
status状态码用于表示进程的退出状态,提供了进程执行结果的信息,状态码遵循以下约定:
- 0:表示命令成功执行,没有错误发生。
- 非0:表示命令执行失败。具体的非0值表示不同类型的错误,具体含义通常与执行的程序或命令相关。例如:
- 1:一般性错误。
- 2:命令语法错误。
- 126:命令不能执行(权限问题)。
- 127:命令未找到。
- 128:命令因信号导致终止(例如,程序被 kill 命令中断)。
- 130:程序因接收到 Ctrl+C(SIGINT)信号而退出。
status通常被定义成整形,但是并不能当作一般的整形看待,而是要看作成位图:
我们有一个 32 位的 status
,其中高8位用于表示退出状态,低8位用于表示因信号退出的原因。
-
高8位(退出状态)可以有 256 种可能的退出码:
0
:正常退出。1
到127
:表示不同的错误。128
到255
:表示因信号终止,计算方式为128 + 信号编号
。 -
低8位(信号终止标志):
如果进程是由于信号终止的,那么低8位会记录相应的信号编号(例如,
SIGKILL
对应 9,SIGSEGV
对应 11)。如果进程不是由信号终止的,低8位通常为 0。
3.退出方法
进程退出的常见方法有:exit(),_exit()以及main()函数返回,下面依次进行介绍:
📚exit函数
exit(int status):这是进程正常终止的一种方式。调用exit()后,进程会清理其资源(文件描述符、内存等),并将状态码status返回给操作系统。当返回0时表示成功退出,返回非0表示出现错误。
在多线程程序中,exit()
会终止当前进程以及所有线程。
#include <stdio.h>
#include <stdlib.h>int main() {printf("This process will exit normally.\n");exit(0); // 正常退出,状态码为0
}
📚_exit函数
_exit(int status):这个函数与 exit()
很相似,但它不会执行标准库的清理操作(如缓冲区刷新等),直接终止进程。来看下面这段代码:
int main()
{printf("this is a process, pid is %d, ppid is %d",getpid(),getppid());exit(0);
}
调用exit时,正常打印;
int main()
{printf("this is a process, pid is %d, ppid is %d",getpid(),getppid());_exit(0);
}
❓调用_exit时,没有正常打印,这是为什么呢?
✅printf输出时如果没有加上\n,此时输出的内容会存在标准输出缓冲区中,并不会立刻显示在终端,而调用_exit函数时,由于它不会执行标准库的清理操作,所以缓冲区的内容就不会显示在终端
exit函数最后其实会调用_exit函数,只不过在调用之前,多做了如清理缓冲区的操作:
📚main函数返回
return
:在 main
函数中使用时,程序会结束并返回指定的退出状态码(通常为 0
表示成功,非 0
表示错误)。return
结束当前函数的执行,但如果在 main
函数中调用,它会导致程序退出。
return返回和exit调用的效果是一样的,其实他们本质上是等价的:return 0 等价于 exit(0)
只不过在main函数中用return返回作为程序终止的标志更符合函数的语义,可读性更强。
📖三、进程等待
之前的博客讲过,子进程退出,如果父进程不做任何处理,就会引发内存泄露(进程表等信息不会被清理),产生僵尸进程。 博客链接在此:详解僵尸进程于孤儿进程
那么避免僵尸进程的办法就是进程等待,父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息。
1.wait方法
wait()是一个比较简化的系统调用,用于让父进程等待任意一个子进程的终止。wait()函数会阻塞父进程,直到有子进程终止,并且返回一个子进程的PID。
📚语法
#include <sys/wait.h>
pid_t wait(int *status);
status:用于返回子进程的退出状态。
返回值:如果调用成功,返回子进程的PID;如果没有则返回-1。
📚总结
1. 父进程调用wait()时会阻塞,直到有子进程结束并回收它的状态;
2. 如果有多个子进程退出,wait()返回任意一个子进程的PID;
3. 如果没有子进程,wait()会返回-1。
2.waitpid方法
waitpid()
是 wait()
的更为灵活和可控制的版本,允许父进程等待特定的子进程结束,或者通过指定参数进行更精细的控制。
📚语法
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);
pid
:指定需要等待的子进程的 PID。可以取以下几种值:pid > 0
:等待指定 PID 的子进程。pid == -1
:等待同组进程中的任意子进程。
status
:与wait()
相同,保存子进程的退出状态。options
:控制行为的标志,常用的选项有:WNOHANG
:非阻塞模式,如果没有子进程退出,立即返回,而不是阻塞。WUNTRACED
:如果子进程已经停止(但没有退出),也返回。
- 返回值:
- 返回子进程的 PID,如果没有子进程或者发生错误,返回
-1
。 - 如果
status
中的退出状态有特殊状态(如退出信号),需要使用宏来解析。
- 返回子进程的 PID,如果没有子进程或者发生错误,返回
int main()
{pid_t pid = fork();if(pid == 0){// 子进程printf("this is child process,pid is %d,ppid is %d\n",getpid(),getppid());exit(20);}// 父进程printf("this is father process,pid is %d\n",getpid());int status;pid_t child_pid = waitpid(-1,&status,WNOHANG);printf("child process has exited,code is %d,pid is %d\n",WEXITSTATUS(status),child_pid);exit(0);
}
父进程调用 wait()
或 waitpid()
时,它会传递一个指向 status
变量的指针,用于写入子进程的退出状态,所以我们需要在外部定义一个status变量,并通过取地址的方式传入函数内部。
❓定义成其他变量名可以吗:完全可以!
✅变量名只是内存的一个标识符,是用户自定义的,wait()
或 waitpid()
只关心的是传递给它的地址,而不是变量的名字,只不过定义成status
这样代码更加易读。
❓定义成其他类型可以吗:不可以!
✅status
参数必须是一个指向 int
类型的指针。如果传递其他类型(例如 float*
或 char*
),程序可能会产生编译错误,这是因为 wait()
和 waitpid()
会在 status
指向的内存中写入整数值,用来存储子进程的退出状态。如果指针指向的类型不匹配,内存解释将出错。
上述代码中由于waitpid内部设置为WNOHANG模式,没有子进程返回时直接退出,不阻塞:
需要sleep(1)等待子进程退出后,waitpid才能接收到退出信息:
// 父进程printf("this is father process,pid is %d\n",getpid());int status;sleep(1);pid_t child_pid = waitpid(-1,&status,WNOHANG);printf("child process has exited,code is %d,pid is %d\n",WEXITSTATUS(status),child_pid);exit(0);
其中WEXITSTATUS是一个宏函数,用于解码退出状态,因为上面讲过,32位status的高8位存储退出状态,所以不能直接引用status查看,而要用一个宏函数进行解码。
📚总结
特性 | wait() | waitpid() |
等待目标 | 等待任意子进程的结束 | 可以指定特定的子进程(通过 pid 参数) |
阻塞与非阻塞 | 总是阻塞,直到至少有一个子进程结束 | 可以通过 WNOHANG 使其非阻塞 |
灵活性 | 较少灵活性,只能等待任何一个子进程 | 更灵活,可以等待指定的子进程或进程组 |
选项 | 没有额外选项 | 支持更多控制选项,如 WNOHANG |
返回值 | 返回一个子进程的 PID | 返回指定子进程的 PID,或者 -1 错误 |
错误处理 | 如果没有子进程,返回 -1 | 如果没有子进程,返回 -1 |
以上就是【进程的生命之旅——诞生、消逝与守候】的全部内容,欢迎指正~
码文不易,还请多多关注支持,这是我持续创作的最大动力!
相关文章:
【Linux】进程的生命之旅——诞生、消逝与守候(fork/exit/wait)
🎬 个人主页:谁在夜里看海. 📖 个人专栏:《C系列》《Linux系列》《算法系列》 ⛰️ 一念既出,万山无阻 目录 📖一、进程创建 1.fork函数 📚高层封装特性 📚fork返回值 2.写时拷…...
使用vcpkg自动链接tinyxml2时莫名链接其他库(例如boost)
使用vcpkg自动链接tinyxml2时莫名链接其他库(例如boost) vcpkg的自动链接功能非常方便,但在某些情况下会出现过度链接的问题。 链接错误症状 以tinyxml2为例,程序中调用tinyxml2的函数后,若vcpkg中同时存在opencv和…...
【去毛刺】OpenCV图像处理基础:腐蚀与膨胀操作入门
在数字图像处理中,形态学操作是一种常用的技术,用于提取图像中的特定形状或特征。其中,腐蚀(Erosion)和膨胀(Dilation)是两种基本的形态学运算。本文将通过一个简单的例子来演示如何使用Python中…...
道可云人工智能元宇宙每日资讯|第三届京西地区发展论坛成功召开
道可云元宇宙每日简报(2024年11月27日)讯,今日元宇宙新鲜事有: 工信部等十二部门印发《5G规模化应用“扬帆”行动升级方案》 11月25日,工业和信息化部等十二部门印发《5G规模化应用“扬帆”行动升级方案》。《方案》…...
若依框架部署在网站一个子目录下(/admin)问题(
部署在子目录下首先修改vue.config.js文件: 问题一:登陆之后跳转到了404页面问题,解决办法如下: src/router/index.js 把404页面直接变成了首页(大佬有啥优雅的解决办法求告知) 问题二:退出登录…...
【ue5】UE5运行时下载视频/UE5 runtime download video(MP4)
插件还是老朋友。 节点的content type要打对。 (参照表:MIME 类型(MIME Type)完整对照表 - 免费在线工具) 结果展示:...
对比C++,Rust在内存安全上做的努力
简介 近年来,越来越多的组织表示,如果新项目在技术选型时需要使用系统级开发语言,那么不要选择使用C/C这种内存不安全的系统语言,推荐使用内存安全的Rust作为替代。 谷歌也声称,Android 的安全漏洞,从 20…...
如何利用 Qt 的模块化架构组织大型项目
目录 1. 大型项目的架构设计 1.1 分层架构 1.2 事件驱动与异步架构 2. 模块划分与职责分离 2.1 功能模块划分 2.2 模块之间的依赖管理 3. 跨平台开发与模块复用 在大型软件项目中,随着代码量的增加和功能的扩展,项目的复杂度会显著提升。没有良好…...
探索Python词云库WordCloud的奥秘
文章目录 探索Python词云库WordCloud的奥秘1. 背景介绍:为何选择WordCloud?2. WordCloud库简介3. 安装WordCloud库4. 简单函数使用方法5. 应用场景示例6. 常见Bug及解决方案7. 总结 探索Python词云库WordCloud的奥秘 1. 背景介绍:为何选择Wo…...
MySQL根据idb文件恢复数据
首先得有对应表的idb文件以及建表语句 1.首先在新数据库建表 CREATE TABLE sys_menu (id bigint(20) NOT NULL,parent_id bigint(20) NULL DEFAULT NULL,name varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,type int(11) NULL DEFAULT …...
hadoop-mapreduce词频统计
一、Map Reduce主要阶段 二、词频统计示例 0.MapReduce 词频统计(Word Count)示例图 1. Input 阶段(输入阶段) 输入数据是一段文本,如下: Hadoop is a big data framework. Hadoop can store vast data. Hadoop processes big …...
精心修炼Java并发编程(JUC)-volatile与synchronized关键字
volatile volatile 是 JVM 提供的 最轻量级的同步机制,中文意思是不稳定的,易变的,用 volatile 修饰变量是为了保证变量在多线程中的可见性,它表达的含义是:告诉编译器,对这个变量的读写,需要基…...
【ROS2】ROS2 与 ROS1 编码方式对比(Python实现)
目录 一、初始化和关闭节点二、发布者三、订阅者四、服务端五、客户端六、参数管理七、日志记录八、生命周期管理 ROS2 在 Python 编程中引入了一些新的概念和 API,这些变化使得代码更加模块化和易于维护。特别是 rclpy 库提供了更丰富的功能和更好的错误处理机制&a…...
ElasticSearch的下载和基本使用(通过apifox)
1.概述 一个开源的高扩展的分布式全文检索引擎,近乎实时的存储,检索数据 2.安装路径 Elasticsearch 7.8.0 | Elastic 安装后启动elasticsearch-7.8.0\bin里的elasticsearch.bat文件, 启动后就可以访问本地的es库http://localhost:9200/ …...
城市轨道交通运营控制指挥中心设计方案
为某城市轨道交通运营控制指挥中心(OCC)的设计提供方案时,我们需要考虑到多个方面的需求,包括系统架构、设备选择、功能实现、数据流与监控、通信管理等。以下是一个综合性的设计方案,涉及系统硬件和软件的选择、布局规划、安全性等方面,以确保指挥中心的高效运作、实时监…...
多目标优化算法:多目标河马优化算法(MOHOA)求解ZDT1、ZDT2、ZDT3、ZDT4、ZDT6,提供完整MATLAB代码
一、河马优化算法 河马优化算法(Hippopotamus optimization algorithm,HO)由Amiri等人于2024年提出的一种模拟自然界中河马觅食行为的新型群体智能优化算法。该算法由Mohammad Hussein Amiri等人于2024年2月发表在Nature旗下子刊《Scientifi…...
线程与进程的个人理解
进程(Process): 一个程序在执行时,操作系统为其分配的资源(如内存、CPU 时间等)构成了一个进程。每个进程都有自己的独立的地址空间、堆栈和局部变量,它们之间不共享内存(除非通过特…...
vscode的项目给gitlab上传
目录 一.创建gitlab帐号 二.在gitlab创建项目仓库 三.Windows电脑安装Git 四.vscode项目git上传 一.创建gitlab帐号 二.在gitlab创建项目仓库 图来自:Git-Gitlab中如何创建项目、创建Repository、以及如何删除项目_gitlab新建项目-CSDN博客) 三.Windows电脑安…...
企业微信定位打卡
废话少说:定位修改软件链接奉上 一、定位打卡原理 GPS定位:企业微信可以利用手机的GPS功能进行定位,这是一种基于卫星的定位技术,能够提供相对精确的位置信息,通常精确度在20米以内。这种方式耗电较大,且在…...
libaom 源码分析:码率控制介绍
码率控制 命令行码率控制选项:可以看到码率控制包括丢帧、resize、超分、码控模式、目标码率、目标上限下限(类似 x264、x265 中的 VBV)、码控偏置、GOP 码率等。Rate Control Options:--drop-frame=<arg> Temporal resampling threshold (buf %)--resize-mo…...
RK3568平台开发系列讲解(DMA篇)DMA engine使用
🚀返回专栏总目录 文章目录 一、申请DMA channel二、配置DMA channel的参数三、获取传输描述(tx descriptor)四、启动传输沉淀、分享、成长,让自己和他人都能有所收获!😄 📢DMA子系统下有一个帮助测试的测试驱动(drivers/dma/dmatest.c), 从这个测试驱动入手我们了解…...
C++中的函数对象
C 中函数对象的定义和特点 定义:函数对象(Function Object)也叫仿函数(Functor),是一个类,这个类重载了函数调用运算符()。当创建这个类的对象后,可以像使用函数一样使用这个对象&am…...
Linux指标之平均负载(The Average load of Linux Metrics)
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…...
盛最多水的容器
本节将数组与坐标轴共同组成一个容器,通过改变容器的两个端点使容器装的水最多,容器两个端点不断移动可以通过左右指针算法解决. 问题描述: 给定两个非负整数k1,k2...km每个数代表坐标中的一个点(i,ki).在坐标内绘制m条垂线,垂直线i的两个端点分别为(i,k1)和(i,0)找出其中的两…...
光伏功率预测!Transformer-LSTM、Transformer、CNN-LSTM、LSTM、CNN五模型时序预测
目录 预测效果基本介绍程序设计参考资料 预测效果 基本介绍 Transformer-LSTM、Transformer、CNN-LSTM、LSTM、CNN五模型多变量时序光伏功率预测 (Matlab2023b 多输入单输出) 1.程序已经调试好,替换数据集后,仅运行一个main即可运行,数据格式…...
java全栈day10--后端Web基础(基础知识)
引言:只要能通过浏览器访问的网站全是B/S架构,其中最常用的服务器就是Tomcat 在浏览器与服务器交互的时候采用的协议是HTTP协议 一、Tomcat服务器 1.1介绍 官网地址:Apache Tomcat - Welcome! 1.2基本使用(网上有安装教程,建议…...
使用爬虫时,如何确保数据的准确性?
在数字化时代,数据的准确性对于决策和分析至关重要。本文将探讨如何在使用Python爬虫时确保数据的准确性,并提供代码示例。 1. 数据清洗 数据清洗是确保数据准确性的首要步骤。在爬取数据后,需要对数据进行清洗,去除重复、无效和…...
Burp入门(4)-扫描功能介绍
声明:学习视频来自b站up主 泷羽sec,如涉及侵权马上删除文章 感谢泷羽sec 团队的教学 视频地址:burp功能介绍(1)_哔哩哔哩_bilibili 本文介绍burp的主动扫描和被动扫描功能。 一、主动扫描 工作原理: 主动…...
Tourtally:颠覆传统的AI智能旅行规划革命
# Tourtally:颠覆传统的AI智能旅行规划革命 在快速变化的旅行科技世界里,一个划时代的平台正在重新定义我们探索世界的方式。让我们一起认识 Tourtally,这个由人工智能驱动的旅行规划助手,正在彻底改变旅行体验。 ## 旅行规划的…...
chrome允许http网站打开摄像头和麦克风
第一步 chrome://flags/#unsafely-treat-insecure-origin-as-secure 第二步 填入网址,点击启用 第三步 重启 Chrome:设置完成后,点击页面底部的 “Relaunch” 按钮,重新启动 Chrome 浏览器,使更改生效。...
网站备案需要钱吗/一诺网络推广公司
在element-ui的运用中,涉及到了select下拉列表。项目中需要将select的默认值给展示出来,需要设置默认值 在官方给出的文档时候 <script>export default {data() {return {options: [{value: 选项1,label: 黄金糕}, {value: 选项2,label: 双皮奶},…...
大学毕业做网站插画师好吗/全国各城市疫情高峰感染进度
背景 我试图用python编写一个基本的字母游戏。在游戏中,计算机管理员从可能的单词列表中选出一个单词。每个玩家(计算机人工智能和人类)都会显示一系列空格,每个字母对应一个单词。然后,每个玩家猜测一个字母和一个位置…...
网站页面制作视频/互联网广告营销
Amazon DynamoDB 全局表 为部署多区域、多主机数据库提供了完全托管的解决方案,而不必构建和维护您自己的复制解决方案。在创建全局表时,指定要在其中提供表的 AWS 区域。DynamoDB 执行在这些区域中创建相同的表并将持续数据更改传播到所有这些表所必需的…...
5 还有网站的域/手机系统优化软件
软件开发的发展历程 软件是用计算机语言编写的。计算机语言的发展经历了从机器语言、汇编语言到高级语言的历程。 HTML(Hyper Text Markup Language 超文本标识语言) 是一种用来制作超文本文档的简单标记语言。 用HTML编写的超文本文件称为HTML文件&a…...
沙井网站建设公司/广告公司联系方式
不要太直接。比如赞美他人穿的衣服好看不能单独夸衣服,要说衣服穿在你身上,体现了独特的美。要有差异化。尤其是一个经常收到赞美的人,你要夸的话一定不能简单陈述事实,要找差异的地方。要适度。再好的话说多了也是废话࿰…...
网站设计风格升级/社区建站网站系统
内存对齐 现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但是各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。其他平台可能没有这种情况&am…...