add blogs pages
|
|
@ -3,6 +3,7 @@ import BaseLayout from "../layouts/BaseLayout.astro"
|
|||
import Tile from "../layouts/Tile.astro"
|
||||
import MoreTile from "../layouts/MoreTile.astro"
|
||||
const allPosts = await Astro.glob("../pages/posts/*.md")
|
||||
const Blogs = await Astro.glob("../pages/posts/blogs/**/*.md")
|
||||
allPosts.sort(
|
||||
(a, b) =>
|
||||
Date.parse(b.frontmatter.pubDate) - Date.parse(a.frontmatter.pubDate)
|
||||
|
|
@ -84,6 +85,18 @@ allPosts.sort(
|
|||
)
|
||||
})
|
||||
}
|
||||
{
|
||||
Blogs.slice(2, 6).map((post) => {
|
||||
return (
|
||||
<MoreTile
|
||||
title={post.frontmatter.title}
|
||||
href={post.url}
|
||||
date={post.frontmatter.pubDate}
|
||||
tags={post.frontmatter.tags}
|
||||
/>
|
||||
)
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
<div class="view-archive-wrapper">
|
||||
<a
|
||||
|
|
|
|||
|
|
@ -1,48 +0,0 @@
|
|||
---
|
||||
title: 友情链接
|
||||
date: 2019-12-25 14:27:01
|
||||
permalink: /friends
|
||||
article: false
|
||||
sidebar: false
|
||||
author:
|
||||
name: nbtca
|
||||
link: https://github.com/nbtca
|
||||
---
|
||||
|
||||
<!--
|
||||
普通卡片列表容器,可用于友情链接、项目推荐、古诗词展示等。
|
||||
cardList 后面可跟随一个数字表示每行最多显示多少个,选值范围1~4,默认3。在小屏时会根据屏幕宽度减少每行显示数量。
|
||||
-->
|
||||
::: cardList
|
||||
```yaml
|
||||
# - name: 麋鹿鲁哟
|
||||
# desc: 大道至简,知易行难
|
||||
# avatar: ./assets/82707d577b914020b54384a615b0676e/20200122153807.jpg # 可选
|
||||
# link: https://www.cnblogs.com/miluluyo/ # 可选
|
||||
# bgColor: '#CBEAFA' # 可选,默认var(--bodyBg)。颜色值有#号时请添加单引号
|
||||
# textColor: '#6854A1' # 可选,默认var(--textColor)
|
||||
- name: 鲁冠泽的博客
|
||||
link: https://cimoc.cn/
|
||||
avatar: ./assets/82707d577b914020b54384a615b0676e/cimoccn.png
|
||||
descr: Java, Web。
|
||||
- name: 江蕾的博客
|
||||
link: https://www.cnblogs.com/JLay
|
||||
avatar: ./assets/82707d577b914020b54384a615b0676e/DoEH51Nj97Ah64a.png
|
||||
descr: 前端。
|
||||
- name: 黄文轩的博客
|
||||
link: https://www.cnblogs.com/N3ptune
|
||||
avatar: ./assets/82707d577b914020b54384a615b0676e/4J9NfH1UZD3sz5I.png
|
||||
descr: 网安, Linux和C/C++。
|
||||
- name: 陈学书的博客
|
||||
link: https://www.cnblogs.com/Flat-White
|
||||
avatar: ./assets/82707d577b914020b54384a615b0676e/VjBGkQ6c58vH4l9.png
|
||||
descr: Mac, 人工智能, Web和流水账。
|
||||
- name: 王纯的博客
|
||||
link: https://chundot.org
|
||||
avatar: ./assets/82707d577b914020b54384a615b0676e/avatar.png
|
||||
descr: Web。
|
||||
- name: 章晟玮的博客
|
||||
link: https://bcscb.xyz/
|
||||
avatar: https://cdn.bcscb.xyz/img/1.jpg
|
||||
descr: 算法记录。
|
||||
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
---
|
||||
title: 关于
|
||||
date: 2023-12-12 14:27:01
|
||||
permalink: /about/
|
||||
sidebar: false
|
||||
article: false
|
||||
author:
|
||||
name: nbtca
|
||||
link: https://github.com/nbtca
|
||||
---
|
||||
|
||||
## 📚Blog
|
||||
这是一个兼具博客文章、知识管理、文档查找的计算机协会网站。欢迎到[GitHub](https://github.com/xugaoyi/vuepress-theme-vdoing)点个Star( •̀ ω •́ )✧
|
||||
|
||||
:::tip
|
||||
文章内容仅是计协的一些小技术,如有误还请指正。
|
||||
:::
|
||||
|
||||
[更新日志](https://github.com/nbtca/blogs/commits/main)
|
||||
|
||||
## 🎨Theme
|
||||
本站主题是 [`Vdoing`](https://github.com/xugaoyi/vuepress-theme-vdoing),这是一款简洁高效的VuePress 知识管理&博客 主题。旨在轻松打造一个`结构化`与`碎片化`并存的个人在线知识库&博客,让你的知识海洋像一本本书一样清晰易读。配合多维索引,让每一个知识点都可以快速定位! [Github地址](https://github.com/xugaoyi/vuepress-theme-vdoing)
|
||||
|
||||
## 🐼Our
|
||||
计算机协会成员
|
||||
|
||||
### 技能
|
||||
* 熟悉 JavaScript、HTML、CSS、Vue、React 的拼写
|
||||
* 了解 Linux、windows、macOS 的开关机方式
|
||||
* 精通 Git 的 pull 和 push,并注册了 GitHub 帐号刷了一些 star
|
||||
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 263 KiB After Width: | Height: | Size: 263 KiB |
35
src/pages/posts/blogs/关于/关于.md
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
---
|
||||
layout: "../../../../layouts/MarkdownPost.astro"
|
||||
title: 关于
|
||||
pubDate: 2023-12-12 14:27:01
|
||||
permalink: /about/
|
||||
sidebar: false
|
||||
article: false
|
||||
author:
|
||||
name: nbtca
|
||||
link: https://github.com/nbtca
|
||||
---
|
||||
|
||||
## 📚Blog
|
||||
|
||||
这是一个兼具博客文章、知识管理、文档查找的计算机协会网站。欢迎到[GitHub](https://github.com/xugaoyi/vuepress-theme-vdoing)点个 Star( •̀ ω •́ )✧
|
||||
|
||||
:::tip
|
||||
文章内容仅是计协的一些小技术,如有误还请指正。
|
||||
:::
|
||||
|
||||
[更新日志](https://github.com/nbtca/blogs/commits/main)
|
||||
|
||||
## 🎨Theme
|
||||
|
||||
本站主题是 [`Vdoing`](https://github.com/xugaoyi/vuepress-theme-vdoing),这是一款简洁高效的 VuePress 知识管理&博客 主题。旨在轻松打造一个`结构化`与`碎片化`并存的个人在线知识库&博客,让你的知识海洋像一本本书一样清晰易读。配合多维索引,让每一个知识点都可以快速定位! [Github 地址](https://github.com/xugaoyi/vuepress-theme-vdoing)
|
||||
|
||||
## 🐼Our
|
||||
|
||||
计算机协会成员
|
||||
|
||||
### 技能
|
||||
|
||||
- 熟悉 JavaScript、HTML、CSS、Vue、React 的拼写
|
||||
- 了解 Linux、windows、macOS 的开关机方式
|
||||
- 精通 Git 的 pull 和 push,并注册了 GitHub 帐号刷了一些 star
|
||||
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 358 KiB After Width: | Height: | Size: 358 KiB |
|
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 71 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 132 KiB After Width: | Height: | Size: 132 KiB |
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.1 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 89 KiB After Width: | Height: | Size: 89 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 582 KiB After Width: | Height: | Size: 582 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 330 KiB After Width: | Height: | Size: 330 KiB |
|
Before Width: | Height: | Size: 268 KiB After Width: | Height: | Size: 268 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 212 KiB After Width: | Height: | Size: 212 KiB |
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 139 KiB After Width: | Height: | Size: 139 KiB |
|
Before Width: | Height: | Size: 138 KiB After Width: | Height: | Size: 138 KiB |
|
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 300 KiB After Width: | Height: | Size: 300 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 144 KiB After Width: | Height: | Size: 144 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 109 KiB After Width: | Height: | Size: 109 KiB |
|
Before Width: | Height: | Size: 118 KiB After Width: | Height: | Size: 118 KiB |
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
layout: "../../../../../layouts/MarkdownPost.astro"
|
||||
title: 从汇编角度看C语言 - 1
|
||||
date: 2022-03-27 21:38:45
|
||||
pubDate: 2022-03-27 21:38:45
|
||||
categories:
|
||||
- 技术
|
||||
- C语言
|
||||
|
|
@ -15,7 +16,7 @@ tags:
|
|||
-
|
||||
---
|
||||
|
||||
# 从汇编角度看C语言 - 1
|
||||
# 从汇编角度看 C 语言 - 1
|
||||
|
||||
## 写在前面
|
||||
|
||||
|
|
@ -23,13 +24,13 @@ tags:
|
|||
|
||||
- 系统平台: Windows 10;
|
||||
- 调试工具: OllyDBG (吾爱破解专版) ;
|
||||
- 开发工具: Visual Studio 2008 Debug模式;
|
||||
- 基础要求: 了解C语言和汇编语言;
|
||||
- 大致内容: 简述了一些汇编语言与C语言的关系,方便初学者更好的认识程序的底层运行机制。
|
||||
- 开发工具: Visual Studio 2008 Debug 模式;
|
||||
- 基础要求: 了解 C 语言和汇编语言;
|
||||
- 大致内容: 简述了一些汇编语言与 C 语言的关系,方便初学者更好的认识程序的底层运行机制。
|
||||
|
||||
## 开始
|
||||
|
||||
用VS2008编译如下代码:
|
||||
用 VS2008 编译如下代码:
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
|
|
@ -41,24 +42,24 @@ int main(int argc,char* argv[])
|
|||
}
|
||||
```
|
||||
|
||||
将生成的**可执行文件**拖入**OllyDBG**。
|
||||
将生成的**可执行文件**拖入**OllyDBG**。
|
||||
|
||||
## 如何寻找C程序入口?
|
||||
## 如何寻找 C 程序入口?
|
||||
|
||||
明确两点:
|
||||
|
||||
1. mainCRTStartup和wmainCRTStartup是控制台环境下多字节编码和Unicode编码的启动函数;
|
||||
2. WinMainCRTStartup和wWinMainCRTStartup是windows环境下多字节编码和Unicode编码的启动函数。
|
||||
1. mainCRTStartup 和 wmainCRTStartup 是控制台环境下多字节编码和 Unicode 编码的启动函数;
|
||||
2. WinMainCRTStartup 和 wWinMainCRTStartup 是 windows 环境下多字节编码和 Unicode 编码的启动函数。
|
||||
|
||||
mainCRTStartup做了哪些事?
|
||||
mainCRTStartup 做了哪些事?
|
||||
|
||||
如何通过mainCRTStartup来找到main函数入口?
|
||||
如何通过 mainCRTStartup 来找到 main 函数入口?
|
||||
|
||||
以上述程序为例,寻找其`main`函数入口。
|
||||
|
||||

|
||||
|
||||
初步调试文件,可以发现许多`jmp`指令,这是编译器生成的启动代码,往下按F8跟随跳转。
|
||||
初步调试文件,可以发现许多`jmp`指令,这是编译器生成的启动代码,往下按 F8 跟随跳转。
|
||||
|
||||

|
||||
|
||||
|
|
@ -68,17 +69,17 @@ mainCRTStartup做了哪些事?
|
|||
|
||||

|
||||
|
||||
发现再`call hello.00241140`后控制台打出Hello World,因此在此处设下断点。
|
||||
发现再`call hello.00241140`后控制台打出 Hello World,因此在此处设下断点。
|
||||
|
||||
按下`Ctrl+F2`后重新启动程序,按下`F9`运行到该断点,按下`F7`单步进入。
|
||||
|
||||

|
||||
|
||||
按几次F8后看到如上界面,可以看到Hello World字符串,程序的开头即初始化栈帧操作,显然已经成功来到了main函数中。
|
||||
按几次 F8 后看到如上界面,可以看到 Hello World 字符串,程序的开头即初始化栈帧操作,显然已经成功来到了 main 函数中。
|
||||
|
||||

|
||||
|
||||
查看如上高亮的指令,该指令将“Hello World”字符串的首地址压入栈中,而后调用printf,将字符串打印在控制台上。
|
||||
查看如上高亮的指令,该指令将“Hello World”字符串的首地址压入栈中,而后调用 printf,将字符串打印在控制台上。
|
||||
|
||||

|
||||
|
||||
|
|
@ -86,25 +87,25 @@ mainCRTStartup做了哪些事?
|
|||
|
||||
## 函数
|
||||
|
||||
C语言程序是由具有不同功能的函数组成的,因此在逆向分析中应将重点放在函数的识别及参数的传递上,这样做可以将注意力集中在某一段代码上。
|
||||
C 语言程序是由具有不同功能的函数组成的,因此在逆向分析中应将重点放在函数的识别及参数的传递上,这样做可以将注意力集中在某一段代码上。
|
||||
|
||||
### 函数的识别
|
||||
|
||||
下面讨论**函数的识别**:
|
||||
|
||||
调用函数的代码保存了一个返回地址,该地址会与参数一起传递给被调用的函数。绝大多数情况下编译器都使用call和ret指令来调用函数和返回调用位置。
|
||||
调用函数的代码保存了一个返回地址,该地址会与参数一起传递给被调用的函数。绝大多数情况下编译器都使用 call 和 ret 指令来调用函数和返回调用位置。
|
||||
|
||||
call指令与跳转指令功能类似,但call指令保存返回信息,这里的返回信息实际上主要就是返回地址。
|
||||
call 指令与跳转指令功能类似,但 call 指令保存返回信息,这里的返回信息实际上主要就是返回地址。
|
||||
|
||||
call指令执行时会将其之后的指令地址压入栈的顶部,当遇到ret指令时返回这个地址。
|
||||
call 指令执行时会将其之后的指令地址压入栈的顶部,当遇到 ret 指令时返回这个地址。
|
||||
|
||||
也就是说,call指令给出的地址就是被调用函数的起始地址,ret指令用于结束函数的执行。
|
||||
也就是说,call 指令给出的地址就是被调用函数的起始地址,ret 指令用于结束函数的执行。
|
||||
|
||||
因此可以通过定位call指令或ret指令来识别函数,call的操作数就是所调用函数的首地址。
|
||||
因此可以通过定位 call 指令或 ret 指令来识别函数,call 的操作数就是所调用函数的首地址。
|
||||
|
||||
话不多说,看一个例子。
|
||||
|
||||
用vs2008编译如下代码,使用OllyDBG进行调试:
|
||||
用 vs2008 编译如下代码,使用 OllyDBG 进行调试:
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
|
|
@ -122,13 +123,11 @@ int main(int argc,char* argv[])
|
|||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
进入OD后,依然要越过启动代码,寻找真正的`main`函数。
|
||||
进入 OD 后,依然要越过启动代码,寻找真正的`main`函数。
|
||||
|
||||

|
||||
|
||||
连续按F8。
|
||||
连续按 F8。
|
||||
|
||||
按照以往的经验,调用`main`函数至少在调用`GetCommandLine`函数之后。
|
||||
|
||||
|
|
@ -138,13 +137,13 @@ int main(int argc,char* argv[])
|
|||
|
||||

|
||||
|
||||
看到这里可以push了3个参数,发现argv和argc的字样,那么下一个call十有八九会到达`main`函数了。
|
||||
看到这里可以 push 了 3 个参数,发现 argv 和 argc 的字样,那么下一个 call 十有八九会到达`main`函数了。
|
||||
|
||||
按F7单步进入。
|
||||
按 F7 单步进入。
|
||||
|
||||

|
||||
|
||||
来到这里就已经很明显了,可以明显的看到下图中由`mov ..., 0x5`和`mov ..., 0x6`两个语句,这明显是在给变量赋值上5和6,那么就可以推测`call test.00DD135C`实际上在调用`add`函数,将光标移动指令处,按回车键。
|
||||
来到这里就已经很明显了,可以明显的看到下图中由`mov ..., 0x5`和`mov ..., 0x6`两个语句,这明显是在给变量赋值上 5 和 6,那么就可以推测`call test.00DD135C`实际上在调用`add`函数,将光标移动指令处,按回车键。
|
||||
|
||||

|
||||
|
||||
|
|
@ -152,19 +151,19 @@ int main(int argc,char* argv[])
|
|||
|
||||

|
||||
|
||||
那么回到之前的`main`函数,可以看到代码将0x5和0x6放入rax和ecx寄存器后,又对其进行了压栈操作,实际等价于`push 0x5`和`push 0x6`。
|
||||
那么回到之前的`main`函数,可以看到代码将 0x5 和 0x6 放入 rax 和 ecx 寄存器后,又对其进行了压栈操作,实际等价于`push 0x5`和`push 0x6`。
|
||||
|
||||

|
||||
|
||||
push操作就是x86架构下典型的压栈方式,符合**__cdecl**调用约定(C/C++程序的默认调用约定,在此不作赘述),在该约定下,可以看到压栈顺序是逆序的,右边的参数先进栈,左边的参数后进栈,栈顶指针esp指向栈中第1个可用的数据项。
|
||||
push 操作就是 x86 架构下典型的压栈方式,符合**\_\_cdecl**调用约定(C/C++程序的默认调用约定,在此不作赘述),在该约定下,可以看到压栈顺序是逆序的,右边的参数先进栈,左边的参数后进栈,栈顶指针 esp 指向栈中第 1 个可用的数据项。
|
||||
|
||||
在调用函数时,调用者依次将参数压入栈,然后调用函数。函数被调用以后,在栈中取得数据并进行计算,函数计算结束后,由调用者或者函数本身修改栈,使栈恢复原样(平衡栈数据)。
|
||||
|
||||
现在将程序运行到call指令之前,查看OD的栈区数据,可以看到显示Arg1=5,Arg2=6,显然这两个参数已经被压栈。
|
||||
现在将程序运行到 call 指令之前,查看 OD 的栈区数据,可以看到显示 Arg1=5,Arg2=6,显然这两个参数已经被压栈。
|
||||
|
||||

|
||||
|
||||
进入add函数后,可以看到程序将arg1存入eax寄存器,再和arg2相加,就完成了计算。
|
||||
进入 add 函数后,可以看到程序将 arg1 存入 eax 寄存器,再和 arg2 相加,就完成了计算。
|
||||
|
||||

|
||||
|
||||
|
|
@ -185,11 +184,11 @@ int add(int x,int y)
|
|||
}
|
||||
```
|
||||
|
||||
进入OD,再次回到add函数中。
|
||||
进入 OD,再次回到 add 函数中。
|
||||
|
||||

|
||||
|
||||
`mov eax, [local.2]`是将计算的最后结果就保存在eax寄存器中,eax就作为存放返回值的寄存器。
|
||||
`mov eax, [local.2]`是将计算的最后结果就保存在 eax 寄存器中,eax 就作为存放返回值的寄存器。
|
||||
|
||||

|
||||
|
||||
|
|
@ -214,15 +213,15 @@ int main(int argc,char* argv[])
|
|||
}
|
||||
```
|
||||
|
||||
这里定义了一个`max`函数,接收`a`和`b`的地址,将其中较大数放入变量`a`中。使用OD,进入`main`函数。
|
||||
这里定义了一个`max`函数,接收`a`和`b`的地址,将其中较大数放入变量`a`中。使用 OD,进入`main`函数。
|
||||
|
||||

|
||||
|
||||
进入`max`函数,`mov eax, [arg.1]`和`mov ecx, [arg.2]`是将参数`a`和`b`的值加载到两个寄存器。
|
||||
|
||||
可以看到`cmp`指令,这是一个用于比较大小的指令,紧跟着的是条件跳转指令,表示如果a<b则不跳转,继续往下执行,这里不多说。
|
||||
可以看到`cmp`指令,这是一个用于比较大小的指令,紧跟着的是条件跳转指令,表示如果 a<b 则不跳转,继续往下执行,这里不多说。
|
||||
|
||||
直接看到`mov dword ptr ds:[eax], edx`。`dword ptr`是指明数据宽度,而这一步操作就是将结果填入变量a所在的内存地址处。
|
||||
直接看到`mov dword ptr ds:[eax], edx`。`dword ptr`是指明数据宽度,而这一步操作就是将结果填入变量 a 所在的内存地址处。
|
||||
|
||||

|
||||
|
||||
|
|
@ -254,9 +253,9 @@ int main(void)
|
|||
}
|
||||
```
|
||||
|
||||
进入`add`函数,`sub esp,0xcc`即下降栈顶0xcc个字节,实际上是为局部变量开辟空间,同时也在预防栈溢出攻击(一种攻击手法,此处不作赘述)。
|
||||
进入`add`函数,`sub esp,0xcc`即下降栈顶 0xcc 个字节,实际上是为局部变量开辟空间,同时也在预防栈溢出攻击(一种攻击手法,此处不作赘述)。
|
||||
|
||||
这里开辟后的空间一部分是用来存放变量z的。
|
||||
这里开辟后的空间一部分是用来存放变量 z 的。
|
||||
|
||||

|
||||
|
||||
|
|
@ -297,11 +296,11 @@ int main(void)
|
|||
}
|
||||
```
|
||||
|
||||
这里是对z赋值,直接用`mov`将7写入一个固定的内存地址。
|
||||
这里是对 z 赋值,直接用`mov`将 7 写入一个固定的内存地址。
|
||||
|
||||

|
||||
|
||||
在`add`函数中,同样直接从固定的地址中取出`z`的值到eax寄存器中。
|
||||
在`add`函数中,同样直接从固定的地址中取出`z`的值到 eax 寄存器中。
|
||||
|
||||

|
||||
|
||||
|
|
@ -335,7 +334,6 @@ int main(void)
|
|||
|
||||

|
||||
|
||||
在内存中数组可存在于栈、数据段及动态内存中,本例中`a[]`数组就保存在数据段.data中,其寻址用“基址+偏移量”实现。
|
||||
在内存中数组可存在于栈、数据段及动态内存中,本例中`a[]`数组就保存在数据段.data 中,其寻址用“基址+偏移量”实现。
|
||||
|
||||
`b[]`数组放在栈中,这些栈在编译时分配。数组在声明时可以直接计算偏移地址,针对数组成员寻址时是采用实际的偏移量完成的。
|
||||
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
layout: "../../../../../layouts/MarkdownPost.astro"
|
||||
title: 写C语言的种种工具
|
||||
date: 2022-03-09 22:37:34
|
||||
pubDate: 2022-03-09 22:37:34
|
||||
tid: tools-to-write-c
|
||||
categories:
|
||||
- 技术
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
layout: "../../../../../layouts/MarkdownPost.astro"
|
||||
title: Go语言极限入门
|
||||
date: 2022-04-19 10:11:26
|
||||
pubDate: 2022-04-19 10:11:26
|
||||
categories:
|
||||
- 技术
|
||||
- Go语言
|
||||
|
|
@ -15,14 +16,13 @@ tags:
|
|||
-
|
||||
---
|
||||
|
||||
## Go语言极限入门
|
||||
## Go 语言极限入门
|
||||
|
||||
> 参考书目: 《Go程序设计语言》
|
||||
>
|
||||
> 参考书目: 《Go 程序设计语言》
|
||||
|
||||
#### 快速入门
|
||||
|
||||
如下是hello world程序:
|
||||
如下是 hello world 程序:
|
||||
|
||||
```go
|
||||
// hello.go
|
||||
|
|
@ -37,19 +37,19 @@ func main() {
|
|||
|
||||
终端执行 `go run hello.go`。
|
||||
|
||||
Go代码是用包来组织的,包类似于其他语言中的库和模块。
|
||||
Go 代码是用包来组织的,包类似于其他语言中的库和模块。
|
||||
|
||||
`package main`指明了这个文件属于哪个包。
|
||||
|
||||
后面跟着导入的是其他包的列表,fmt用于格式化输出和扫描输入。
|
||||
后面跟着导入的是其他包的列表,fmt 用于格式化输出和扫描输入。
|
||||
|
||||
main包比较特殊,它用来定义一个独立的可执行程序,而不是库。import声明必须跟在package声明之后。import导入声明后,是组成程序的函数。
|
||||
main 包比较特殊,它用来定义一个独立的可执行程序,而不是库。import 声明必须跟在 package 声明之后。import 导入声明后,是组成程序的函数。
|
||||
|
||||
一个函数的声明由func关键字、函数名、参数列表(main函数为空)、返回值列表和函数体构成。
|
||||
一个函数的声明由 func 关键字、函数名、参数列表(main 函数为空)、返回值列表和函数体构成。
|
||||
|
||||
###### 命令行参数
|
||||
|
||||
命令行参数以os包中Args名字的变量供程序访问,在os包外面,使用os.Args这个名字,这是一个字符串slice。
|
||||
命令行参数以 os 包中 Args 名字的变量供程序访问,在 os 包外面,使用 os.Args 这个名字,这是一个字符串 slice。
|
||||
|
||||
```go
|
||||
// echo.go 输出命令行参数
|
||||
|
|
@ -76,9 +76,9 @@ $ go build echo.go
|
|||
hello
|
||||
```
|
||||
|
||||
var 关键字声明了两个string类型的变量s和sep。变量可以声明的时候初始化。如果变量没有明确地初始化,它将隐式初始化这个类型的空值。
|
||||
var 关键字声明了两个 string 类型的变量 s 和 sep。变量可以声明的时候初始化。如果变量没有明确地初始化,它将隐式初始化这个类型的空值。
|
||||
|
||||
for 是 go里面唯一的循环语句。
|
||||
for 是 go 里面唯一的循环语句。
|
||||
|
||||
```go
|
||||
for initlization; condition; post {
|
||||
|
|
@ -86,11 +86,11 @@ for initlization; condition; post {
|
|||
}
|
||||
```
|
||||
|
||||
可选的initialization(初始化)语句在循环开始之前执行。如果存在,它必须是一个简单的语句。三部分都是可省的,如果三部分都不存在,只有一个for,那就是无限循环。
|
||||
可选的 initialization(初始化)语句在循环开始之前执行。如果存在,它必须是一个简单的语句。三部分都是可省的,如果三部分都不存在,只有一个 for,那就是无限循环。
|
||||
|
||||
另一种形式的for循环是在字符串或slice数据上迭代。
|
||||
另一种形式的 for 循环是在字符串或 slice 数据上迭代。
|
||||
|
||||
如下是第二种echo程序:
|
||||
如下是第二种 echo 程序:
|
||||
|
||||
```go
|
||||
// echo.go
|
||||
|
|
@ -111,9 +111,9 @@ func main() {
|
|||
}
|
||||
```
|
||||
|
||||
每一次迭代,range都产生一对值: 索引和这个索引处元素的值。因为这个例子里用不到索引,但是语法上range循环需要处理。应次也必须处理索引。可以将索引赋予一个临时变量,然后忽略它,但是**go不允许存在无用的变量**。选择使用**空标识符**"__"。空标识符可以用在任何语法需要变量名但逻辑不需要的地方。
|
||||
每一次迭代,range 都产生一对值: 索引和这个索引处元素的值。因为这个例子里用不到索引,但是语法上 range 循环需要处理。应次也必须处理索引。可以将索引赋予一个临时变量,然后忽略它,但是**go 不允许存在无用的变量**。选择使用**空标识符**"\_\_"。空标识符可以用在任何语法需要变量名但逻辑不需要的地方。
|
||||
|
||||
如果有大量的数据要处理,这样做的代价会比较大。可以使用strings包中的`Join`函数。
|
||||
如果有大量的数据要处理,这样做的代价会比较大。可以使用 strings 包中的`Join`函数。
|
||||
|
||||
```go
|
||||
package main
|
||||
|
|
@ -131,7 +131,7 @@ func main() {
|
|||
|
||||
###### 找出重复行
|
||||
|
||||
如下程序要输出标准输入中出现次数大于1的行,前面是次数。
|
||||
如下程序要输出标准输入中出现次数大于 1 的行,前面是次数。
|
||||
|
||||
```go
|
||||
package main
|
||||
|
|
@ -156,21 +156,21 @@ func main() {
|
|||
}
|
||||
```
|
||||
|
||||
在上述这个程序中,引入了if语句、map类型和bufio包。
|
||||
在上述这个程序中,引入了 if 语句、map 类型和 bufio 包。
|
||||
|
||||
像for一样,if语句中的条件部分也从不放在圆括号里。
|
||||
像 for 一样,if 语句中的条件部分也从不放在圆括号里。
|
||||
|
||||
map存储一个键值对集合。在这里map的键是字符串,值是数字。内置的函数make可以用来新建map,它还可以有其他用途。
|
||||
map 存储一个键值对集合。在这里 map 的键是字符串,值是数字。内置的函数 make 可以用来新建 map,它还可以有其他用途。
|
||||
|
||||
`counts := make(map[string]int)`
|
||||
|
||||
每次从输入读取一行内容,这一行就作为map中的键,对应的值递增1。键在map中不存在时也是没有问题的。为了输出结果,使用基于range的for循环。
|
||||
每次从输入读取一行内容,这一行就作为 map 中的键,对应的值递增 1。键在 map 中不存在时也是没有问题的。为了输出结果,使用基于 range 的 for 循环。
|
||||
|
||||
bufio包,使用它可以简便和高效地处理输入和输出。其中一个最有用的特性是称为扫描器(Scanner)的类型,可以读取输入,以行或者单词为单位断开。
|
||||
bufio 包,使用它可以简便和高效地处理输入和输出。其中一个最有用的特性是称为扫描器(Scanner)的类型,可以读取输入,以行或者单词为单位断开。
|
||||
|
||||
`input := bufio.NewScanner(os.Stdin)`
|
||||
|
||||
Printf函数有超过10个转义字符:
|
||||
Printf 函数有超过 10 个转义字符:
|
||||
|
||||
| verb | 描述 |
|
||||
| -------- | ---------------------------- |
|
||||
|
|
@ -247,7 +247,7 @@ $ ./main test.txt
|
|||
|
||||
上述程序是采用"流式"模式读取输入,然后按需拆分为行。
|
||||
|
||||
这里引入一个ReadFile函数(从io/ioutil包导入),它读取整个命名文件的内容,还引入一个strings.Split函数,将一个字符串分割为一个由子串组成的slice:
|
||||
这里引入一个 ReadFile 函数(从 io/ioutil 包导入),它读取整个命名文件的内容,还引入一个 strings.Split 函数,将一个字符串分割为一个由子串组成的 slice:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
|
@ -279,11 +279,11 @@ func main() {
|
|||
}
|
||||
```
|
||||
|
||||
ReadFile函数返回一个可以转化成字符串的字节slice,这样它可以被strings.Split分割。
|
||||
ReadFile 函数返回一个可以转化成字符串的字节 slice,这样它可以被 strings.Split 分割。
|
||||
|
||||
###### **获取一个URL**
|
||||
###### **获取一个 URL**
|
||||
|
||||
Go提供了一系列包,在net包下面分组管理,使用它们可以方便地通过互联网发送和接受信息。
|
||||
Go 提供了一系列包,在 net 包下面分组管理,使用它们可以方便地通过互联网发送和接受信息。
|
||||
|
||||
```go
|
||||
package main
|
||||
|
|
@ -313,9 +313,9 @@ func main() {
|
|||
}
|
||||
```
|
||||
|
||||
程序fetch展示从互联网获取信息的最小需求,它获取每个指定URL的内容,然后不加解析地输出。fetch来自curl工具。
|
||||
程序 fetch 展示从互联网获取信息的最小需求,它获取每个指定 URL 的内容,然后不加解析地输出。fetch 来自 curl 工具。
|
||||
|
||||
这个程序使用的函数来自两个包: net/http和io/ioutil。http.Get函数产生一个HTTP请求,如果没有出错,返回结果存在响应结构resp里面,其中resp的Body域包含服务器端响应的一个可读取数据流。随后ioutil.ReadAll读取整个响应结果并存入b。
|
||||
这个程序使用的函数来自两个包: net/http 和 io/ioutil。http.Get 函数产生一个 HTTP 请求,如果没有出错,返回结果存在响应结构 resp 里面,其中 resp 的 Body 域包含服务器端响应的一个可读取数据流。随后 ioutil.ReadAll 读取整个响应结果并存入 b。
|
||||
|
||||
```go
|
||||
package main
|
||||
|
|
@ -345,7 +345,7 @@ func main() {
|
|||
}
|
||||
```
|
||||
|
||||
关闭Body数据流来避免资源泄露。
|
||||
关闭 Body 数据流来避免资源泄露。
|
||||
|
||||
运行结果:
|
||||
|
||||
|
|
@ -396,7 +396,7 @@ func main() {
|
|||
}
|
||||
```
|
||||
|
||||
###### 并发获取多个URL
|
||||
###### 并发获取多个 URL
|
||||
|
||||
```go
|
||||
package main
|
||||
|
|
@ -449,15 +449,15 @@ $ ./fetchall http://www.baidu.com http://www.qq.com
|
|||
0.14s elapsed
|
||||
```
|
||||
|
||||
这个进程可以并发获取很多URL内容,于是这个进程使用的时间不超过耗时最长时间的任务。这个程序不保存响应内容,但会报告每个响应的大小和花费的时间。
|
||||
这个进程可以并发获取很多 URL 内容,于是这个进程使用的时间不超过耗时最长时间的任务。这个程序不保存响应内容,但会报告每个响应的大小和花费的时间。
|
||||
|
||||
gorotine是一个并发执行的函数。通道是一种允许某一进程向另一种进程传递制定类型的值的通信机制。main函数在一个goroutine中执行,然后go语句创建额外的goroutine。
|
||||
gorotine 是一个并发执行的函数。通道是一种允许某一进程向另一种进程传递制定类型的值的通信机制。main 函数在一个 goroutine 中执行,然后 go 语句创建额外的 goroutine。
|
||||
|
||||
main函数使用make创建一 个字符串通道。对于每个命令行参数,go语句在第一轮循环中启动一个新的goroutine,它异步调用fetch来使用http.Get获取URL内容。io.Copy函数读取响应的内容,然后通过写入ioutil.Discard输出流进行丢弃。Copy返回字节数和错误信息。每一个结果返回时,fetch发送一行汇总信息到通道ch。main中第二轮循环接收并且输出那些汇总行。
|
||||
main 函数使用 make 创建一 个字符串通道。对于每个命令行参数,go 语句在第一轮循环中启动一个新的 goroutine,它异步调用 fetch 来使用 http.Get 获取 URL 内容。io.Copy 函数读取响应的内容,然后通过写入 ioutil.Discard 输出流进行丢弃。Copy 返回字节数和错误信息。每一个结果返回时,fetch 发送一行汇总信息到通道 ch。main 中第二轮循环接收并且输出那些汇总行。
|
||||
|
||||
###### 一个WEB服务器
|
||||
###### 一个 WEB 服务器
|
||||
|
||||
如下代码,实现一个简单的服务器,将返回服务器URL路径部分:
|
||||
如下代码,实现一个简单的服务器,将返回服务器 URL 路径部分:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
|
@ -485,7 +485,7 @@ $ ./fetch http://localhost:8000/help
|
|||
URL.Path = "/help"
|
||||
```
|
||||
|
||||
这里的库函数做了大部分工作。main函数将一个处理函数和以/开头的URL链接在一起,代表所有的URL使用这个函数处理,然后启动服务器监听8000端口处的请求。一个请求由http.Request类型的结构体表示,它包含很多关联的域,其中一个是所请求的URL。当一个请求到达时,它被转交给处理函数,并从请求的URL中提取路径部分,使用fmt.Printf格式化,然后作为响应发送回去。
|
||||
这里的库函数做了大部分工作。main 函数将一个处理函数和以/开头的 URL 链接在一起,代表所有的 URL 使用这个函数处理,然后启动服务器监听 8000 端口处的请求。一个请求由 http.Request 类型的结构体表示,它包含很多关联的域,其中一个是所请求的 URL。当一个请求到达时,它被转交给处理函数,并从请求的 URL 中提取路径部分,使用 fmt.Printf 格式化,然后作为响应发送回去。
|
||||
|
||||
为服务器添加功能也很简单,如下程序会返回收到的请求数量:
|
||||
|
||||
|
|
@ -534,11 +534,11 @@ $ ./fetch http://localhost:8000/count
|
|||
Count 2
|
||||
```
|
||||
|
||||
这个服务器有两个处理函数,通过请求的URL来决定哪一个被调用: 请求/count调用counter,其他的调用handler。
|
||||
这个服务器有两个处理函数,通过请求的 URL 来决定哪一个被调用: 请求/count 调用 counter,其他的调用 handler。
|
||||
|
||||
以/结尾的处理模式匹配所有含有这个前缀的URL。在后台,对于每个传入的请求,服务器在不同的goroutine中运行该处理函数,这样它可以同时处理多个请求。
|
||||
以/结尾的处理模式匹配所有含有这个前缀的 URL。在后台,对于每个传入的请求,服务器在不同的 goroutine 中运行该处理函数,这样它可以同时处理多个请求。
|
||||
|
||||
然而,如果两个并发的请求试图同时更新计数值count,count可能会不一致地增加,程序会产生一个严重的竞态BUG。为了避免该问题,必须确保最多只有一个goroutine在同一时间访问变量,这正是mu.Lock()和mu.Unlock()语句的作用。
|
||||
然而,如果两个并发的请求试图同时更新计数值 count,count 可能会不一致地增加,程序会产生一个严重的竞态 BUG。为了避免该问题,必须确保最多只有一个 goroutine 在同一时间访问变量,这正是 mu.Lock()和 mu.Unlock()语句的作用。
|
||||
|
||||
修改处理函数,使其可以报告接收到的消息头和表单数据,这样可以方便服务器审查和调试请求。
|
||||
|
||||
|
|
@ -585,9 +585,9 @@ RemoteAddr = "127.0.0.1:47766"
|
|||
|
||||
#### 程序结构
|
||||
|
||||
声明是给一个程序实体**命名**,并且设定其部分或全部属性。有4个主要声明: 变量(var)、常量(const)、类型(type)函数(func)。
|
||||
声明是给一个程序实体**命名**,并且设定其部分或全部属性。有 4 个主要声明: 变量(var)、常量(const)、类型(type)函数(func)。
|
||||
|
||||
Go程序存储在一个或多个以.go为后缀的文件里。每一个文件以package声明开头,表明文件属于哪个包。package 声明后面是import声明,然后是*包级别*的类型、变量、常量、函数的声明,不区分顺序。
|
||||
Go 程序存储在一个或多个以.go 为后缀的文件里。每一个文件以 package 声明开头,表明文件属于哪个包。package 声明后面是 import 声明,然后是*包级别*的类型、变量、常量、函数的声明,不区分顺序。
|
||||
|
||||
例如,下面的程序声明一个常量、一个函数和一对变量:
|
||||
|
||||
|
|
@ -608,13 +608,13 @@ func main() {
|
|||
// 输出: boiling point = 212 F or 100 C
|
||||
```
|
||||
|
||||
常量boilingF是一个包级别的声明(main包),f和c是属于main函数的局部变量。包级别的实体名字不仅对于包含其声明的源文件可见,而且对于同一个包里面的所有源文件可见。
|
||||
常量 boilingF 是一个包级别的声明(main 包),f 和 c 是属于 main 函数的局部变量。包级别的实体名字不仅对于包含其声明的源文件可见,而且对于同一个包里面的所有源文件可见。
|
||||
|
||||
另一方面,局部声明仅仅是在声明所在的函数内部可见,并且可能对于函数中的一小块区域可见。
|
||||
|
||||
**函数的声明**包含一个名字、参数列表(由函数的调用者提供的变量)、一个可选的返回值列表,以及函数体。
|
||||
|
||||
下面的函数fToC封装了温度转换的逻辑,这样可以只定义一次而在多个地方使用。
|
||||
下面的函数 fToC 封装了温度转换的逻辑,这样可以只定义一次而在多个地方使用。
|
||||
|
||||
```go
|
||||
package main
|
||||
|
|
@ -644,17 +644,17 @@ func fToC(f float64) float64 {
|
|||
|
||||
类型和表达式部分可以省略一个,但不能都省略。
|
||||
|
||||
如果类型省略,它的类型将由初始化表达式决定。如果表达式省略,其初始值对应于类型的零值,因此Go中不存在未初始化变量。
|
||||
如果类型省略,它的类型将由初始化表达式决定。如果表达式省略,其初始值对应于类型的零值,因此 Go 中不存在未初始化变量。
|
||||
|
||||
###### 短变量声明
|
||||
|
||||
在函数中,一种称作**短变量声明**的可选形式可以用来初始化局部变量。
|
||||
|
||||
形式: `name := expression`,name的类型由expression的类型来决定。
|
||||
形式: `name := expression`,name 的类型由 expression 的类型来决定。
|
||||
|
||||
在局部变量的声明和初始化主要使用短声明。
|
||||
|
||||
var声明通常是为那些跟初始化表达式类型不一致的局部变量保留的,或者用于后面才对变量赋值以及变量初始值不重要的情况。
|
||||
var 声明通常是为那些跟初始化表达式类型不一致的局部变量保留的,或者用于后面才对变量赋值以及变量初始值不重要的情况。
|
||||
|
||||
```go
|
||||
i := 100
|
||||
|
|
@ -666,7 +666,7 @@ i,j := 0,1
|
|||
|
||||
指针的值是一个变量的地址。
|
||||
|
||||
如果一个变量声明为`var x int`,表达式&x获取一个指向整型变量的指针。
|
||||
如果一个变量声明为`var x int`,表达式&x 获取一个指向整型变量的指针。
|
||||
|
||||
```go
|
||||
x := 1
|
||||
|
|
@ -678,7 +678,7 @@ fmt.Println(x) // 结果"2"
|
|||
|
||||
每个聚合类型变量的组成都是变量,所以也有一个地址。
|
||||
|
||||
指针类型的零值是nil。
|
||||
指针类型的零值是 nil。
|
||||
|
||||
函数可以返回局部变量的地址。
|
||||
|
||||
|
|
@ -704,7 +704,7 @@ incr(&v) // v 等于 2
|
|||
fmt.Println(incr(&v)) // "3"
|
||||
```
|
||||
|
||||
指针对于flag包是很关键的,它使用程序的命令行参数来设置整个程序内某些变量的值。
|
||||
指针对于 flag 包是很关键的,它使用程序的命令行参数来设置整个程序内某些变量的值。
|
||||
|
||||
```go
|
||||
package main
|
||||
|
|
@ -727,9 +727,9 @@ func main() {
|
|||
}
|
||||
```
|
||||
|
||||
flag.Bool函数创建一个新的布尔标识变量,它有3个参数。变量sep和n是指向标识变量的指针,必须通过sep和n来访问。
|
||||
flag.Bool 函数创建一个新的布尔标识变量,它有 3 个参数。变量 sep 和 n 是指向标识变量的指针,必须通过 sep 和 n 来访问。
|
||||
|
||||
当程序运行前,在使用标识前,必须调用flag.Parse来更新标识变量的默认值。非标识参数也可以从flag.Args()返回的字符串slice来访问。如果flag.Parse遇到错误,它输出一条帮助信息,然后调用os.Exit(2)来结束程序。
|
||||
当程序运行前,在使用标识前,必须调用 flag.Parse 来更新标识变量的默认值。非标识参数也可以从 flag.Args()返回的字符串 slice 来访问。如果 flag.Parse 遇到错误,它输出一条帮助信息,然后调用 os.Exit(2)来结束程序。
|
||||
|
||||
运行示例:
|
||||
|
||||
|
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 94 KiB |
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
layout: "../../../../../layouts/MarkdownPost.astro"
|
||||
title: Linux笔记 - 信号
|
||||
date: 2022-04-05 13:13:28
|
||||
pubDate: 2022-04-05 13:13:28
|
||||
categories:
|
||||
- 技术
|
||||
- Linux
|
||||
|
|
@ -20,11 +21,10 @@ tags:
|
|||
信号本质上是整数,用户模式下用来模拟硬件中断。
|
||||
|
||||
- 什么是硬件中断?先理解为硬件引发的中断。
|
||||
- 什么是中断?简言之,就是让CPU停下当前干的事转而去处理新的情况。
|
||||
- 什么是中断?简言之,就是让 CPU 停下当前干的事转而去处理新的情况。
|
||||
- 信号是谁产生的?可以由硬件、内核和进程产生。
|
||||
|
||||
|
||||
例如在终端上用`Ctrl+C`,可以结束掉当前终端进程,本质上是发送SIGINT信号。
|
||||
例如在终端上用`Ctrl+C`,可以结束掉当前终端进程,本质上是发送 SIGINT 信号。
|
||||
|
||||
如下是一个实例程序,该程序循环打印数字。运行起来,看看`Ctrl+C`能不能中止它:
|
||||
|
||||
|
|
@ -52,15 +52,15 @@ int main()
|
|||
}
|
||||
```
|
||||
|
||||
如下所示,现在使用Ctrl+C是没办法中断这个程序的:
|
||||
如下所示,现在使用 Ctrl+C 是没办法中断这个程序的:
|
||||
|
||||

|
||||
|
||||
这里可以用man命令查看一下signal函数的帮助文档:
|
||||
这里可以用 man 命令查看一下 signal 函数的帮助文档:
|
||||
|
||||

|
||||
|
||||
signal关联上了一个信号处理函数,来处理信号。
|
||||
signal 关联上了一个信号处理函数,来处理信号。
|
||||
|
||||
现在修改这个代码,添加一行打印当前进程号,然后打开两个终端,一个终端运行上面的程序,一个终端尝试`kill`命令来发送信号:
|
||||
|
||||
|
|
@ -74,13 +74,13 @@ kill 要指定参数:
|
|||
|
||||
**信号处理的过程**
|
||||
|
||||
进程A在运行,内核、硬件或者其他进程发送信号给进程A。进程A接收到信号后,直接做信号本身规定的对应处理或者做事先注册好的信号处理。如上面signal函数就是注册信号处理,hand函数替换了本身默认的信号处理。当然信号是可以屏蔽的,不作任何处理。
|
||||
进程 A 在运行,内核、硬件或者其他进程发送信号给进程 A。进程 A 接收到信号后,直接做信号本身规定的对应处理或者做事先注册好的信号处理。如上面 signal 函数就是注册信号处理,hand 函数替换了本身默认的信号处理。当然信号是可以屏蔽的,不作任何处理。
|
||||
|
||||
Linux有哪些信号类型:
|
||||
Linux 有哪些信号类型:
|
||||
|
||||

|
||||
|
||||
Linux 信号有64个,分为不可靠信号(非实时,1-31,Unix提供)和可靠信号(32-64,后来扩充的)。又有系统自带的标准信号和用户自定义的信号。
|
||||
Linux 信号有 64 个,分为不可靠信号(非实时,1-31,Unix 提供)和可靠信号(32-64,后来扩充的)。又有系统自带的标准信号和用户自定义的信号。
|
||||
|
||||
介绍几个命令或函数:
|
||||
|
||||
|
|
@ -204,11 +204,11 @@ int main(int argc,char* argv[])
|
|||
|
||||
也试试发送其他数据,联合体中还有一个指针类型的成员。
|
||||
|
||||
## Linux信号(续)
|
||||
## Linux 信号(续)
|
||||
|
||||
> 参考网站: Github、《极客时间》
|
||||
|
||||
现在从底层角度来观赏Linux信号机制。
|
||||
现在从底层角度来观赏 Linux 信号机制。
|
||||
|
||||
首先看信号的具体作用:
|
||||
|
||||
|
|
@ -216,13 +216,13 @@ int main(int argc,char* argv[])
|
|||
|
||||

|
||||
|
||||
如上文所提,处理信号有3种方式:执行默认操作,捕获信号和忽略信号
|
||||
如上文所提,处理信号有 3 种方式:执行默认操作,捕获信号和忽略信号
|
||||
|
||||
设置信号处理有两种方式: signal和sigaction
|
||||
设置信号处理有两种方式: signal 和 sigaction
|
||||
|
||||
signal将一个动作和信号关联,sigaction也是将一个动作和信号关联,但这个动作用一个结构体表示了,处理信号将更加细致
|
||||
signal 将一个动作和信号关联,sigaction 也是将一个动作和信号关联,但这个动作用一个结构体表示了,处理信号将更加细致
|
||||
|
||||
siganal不是系统调用,而是glibc封装的一个函数,实现如下:
|
||||
siganal 不是系统调用,而是 glibc 封装的一个函数,实现如下:
|
||||
|
||||
```c
|
||||
# define signal __sysv_signal
|
||||
|
|
@ -242,9 +242,9 @@ __sysv_signal (int sig, __sighandler_t handler)
|
|||
weak_alias (__sysv_signal, sysv_signal)
|
||||
```
|
||||
|
||||
这里可以看到 sa_flags 设置了一个SA_ONESHOT,这意味着信号处理函数只作用一次,用完一次后就返回默认行为
|
||||
这里可以看到 sa_flags 设置了一个 SA_ONESHOT,这意味着信号处理函数只作用一次,用完一次后就返回默认行为
|
||||
|
||||
同时设置了SA_NOMASK,通过 __sigemptyset,将 sa_mask 设置为空。这样的设置表示在这个信号处理函数执行过程中,如果再有其他信号,哪怕相同的信号到来的时候,这个信号处理函数会被中断。如果一个信号处理函数真的被其他信号中断,问题也不大,因为当处理完了其他的信号处理函数后,还会回来接着处理这个信号处理函数的,但是对于相同的信号就有点尴尬了。一般的思路应该是,当某一个信号的信号处理函数运行的时候,暂时屏蔽这个信号,屏蔽并不意味着信号一定丢失,而是暂存,这样能够做到信号处理函数对于相同的信号,处理完一个再处理下一个,这样信号处理函数的逻辑要简单得多。
|
||||
同时设置了 SA_NOMASK,通过 \_\_sigemptyset,将 sa_mask 设置为空。这样的设置表示在这个信号处理函数执行过程中,如果再有其他信号,哪怕相同的信号到来的时候,这个信号处理函数会被中断。如果一个信号处理函数真的被其他信号中断,问题也不大,因为当处理完了其他的信号处理函数后,还会回来接着处理这个信号处理函数的,但是对于相同的信号就有点尴尬了。一般的思路应该是,当某一个信号的信号处理函数运行的时候,暂时屏蔽这个信号,屏蔽并不意味着信号一定丢失,而是暂存,这样能够做到信号处理函数对于相同的信号,处理完一个再处理下一个,这样信号处理函数的逻辑要简单得多。
|
||||
|
||||
还有一个设置就是设置了 SA_INTERRUPT,清除了 SA_RESTART。信号的到来时间是不可预期的,有可能程序正在调用某个漫长的系统调用的时候,突然到来一个信号,中断了这个系统调用,去执行信号处理函数。那么执行完后信号处理函数,系统调用怎么办?
|
||||
|
||||
|
|
@ -252,9 +252,9 @@ weak_alias (__sysv_signal, sysv_signal)
|
|||
|
||||
另外一种处理方法是 SA_RESTART。这个时候系统调用会被自动重新启动,不需要调用方自己写代码。当然也可能存在问题,例如从终端读入一个字符,这个时候用户在终端输入一个'a'字符,在处理'a'字符的时候被信号中断了,等信号处理完毕,再次读入一个字符的时候,如果用户不再输入,就停在那里了,需要用户再次输入同一个字符。
|
||||
|
||||
可知signal函数是受到限制的,因此,建议使用 sigaction 函数,根据自己的需要定制参数。
|
||||
可知 signal 函数是受到限制的,因此,建议使用 sigaction 函数,根据自己的需要定制参数。
|
||||
|
||||
下面是glibc中的实现:
|
||||
下面是 glibc 中的实现:
|
||||
|
||||
```c
|
||||
int
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
layout: "../../../../../layouts/MarkdownPost.astro"
|
||||
title: Linux笔记 - 文件I/O
|
||||
date: 2022-03-19 22:38:56
|
||||
pubDate: 2022-03-19 22:38:56
|
||||
categories:
|
||||
- 技术
|
||||
- Linux
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
layout: "../../../../../layouts/MarkdownPost.astro"
|
||||
title: Linux笔记 - 进程
|
||||
date: 2022-04-05 13:13:35
|
||||
pubDate: 2022-04-05 13:13:35
|
||||
categories:
|
||||
- 技术
|
||||
- Linux
|
||||
|
|
@ -15,7 +16,7 @@ tags:
|
|||
-
|
||||
---
|
||||
|
||||
## Linux进程
|
||||
## Linux 进程
|
||||
|
||||
本文会简单讲述进程创建和进程间通信。
|
||||
|
||||
|
|
@ -39,11 +40,11 @@ tags:
|
|||
|
||||

|
||||
|
||||
在终端输入`top`,输入q可以退出:
|
||||
在终端输入`top`,输入 q 可以退出:
|
||||
|
||||

|
||||
|
||||
Linux系统上可以管理多个进程,进程被分时分片处理。
|
||||
Linux 系统上可以管理多个进程,进程被分时分片处理。
|
||||
|
||||
下面演示在程序中如何创建进程:
|
||||
|
||||
|
|
@ -95,9 +96,9 @@ int main(void)
|
|||
|
||||

|
||||
|
||||
根据fork函数的返回值来看是在子进程中还是父进程中,调用成功的话,父进程中返回值是子进程的ID,子进程中是0。实际上这里fork返回了两次。
|
||||
根据 fork 函数的返回值来看是在子进程中还是父进程中,调用成功的话,父进程中返回值是子进程的 ID,子进程中是 0。实际上这里 fork 返回了两次。
|
||||
|
||||
fork做了两件事,第一件是复制结构,将父进程的数据结构都复制了一份。
|
||||
fork 做了两件事,第一件是复制结构,将父进程的数据结构都复制了一份。
|
||||
|
||||
第二件事就是唤醒新进程,让子进程运行起来。
|
||||
|
||||
|
|
@ -132,7 +133,7 @@ int main(void)
|
|||
}
|
||||
```
|
||||
|
||||
在代码中定义了一个变量n,初始值为10,子进程的n同样初始为10:
|
||||
在代码中定义了一个变量 n,初始值为 10,子进程的 n 同样初始为 10:
|
||||
|
||||

|
||||
|
||||
|
|
@ -140,7 +141,7 @@ int main(void)
|
|||
|
||||
有一种情况,父进程创建了子进程,父进程先于子进程结束,子进程资源没有被释放,就会变成僵尸进程,持续占用系统资源(内核中进程树会保存进程的数据,树中节点会保存进程的一些数据)。
|
||||
|
||||
子进程结束前,会向父进程发送SIGCHILD信号,父进程收到信号后,回收子进程资源,然后父进程再结束。父进程可以写一个wait函数,等待子进程发送SIGCHILD信号。
|
||||
子进程结束前,会向父进程发送 SIGCHILD 信号,父进程收到信号后,回收子进程资源,然后父进程再结束。父进程可以写一个 wait 函数,等待子进程发送 SIGCHILD 信号。
|
||||
|
||||

|
||||
|
||||
|
|
@ -166,7 +167,7 @@ int main(void) {
|
|||
}
|
||||
```
|
||||
|
||||
使用wait函数就是要等待子进程打印完所有数字,父进程才结束。
|
||||
使用 wait 函数就是要等待子进程打印完所有数字,父进程才结束。
|
||||
|
||||
最后看看守护进程。
|
||||
|
||||
|
|
@ -176,11 +177,11 @@ int main(void) {
|
|||
|
||||

|
||||
|
||||
TPGID为-1的话,就说明是守护进程。
|
||||
TPGID 为-1 的话,就说明是守护进程。
|
||||
|
||||
如果要把一个进程变成守护进程,要先`kill`其父进程,同时摆脱终端的控制。
|
||||
|
||||
要摆脱终端的控制,就要关闭三个文件描述符号:标准输入设备,标准输出设备,标准错误输出设备,然后重定向当前进程IO操作到/dev/null (黑洞设备)。然后要创建新的会话,摆脱原有会话进程组的控制。
|
||||
要摆脱终端的控制,就要关闭三个文件描述符号:标准输入设备,标准输出设备,标准错误输出设备,然后重定向当前进程 IO 操作到/dev/null (黑洞设备)。然后要创建新的会话,摆脱原有会话进程组的控制。
|
||||
|
||||
这里要提到进程的组织形式:多个进程组成一个进程组,多个进程组组成一个会话。这里不详细解释会话是什么。
|
||||
|
||||
|
|
@ -198,11 +199,11 @@ TPGID为-1的话,就说明是守护进程。
|
|||
|
||||
第二种:
|
||||
|
||||
1. 重设文件权限 `umask;`
|
||||
1. 重设文件权限 `umask;`
|
||||
2. 创建子进程 `fork`;
|
||||
3. 结束父进程;
|
||||
4. 创建新会话 `setsid;`
|
||||
5. 防止子进程成为僵尸进程 忽略SIGCHILD SIGUP信号;
|
||||
5. 防止子进程成为僵尸进程 忽略 SIGCHILD SIGUP 信号;
|
||||
6. 改变当前工作目录 `chdir`;
|
||||
7. 重定向文件描述符号 `open dup(fd,0) dup(fd,1)` .
|
||||
|
||||
|
|
@ -275,7 +276,7 @@ int main(void)
|
|||
|
||||
上文提到,`fork`子进程会拷贝父进程的数据,因此父子进程间通信还是比较简单的。
|
||||
|
||||
第一种通信方式,较为朴素,使用普通文件,进程A将要传递的信息放入这个文件,进程B再去读这个文件即可。父子进程间可通过文件描述符号,非父子进程之间就只能通过具体文件来通信。
|
||||
第一种通信方式,较为朴素,使用普通文件,进程 A 将要传递的信息放入这个文件,进程 B 再去读这个文件即可。父子进程间可通过文件描述符号,非父子进程之间就只能通过具体文件来通信。
|
||||
|
||||
第二种方式,文件映射虚拟内存 `mmap`。
|
||||
|
||||
|
|
@ -283,7 +284,7 @@ int main(void)
|
|||
|
||||
除此之外,还有信号、共享内存、消息队列、信号量和网络可用于通信。
|
||||
|
||||
本文主要讲前3种。
|
||||
本文主要讲前 3 种。
|
||||
|
||||
下面代码简单演示了第一种:
|
||||
|
||||
|
|
@ -337,7 +338,7 @@ int main(void)
|
|||
|
||||
下面讨论管道。
|
||||
|
||||
管道也是FIFO结构,分为两种,匿名管道和有名管道。
|
||||
管道也是 FIFO 结构,分为两种,匿名管道和有名管道。
|
||||
|
||||
父子进程使用匿名管道。
|
||||
|
||||
|
|
@ -396,7 +397,7 @@ int main(void)
|
|||
|
||||
接着演示有名管道,流程如下:
|
||||
|
||||
| 进程A | 进程B |
|
||||
| 进程 A | 进程 B |
|
||||
| ------------------- | ------------------ |
|
||||
| 创建管道文件 mkfifo | |
|
||||
| 打开管道文件 | 打开管道文件 |
|
||||
|
|
@ -407,7 +408,7 @@ int main(void)
|
|||
|
||||

|
||||
|
||||
进程A的代码:
|
||||
进程 A 的代码:
|
||||
|
||||
```c
|
||||
#include <unistd.h>
|
||||
|
|
@ -450,7 +451,7 @@ int main(void)
|
|||
}
|
||||
```
|
||||
|
||||
进程B:
|
||||
进程 B:
|
||||
|
||||
```c
|
||||
#include <unistd.h>
|
||||
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 132 KiB After Width: | Height: | Size: 132 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 138 KiB After Width: | Height: | Size: 138 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 8 KiB After Width: | Height: | Size: 8 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 8.3 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 111 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 158 KiB After Width: | Height: | Size: 158 KiB |
|
Before Width: | Height: | Size: 115 KiB After Width: | Height: | Size: 115 KiB |
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |