add blogs pages

This commit is contained in:
lyq 2024-05-15 00:47:31 +08:00
parent 4da476463c
commit 6673bbc9c2
276 changed files with 394 additions and 371 deletions

View file

@ -3,6 +3,7 @@ import BaseLayout from "../layouts/BaseLayout.astro"
import Tile from "../layouts/Tile.astro" import Tile from "../layouts/Tile.astro"
import MoreTile from "../layouts/MoreTile.astro" import MoreTile from "../layouts/MoreTile.astro"
const allPosts = await Astro.glob("../pages/posts/*.md") const allPosts = await Astro.glob("../pages/posts/*.md")
const Blogs = await Astro.glob("../pages/posts/blogs/**/*.md")
allPosts.sort( allPosts.sort(
(a, b) => (a, b) =>
Date.parse(b.frontmatter.pubDate) - Date.parse(a.frontmatter.pubDate) 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> </ul>
<div class="view-archive-wrapper"> <div class="view-archive-wrapper">
<a <a

View file

@ -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: 算法记录。

View file

@ -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

View 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

View file

@ -1,21 +1,22 @@
--- ---
layout: "../../../../../layouts/MarkdownPost.astro"
title: 从汇编角度看C语言 - 1 title: 从汇编角度看C语言 - 1
date: 2022-03-27 21:38:45 pubDate: 2022-03-27 21:38:45
categories: categories:
- 技术 - 技术
- C语言 - C语言
cover: ./assets/c10e40ab02e343e3afa9fb19b5619042/assembly.jpeg cover: ./assets/c10e40ab02e343e3afa9fb19b5619042/assembly.jpeg
tid: c-from-asm-1 tid: c-from-asm-1
description: 反汇编C语言程序的解读。 description: 反汇编C语言程序的解读。
permalink: /pages/989a42/ permalink: /pages/989a42/
author: author:
name: N3ptune name: N3ptune
link: https://www.cnblogs.com/N3ptune link: https://www.cnblogs.com/N3ptune
tags: tags:
- -
--- ---
# 从汇编角度看C语言 - 1 # 从汇编角度看 C 语言 - 1
## 写在前面 ## 写在前面
@ -23,13 +24,13 @@ tags:
- 系统平台: Windows 10 - 系统平台: Windows 10
- 调试工具: OllyDBG (吾爱破解专版) - 调试工具: OllyDBG (吾爱破解专版)
- 开发工具: Visual Studio 2008 Debug模式 - 开发工具: Visual Studio 2008 Debug 模式;
- 基础要求: 了解C语言和汇编语言; - 基础要求: 了解 C 语言和汇编语言;
- 大致内容: 简述了一些汇编语言与C语言的关系,方便初学者更好的认识程序的底层运行机制。 - 大致内容: 简述了一些汇编语言与 C 语言的关系,方便初学者更好的认识程序的底层运行机制。
## 开始 ## 开始
用VS2008编译如下代码 VS2008 编译如下代码:
```c ```c
#include <stdio.h> #include <stdio.h>
@ -41,24 +42,24 @@ int main(int argc,char* argv[])
} }
``` ```
将生成的**可执行文件**拖入**OllyDBG**。 将生成的**可执行文件**拖入**OllyDBG**。
## 如何寻找C程序入口? ## 如何寻找 C 程序入口?
明确两点: 明确两点:
1. mainCRTStartup和wmainCRTStartup是控制台环境下多字节编码和Unicode编码的启动函数; 1. mainCRTStartup 和 wmainCRTStartup 是控制台环境下多字节编码和 Unicode 编码的启动函数;
2. WinMainCRTStartup和wWinMainCRTStartup是windows环境下多字节编码和Unicode编码的启动函数。 2. WinMainCRTStartup 和 wWinMainCRTStartup 是 windows 环境下多字节编码和 Unicode 编码的启动函数。
mainCRTStartup做了哪些事 mainCRTStartup 做了哪些事?
如何通过mainCRTStartup来找到main函数入口 如何通过 mainCRTStartup 来找到 main 函数入口?
以上述程序为例,寻找其`main`函数入口。 以上述程序为例,寻找其`main`函数入口。
![image-20220327214647589](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327214647589.png) ![image-20220327214647589](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327214647589.png)
初步调试文件,可以发现许多`jmp`指令这是编译器生成的启动代码往下按F8跟随跳转。 初步调试文件,可以发现许多`jmp`指令,这是编译器生成的启动代码,往下按 F8 跟随跳转。
![image-20220327214714185](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327214714185.png) ![image-20220327214714185](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327214714185.png)
@ -68,17 +69,17 @@ mainCRTStartup做了哪些事
![image-20220327214738686](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327214738686.png) ![image-20220327214738686](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327214738686.png)
发现再`call hello.00241140`后控制台打出Hello World因此在此处设下断点。 发现再`call hello.00241140`后控制台打出 Hello World因此在此处设下断点。
按下`Ctrl+F2`后重新启动程序,按下`F9`运行到该断点,按下`F7`单步进入。 按下`Ctrl+F2`后重新启动程序,按下`F9`运行到该断点,按下`F7`单步进入。
![image-20220327214803763](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327214803763.png) ![image-20220327214803763](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327214803763.png)
按几次F8后看到如上界面可以看到Hello World字符串程序的开头即初始化栈帧操作显然已经成功来到了main函数中。 按几次 F8 后看到如上界面,可以看到 Hello World 字符串,程序的开头即初始化栈帧操作,显然已经成功来到了 main 函数中。
![image-20220327214826902](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327214826902.png) ![image-20220327214826902](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327214826902.png)
查看如上高亮的指令该指令将“Hello World”字符串的首地址压入栈中而后调用printf将字符串打印在控制台上。 查看如上高亮的指令该指令将“Hello World”字符串的首地址压入栈中而后调用 printf将字符串打印在控制台上。
![image-20220327215012755](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327215012755.png) ![image-20220327215012755](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327215012755.png)
@ -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 ```c
#include <stdio.h> #include <stdio.h>
@ -122,13 +123,11 @@ int main(int argc,char* argv[])
} }
``` ```
进入 OD 后,依然要越过启动代码,寻找真正的`main`函数。
进入OD后依然要越过启动代码寻找真正的`main`函数。
![image-20220327220701475](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327220701475.png) ![image-20220327220701475](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327220701475.png)
连续按F8。 连续按 F8。
按照以往的经验,调用`main`函数至少在调用`GetCommandLine`函数之后。 按照以往的经验,调用`main`函数至少在调用`GetCommandLine`函数之后。
@ -138,13 +137,13 @@ int main(int argc,char* argv[])
![image-20220327220810610](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327220810610.png) ![image-20220327220810610](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327220810610.png)
看到这里可以push了3个参数发现argv和argc的字样那么下一个call十有八九会到达`main`函数了。 看到这里可以 push 了 3 个参数,发现 argv 和 argc 的字样,那么下一个 call 十有八九会到达`main`函数了。
按F7单步进入。 F7 单步进入。
![image-20220327220839595](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327220839595.png) ![image-20220327220839595](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327220839595.png)
来到这里就已经很明显了,可以明显的看到下图中由`mov ..., 0x5`和`mov ..., 0x6`两个语句,这明显是在给变量赋值上5和6那么就可以推测`call test.00DD135C`实际上在调用`add`函数,将光标移动指令处,按回车键。 来到这里就已经很明显了,可以明显的看到下图中由`mov ..., 0x5`和`mov ..., 0x6`两个语句,这明显是在给变量赋值上 5 和 6那么就可以推测`call test.00DD135C`实际上在调用`add`函数,将光标移动指令处,按回车键。
![image-20220327220903701](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327220903701.png) ![image-20220327220903701](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327220903701.png)
@ -152,19 +151,19 @@ int main(int argc,char* argv[])
![image-20220327220934315](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327220934315.png) ![image-20220327220934315](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327220934315.png)
那么回到之前的`main`函数,可以看到代码将0x5和0x6放入rax和ecx寄存器后,又对其进行了压栈操作,实际等价于`push 0x5`和`push 0x6`。 那么回到之前的`main`函数,可以看到代码将 0x5 和 0x6 放入 rax 和 ecx 寄存器后,又对其进行了压栈操作,实际等价于`push 0x5`和`push 0x6`。
![image-20220327221004603](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327221004603.png) ![image-20220327221004603](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327221004603.png)
push操作就是x86架构下典型的压栈方式符合**__cdecl**调用约定(C/C++程序的默认调用约定,在此不作赘述),在该约定下,可以看到压栈顺序是逆序的,右边的参数先进栈,左边的参数后进栈,栈顶指针esp指向栈中第1个可用的数据项。 push 操作就是 x86 架构下典型的压栈方式,符合**\_\_cdecl**调用约定(C/C++程序的默认调用约定,在此不作赘述),在该约定下,可以看到压栈顺序是逆序的,右边的参数先进栈,左边的参数后进栈,栈顶指针 esp 指向栈中第 1 个可用的数据项。
在调用函数时,调用者依次将参数压入栈,然后调用函数。函数被调用以后,在栈中取得数据并进行计算,函数计算结束后,由调用者或者函数本身修改栈,使栈恢复原样(平衡栈数据)。 在调用函数时,调用者依次将参数压入栈,然后调用函数。函数被调用以后,在栈中取得数据并进行计算,函数计算结束后,由调用者或者函数本身修改栈,使栈恢复原样(平衡栈数据)。
现在将程序运行到call指令之前查看OD的栈区数据可以看到显示Arg1=5Arg2=6显然这两个参数已经被压栈。 现在将程序运行到 call 指令之前,查看 OD 的栈区数据,可以看到显示 Arg1=5Arg2=6显然这两个参数已经被压栈。
![image-20220327221037201](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327221037201.png) ![image-20220327221037201](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327221037201.png)
进入add函数后可以看到程序将arg1存入eax寄存器再和arg2相加就完成了计算。 进入 add 函数后,可以看到程序将 arg1 存入 eax 寄存器,再和 arg2 相加,就完成了计算。
![image-20220327221100245](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327221100245.png) ![image-20220327221100245](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327221100245.png)
@ -185,11 +184,11 @@ int add(int x,int y)
} }
``` ```
进入OD再次回到add函数中。 进入 OD再次回到 add 函数中。
![image-20220327221142426](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327221142426.png) ![image-20220327221142426](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327221142426.png)
`mov eax, [local.2]`是将计算的最后结果就保存在eax寄存器中eax就作为存放返回值的寄存器。 `mov eax, [local.2]`是将计算的最后结果就保存在 eax 寄存器中eax 就作为存放返回值的寄存器。
![image-20220327221206970](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327221206970.png) ![image-20220327221206970](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327221206970.png)
@ -214,15 +213,15 @@ int main(int argc,char* argv[])
} }
``` ```
这里定义了一个`max`函数,接收`a`和`b`的地址,将其中较大数放入变量`a`中。使用OD进入`main`函数。 这里定义了一个`max`函数,接收`a`和`b`的地址,将其中较大数放入变量`a`中。使用 OD进入`main`函数。
![image-20220327220253195](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327220253195.png) ![image-20220327220253195](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327220253195.png)
进入`max`函数,`mov eax, [arg.1]`和`mov ecx, [arg.2]`是将参数`a`和`b`的值加载到两个寄存器。 进入`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 所在的内存地址处。
![image-20220327220315921](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327220315921.png) ![image-20220327220315921](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327220315921.png)
@ -254,9 +253,9 @@ int main(void)
} }
``` ```
进入`add`函数,`sub esp,0xcc`即下降栈顶0xcc个字节实际上是为局部变量开辟空间同时也在预防栈溢出攻击(一种攻击手法,此处不作赘述)。 进入`add`函数,`sub esp,0xcc`即下降栈顶 0xcc 个字节,实际上是为局部变量开辟空间,同时也在预防栈溢出攻击(一种攻击手法,此处不作赘述)。
这里开辟后的空间一部分是用来存放变量z的。 这里开辟后的空间一部分是用来存放变量 z 的。
![image-20220327220210074](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327220210074.png) ![image-20220327220210074](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327220210074.png)
@ -297,11 +296,11 @@ int main(void)
} }
``` ```
这里是对z赋值直接用`mov`将7写入一个固定的内存地址。 这里是对 z 赋值,直接用`mov`将 7 写入一个固定的内存地址。
![image-20220327220033227](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327220033227.png) ![image-20220327220033227](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327220033227.png)
在`add`函数中,同样直接从固定的地址中取出`z`的值到eax寄存器中。 在`add`函数中,同样直接从固定的地址中取出`z`的值到 eax 寄存器中。
![image-20220327220014288](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327220014288.png) ![image-20220327220014288](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327220014288.png)
@ -335,7 +334,6 @@ int main(void)
![image-20220327215920733](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327215920733.png) ![image-20220327215920733](./assets/c10e40ab02e343e3afa9fb19b5619042/image-20220327215920733.png)
在内存中数组可存在于栈、数据段及动态内存中,本例中`a[]`数组就保存在数据段.data中其寻址用“基址+偏移量”实现。 在内存中数组可存在于栈、数据段及动态内存中,本例中`a[]`数组就保存在数据段.data 中,其寻址用“基址+偏移量”实现。
`b[]`数组放在栈中,这些栈在编译时分配。数组在声明时可以直接计算偏移地址,针对数组成员寻址时是采用实际的偏移量完成的。 `b[]`数组放在栈中,这些栈在编译时分配。数组在声明时可以直接计算偏移地址,针对数组成员寻址时是采用实际的偏移量完成的。

View file

@ -1,6 +1,7 @@
--- ---
layout: "../../../../../layouts/MarkdownPost.astro"
title: 写C语言的种种工具 title: 写C语言的种种工具
date: 2022-03-09 22:37:34 pubDate: 2022-03-09 22:37:34
tid: tools-to-write-c tid: tools-to-write-c
categories: categories:
- 技术 - 技术

View file

@ -1,28 +1,28 @@
--- ---
layout: "../../../../../layouts/MarkdownPost.astro"
title: Go语言极限入门 title: Go语言极限入门
date: 2022-04-19 10:11:26 pubDate: 2022-04-19 10:11:26
categories: categories:
- 技术 - 技术
- Go语言 - Go语言
cover: ./assets/5973f095d19f42f3b0649a0e2f52f006/image-20220419101405456.png cover: ./assets/5973f095d19f42f3b0649a0e2f52f006/image-20220419101405456.png
tid: go-first-tutorial tid: go-first-tutorial
description: Go语言入门教程。 description: Go语言入门教程。
permalink: /pages/bd144d/ permalink: /pages/bd144d/
author: author:
name: N3ptune name: N3ptune
link: https://www.cnblogs.com/N3ptune link: https://www.cnblogs.com/N3ptune
tags: tags:
- -
--- ---
## Go语言极限入门 ## Go 语言极限入门
> 参考书目: 《Go程序设计语言》 > 参考书目: 《Go 程序设计语言》
>
#### 快速入门 #### 快速入门
如下是hello world程序 如下是 hello world 程序:
```go ```go
// hello.go // hello.go
@ -37,19 +37,19 @@ func main() {
终端执行 `go run hello.go` 终端执行 `go run hello.go`
Go代码是用包来组织的包类似于其他语言中的库和模块。 Go 代码是用包来组织的,包类似于其他语言中的库和模块。
`package main`指明了这个文件属于哪个包。 `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 ```go
// echo.go 输出命令行参数 // echo.go 输出命令行参数
@ -76,9 +76,9 @@ $ go build echo.go
hello hello
``` ```
var 关键字声明了两个string类型的变量s和sep。变量可以声明的时候初始化。如果变量没有明确地初始化它将隐式初始化这个类型的空值。 var 关键字声明了两个 string 类型的变量 s 和 sep。变量可以声明的时候初始化。如果变量没有明确地初始化它将隐式初始化这个类型的空值。
for 是 go里面唯一的循环语句。 for 是 go 里面唯一的循环语句。
```go ```go
for initlization; condition; post { for initlization; condition; post {
@ -86,11 +86,11 @@ for initlization; condition; post {
} }
``` ```
可选的initialization(初始化)语句在循环开始之前执行。如果存在它必须是一个简单的语句。三部分都是可省的如果三部分都不存在只有一个for那就是无限循环。 可选的 initialization(初始化)语句在循环开始之前执行。如果存在,它必须是一个简单的语句。三部分都是可省的,如果三部分都不存在,只有一个 for那就是无限循环。
另一种形式的for循环是在字符串或slice数据上迭代。 另一种形式的 for 循环是在字符串或 slice 数据上迭代。
如下是第二种echo程序 如下是第二种 echo 程序:
```go ```go
// echo.go // echo.go
@ -111,9 +111,9 @@ func main() {
} }
``` ```
每一次迭代range都产生一对值: 索引和这个索引处元素的值。因为这个例子里用不到索引但是语法上range循环需要处理。应次也必须处理索引。可以将索引赋予一个临时变量然后忽略它但是**go不允许存在无用的变量**。选择使用**空标识符**"__"。空标识符可以用在任何语法需要变量名但逻辑不需要的地方。 每一次迭代range 都产生一对值: 索引和这个索引处元素的值。因为这个例子里用不到索引,但是语法上 range 循环需要处理。应次也必须处理索引。可以将索引赋予一个临时变量,然后忽略它,但是**go 不允许存在无用的变量**。选择使用**空标识符**"\_\_"。空标识符可以用在任何语法需要变量名但逻辑不需要的地方。
如果有大量的数据要处理这样做的代价会比较大。可以使用strings包中的`Join`函数。 如果有大量的数据要处理,这样做的代价会比较大。可以使用 strings 包中的`Join`函数。
```go ```go
package main package main
@ -131,7 +131,7 @@ func main() {
###### 找出重复行 ###### 找出重复行
如下程序要输出标准输入中出现次数大于1的行,前面是次数。 如下程序要输出标准输入中出现次数大于 1 的行,前面是次数。
```go ```go
package main 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)` `counts := make(map[string]int)`
每次从输入读取一行内容这一行就作为map中的键对应的值递增1。键在map中不存在时也是没有问题的。为了输出结果使用基于range的for循环。 每次从输入读取一行内容,这一行就作为 map 中的键,对应的值递增 1。键在 map 中不存在时也是没有问题的。为了输出结果,使用基于 range 的 for 循环。
bufio包使用它可以简便和高效地处理输入和输出。其中一个最有用的特性是称为扫描器(Scanner)的类型,可以读取输入,以行或者单词为单位断开。 bufio 包,使用它可以简便和高效地处理输入和输出。其中一个最有用的特性是称为扫描器(Scanner)的类型,可以读取输入,以行或者单词为单位断开。
`input := bufio.NewScanner(os.Stdin)` `input := bufio.NewScanner(os.Stdin)`
Printf函数有超过10个转义字符 Printf 函数有超过 10 个转义字符:
| verb | 描述 | | verb | 描述 |
| -------- | ---------------------------- | | -------- | ---------------------------- |
@ -247,7 +247,7 @@ $ ./main test.txt
上述程序是采用"流式"模式读取输入,然后按需拆分为行。 上述程序是采用"流式"模式读取输入,然后按需拆分为行。
这里引入一个ReadFile函数(从io/ioutil包导入)它读取整个命名文件的内容还引入一个strings.Split函数将一个字符串分割为一个由子串组成的slice 这里引入一个 ReadFile 函数(从 io/ioutil 包导入),它读取整个命名文件的内容,还引入一个 strings.Split 函数,将一个字符串分割为一个由子串组成的 slice
```go ```go
package main package main
@ -279,11 +279,11 @@ func main() {
} }
``` ```
ReadFile函数返回一个可以转化成字符串的字节slice这样它可以被strings.Split分割。 ReadFile 函数返回一个可以转化成字符串的字节 slice这样它可以被 strings.Split 分割。
###### **获取一个URL** ###### **获取一个 URL**
Go提供了一系列包在net包下面分组管理使用它们可以方便地通过互联网发送和接受信息。 Go 提供了一系列包,在 net 包下面分组管理,使用它们可以方便地通过互联网发送和接受信息。
```go ```go
package main 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 ```go
package main package main
@ -345,7 +345,7 @@ func main() {
} }
``` ```
关闭Body数据流来避免资源泄露。 关闭 Body 数据流来避免资源泄露。
运行结果: 运行结果:
@ -396,7 +396,7 @@ func main() {
} }
``` ```
###### 并发获取多个URL ###### 并发获取多个 URL
```go ```go
package main package main
@ -449,15 +449,15 @@ $ ./fetchall http://www.baidu.com http://www.qq.com
0.14s elapsed 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 ```go
package main package main
@ -485,7 +485,7 @@ $ ./fetch http://localhost:8000/help
URL.Path = "/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 Count 2
``` ```
这个服务器有两个处理函数通过请求的URL来决定哪一个被调用: 请求/count调用counter其他的调用handler。 这个服务器有两个处理函数,通过请求的 URL 来决定哪一个被调用: 请求/count 调用 counter其他的调用 handler。
以/结尾的处理模式匹配所有含有这个前缀的URL。在后台对于每个传入的请求服务器在不同的goroutine中运行该处理函数这样它可以同时处理多个请求。 以/结尾的处理模式匹配所有含有这个前缀的 URL。在后台对于每个传入的请求服务器在不同的 goroutine 中运行该处理函数,这样它可以同时处理多个请求。
然而如果两个并发的请求试图同时更新计数值countcount可能会不一致地增加程序会产生一个严重的竞态BUG。为了避免该问题必须确保最多只有一个goroutine在同一时间访问变量这正是mu.Lock()和mu.Unlock()语句的作用。 然而,如果两个并发的请求试图同时更新计数值 countcount 可能会不一致地增加,程序会产生一个严重的竞态 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 // 输出: boiling point = 212 F or 100 C
``` ```
常量boilingF是一个包级别的声明(main包)f和c是属于main函数的局部变量。包级别的实体名字不仅对于包含其声明的源文件可见,而且对于同一个包里面的所有源文件可见。 常量 boilingF 是一个包级别的声明(main 包)f 和 c 是属于 main 函数的局部变量。包级别的实体名字不仅对于包含其声明的源文件可见,而且对于同一个包里面的所有源文件可见。
另一方面,局部声明仅仅是在声明所在的函数内部可见,并且可能对于函数中的一小块区域可见。 另一方面,局部声明仅仅是在声明所在的函数内部可见,并且可能对于函数中的一小块区域可见。
**函数的声明**包含一个名字、参数列表(由函数的调用者提供的变量)、一个可选的返回值列表,以及函数体。 **函数的声明**包含一个名字、参数列表(由函数的调用者提供的变量)、一个可选的返回值列表,以及函数体。
下面的函数fToC封装了温度转换的逻辑这样可以只定义一次而在多个地方使用。 下面的函数 fToC 封装了温度转换的逻辑,这样可以只定义一次而在多个地方使用。
```go ```go
package main package main
@ -644,17 +644,17 @@ func fToC(f float64) float64 {
类型和表达式部分可以省略一个,但不能都省略。 类型和表达式部分可以省略一个,但不能都省略。
如果类型省略它的类型将由初始化表达式决定。如果表达式省略其初始值对应于类型的零值因此Go中不存在未初始化变量。 如果类型省略,它的类型将由初始化表达式决定。如果表达式省略,其初始值对应于类型的零值,因此 Go 中不存在未初始化变量。
###### 短变量声明 ###### 短变量声明
在函数中,一种称作**短变量声明**的可选形式可以用来初始化局部变量。 在函数中,一种称作**短变量声明**的可选形式可以用来初始化局部变量。
形式: `name := expression`name的类型由expression的类型来决定。 形式: `name := expression`name 的类型由 expression 的类型来决定。
在局部变量的声明和初始化主要使用短声明。 在局部变量的声明和初始化主要使用短声明。
var声明通常是为那些跟初始化表达式类型不一致的局部变量保留的或者用于后面才对变量赋值以及变量初始值不重要的情况。 var 声明通常是为那些跟初始化表达式类型不一致的局部变量保留的,或者用于后面才对变量赋值以及变量初始值不重要的情况。
```go ```go
i := 100 i := 100
@ -666,10 +666,10 @@ i,j := 0,1
指针的值是一个变量的地址。 指针的值是一个变量的地址。
如果一个变量声明为`var x int`,表达式&x获取一个指向整型变量的指针。 如果一个变量声明为`var x int`,表达式&x 获取一个指向整型变量的指针。
```go ```go
x := 1 x := 1
p := &x // p 是整型指针 只想x p := &x // p 是整型指针 只想x
fmt.Println(*p) // "1" fmt.Println(*p) // "1"
*p = 2 // 等价于x = 2 *p = 2 // 等价于x = 2
@ -678,7 +678,7 @@ fmt.Println(x) // 结果"2"
每个聚合类型变量的组成都是变量,所以也有一个地址。 每个聚合类型变量的组成都是变量,所以也有一个地址。
指针类型的零值是nil。 指针类型的零值是 nil。
函数可以返回局部变量的地址。 函数可以返回局部变量的地址。
@ -704,7 +704,7 @@ incr(&v) // v 等于 2
fmt.Println(incr(&v)) // "3" fmt.Println(incr(&v)) // "3"
``` ```
指针对于flag包是很关键的它使用程序的命令行参数来设置整个程序内某些变量的值。 指针对于 flag 包是很关键的,它使用程序的命令行参数来设置整个程序内某些变量的值。
```go ```go
package main 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)来结束程序。
运行示例: 运行示例:

View file

@ -1,18 +1,19 @@
--- ---
layout: "../../../../../layouts/MarkdownPost.astro"
title: Linux笔记 - 信号 title: Linux笔记 - 信号
date: 2022-04-05 13:13:28 pubDate: 2022-04-05 13:13:28
categories: categories:
- 技术 - 技术
- Linux - Linux
cover: ./assets/dfc39c2948664aa78df4a4b4aa11bb2c/20220320105749.png cover: ./assets/dfc39c2948664aa78df4a4b4aa11bb2c/20220320105749.png
tid: linux-note-signal tid: linux-note-signal
description: 有关linux信号的一系列笔记。 description: 有关linux信号的一系列笔记。
permalink: /pages/aff776/ permalink: /pages/aff776/
author: author:
name: N3ptune name: N3ptune
link: https://www.cnblogs.com/N3ptune link: https://www.cnblogs.com/N3ptune
tags: tags:
- -
--- ---
## **Linux 信号** ## **Linux 信号**
@ -20,11 +21,10 @@ tags:
信号本质上是整数,用户模式下用来模拟硬件中断。 信号本质上是整数,用户模式下用来模拟硬件中断。
- 什么是硬件中断?先理解为硬件引发的中断。 - 什么是硬件中断?先理解为硬件引发的中断。
- 什么是中断简言之就是让CPU停下当前干的事转而去处理新的情况。 - 什么是中断?简言之,就是让 CPU 停下当前干的事转而去处理新的情况。
- 信号是谁产生的?可以由硬件、内核和进程产生。 - 信号是谁产生的?可以由硬件、内核和进程产生。
例如在终端上用`Ctrl+C`,可以结束掉当前终端进程,本质上是发送 SIGINT 信号。
例如在终端上用`Ctrl+C`可以结束掉当前终端进程本质上是发送SIGINT信号。
如下是一个实例程序,该程序循环打印数字。运行起来,看看`Ctrl+C`能不能中止它: 如下是一个实例程序,该程序循环打印数字。运行起来,看看`Ctrl+C`能不能中止它:
@ -52,15 +52,15 @@ int main()
} }
``` ```
如下所示现在使用Ctrl+C是没办法中断这个程序的 如下所示,现在使用 Ctrl+C 是没办法中断这个程序的:
![signal-1](./assets/dfc39c2948664aa78df4a4b4aa11bb2c/signal-1.png) ![signal-1](./assets/dfc39c2948664aa78df4a4b4aa11bb2c/signal-1.png)
这里可以用man命令查看一下signal函数的帮助文档 这里可以用 man 命令查看一下 signal 函数的帮助文档:
![signal-2](./assets/dfc39c2948664aa78df4a4b4aa11bb2c/signal-2.png) ![signal-2](./assets/dfc39c2948664aa78df4a4b4aa11bb2c/signal-2.png)
signal关联上了一个信号处理函数来处理信号。 signal 关联上了一个信号处理函数,来处理信号。
现在修改这个代码,添加一行打印当前进程号,然后打开两个终端,一个终端运行上面的程序,一个终端尝试`kill`命令来发送信号: 现在修改这个代码,添加一行打印当前进程号,然后打开两个终端,一个终端运行上面的程序,一个终端尝试`kill`命令来发送信号:
@ -74,13 +74,13 @@ kill 要指定参数:
**信号处理的过程** **信号处理的过程**
进程A在运行内核、硬件或者其他进程发送信号给进程A。进程A接收到信号后直接做信号本身规定的对应处理或者做事先注册好的信号处理。如上面signal函数就是注册信号处理hand函数替换了本身默认的信号处理。当然信号是可以屏蔽的不作任何处理。 进程 A 在运行,内核、硬件或者其他进程发送信号给进程 A。进程 A 接收到信号后,直接做信号本身规定的对应处理或者做事先注册好的信号处理。如上面 signal 函数就是注册信号处理hand 函数替换了本身默认的信号处理。当然信号是可以屏蔽的,不作任何处理。
Linux有哪些信号类型 Linux 有哪些信号类型:
![signal-5](./assets/dfc39c2948664aa78df4a4b4aa11bb2c/signal-5.png) ![signal-5](./assets/dfc39c2948664aa78df4a4b4aa11bb2c/signal-5.png)
Linux 信号有64个分为不可靠信号(非实时1-31Unix提供)和可靠信号(32-64后来扩充的)。又有系统自带的标准信号和用户自定义的信号。 Linux 信号有 64 个,分为不可靠信号(非实时1-31Unix 提供)和可靠信号(32-64后来扩充的)。又有系统自带的标准信号和用户自定义的信号。
介绍几个命令或函数: 介绍几个命令或函数:
@ -183,7 +183,7 @@ int main(void)
#include <stdlib.h> #include <stdlib.h>
int main(int argc,char* argv[]) int main(int argc,char* argv[])
{ {
int pid = atoi(argv[1]); int pid = atoi(argv[1]);
int sig = atoi(argv[2]); int sig = atoi(argv[2]);
printf("pid:%d sig:%d\n",argv[1],argv[2]); printf("pid:%d sig:%d\n",argv[1],argv[2]);
@ -204,11 +204,11 @@ int main(int argc,char* argv[])
也试试发送其他数据,联合体中还有一个指针类型的成员。 也试试发送其他数据,联合体中还有一个指针类型的成员。
## Linux信号(续) ## Linux 信号(续)
> 参考网站: Github、《极客时间》 > 参考网站: Github、《极客时间》
现在从底层角度来观赏Linux信号机制。 现在从底层角度来观赏 Linux 信号机制。
首先看信号的具体作用: 首先看信号的具体作用:
@ -216,13 +216,13 @@ int main(int argc,char* argv[])
![signal-15](./assets/dfc39c2948664aa78df4a4b4aa11bb2c/signal-15.png) ![signal-15](./assets/dfc39c2948664aa78df4a4b4aa11bb2c/signal-15.png)
如上文所提,处理信号有3种方式:执行默认操作,捕获信号和忽略信号 如上文所提,处理信号有 3 种方式:执行默认操作,捕获信号和忽略信号
设置信号处理有两种方式: signalsigaction 设置信号处理有两种方式: signalsigaction
signal将一个动作和信号关联sigaction也是将一个动作和信号关联但这个动作用一个结构体表示了处理信号将更加细致 signal 将一个动作和信号关联sigaction 也是将一个动作和信号关联,但这个动作用一个结构体表示了,处理信号将更加细致
siganal不是系统调用而是glibc封装的一个函数实现如下 siganal 不是系统调用,而是 glibc 封装的一个函数,实现如下:
```c ```c
# define signal __sysv_signal # define signal __sysv_signal
@ -242,9 +242,9 @@ __sysv_signal (int sig, __sighandler_t handler)
weak_alias (__sysv_signal, sysv_signal) 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。信号的到来时间是不可预期的有可能程序正在调用某个漫长的系统调用的时候突然到来一个信号中断了这个系统调用去执行信号处理函数。那么执行完后信号处理函数系统调用怎么办 还有一个设置就是设置了 SA_INTERRUPT清除了 SA_RESTART。信号的到来时间是不可预期的有可能程序正在调用某个漫长的系统调用的时候突然到来一个信号中断了这个系统调用去执行信号处理函数。那么执行完后信号处理函数系统调用怎么办
@ -252,9 +252,9 @@ weak_alias (__sysv_signal, sysv_signal)
另外一种处理方法是 SA_RESTART。这个时候系统调用会被自动重新启动不需要调用方自己写代码。当然也可能存在问题例如从终端读入一个字符这个时候用户在终端输入一个'a'字符,在处理'a'字符的时候被信号中断了,等信号处理完毕,再次读入一个字符的时候,如果用户不再输入,就停在那里了,需要用户再次输入同一个字符。 另外一种处理方法是 SA_RESTART。这个时候系统调用会被自动重新启动不需要调用方自己写代码。当然也可能存在问题例如从终端读入一个字符这个时候用户在终端输入一个'a'字符,在处理'a'字符的时候被信号中断了,等信号处理完毕,再次读入一个字符的时候,如果用户不再输入,就停在那里了,需要用户再次输入同一个字符。
可知signal函数是受到限制的因此建议使用 sigaction 函数,根据自己的需要定制参数。 可知 signal 函数是受到限制的,因此,建议使用 sigaction 函数,根据自己的需要定制参数。
下面是glibc中的实现 下面是 glibc 中的实现:
```c ```c
int int

View file

@ -1,18 +1,19 @@
--- ---
layout: "../../../../../layouts/MarkdownPost.astro"
title: Linux笔记 - 文件I/O title: Linux笔记 - 文件I/O
date: 2022-03-19 22:38:56 pubDate: 2022-03-19 22:38:56
categories: categories:
- 技术 - 技术
- Linux - Linux
cover: ./assets/cbea6761a0164906b8396942cf61c664/20220320105749.png cover: ./assets/cbea6761a0164906b8396942cf61c664/20220320105749.png
tid: linux-note-file-io tid: linux-note-file-io
description: 有关linux文件io的一系列笔记。 description: 有关linux文件io的一系列笔记。
permalink: /pages/82d273/ permalink: /pages/82d273/
author: author:
name: N3ptune name: N3ptune
link: https://www.cnblogs.com/N3ptune link: https://www.cnblogs.com/N3ptune
tags: tags:
- -
--- ---
## Linux 文件系统结构 ## Linux 文件系统结构

View file

@ -1,21 +1,22 @@
--- ---
layout: "../../../../../layouts/MarkdownPost.astro"
title: Linux笔记 - 进程 title: Linux笔记 - 进程
date: 2022-04-05 13:13:35 pubDate: 2022-04-05 13:13:35
categories: categories:
- 技术 - 技术
- Linux - Linux
cover: ./assets/f835945d5de246bcabdff51dd984aaf2/20220320105749.png cover: ./assets/f835945d5de246bcabdff51dd984aaf2/20220320105749.png
tid: linux-note-process tid: linux-note-process
description: 有关linux进程的一系列笔记。 description: 有关linux进程的一系列笔记。
permalink: /pages/324bec/ permalink: /pages/324bec/
author: author:
name: N3ptune name: N3ptune
link: https://www.cnblogs.com/N3ptune link: https://www.cnblogs.com/N3ptune
tags: tags:
- -
--- ---
## Linux进程 ## Linux 进程
本文会简单讲述进程创建和进程间通信。 本文会简单讲述进程创建和进程间通信。
@ -39,11 +40,11 @@ tags:
![process-4](./assets/f835945d5de246bcabdff51dd984aaf2/process-4.png) ![process-4](./assets/f835945d5de246bcabdff51dd984aaf2/process-4.png)
在终端输入`top`,输入q可以退出: 在终端输入`top`,输入 q 可以退出:
![process-5](./assets/f835945d5de246bcabdff51dd984aaf2/process-5.png) ![process-5](./assets/f835945d5de246bcabdff51dd984aaf2/process-5.png)
Linux系统上可以管理多个进程进程被分时分片处理。 Linux 系统上可以管理多个进程,进程被分时分片处理。
下面演示在程序中如何创建进程: 下面演示在程序中如何创建进程:
@ -95,9 +96,9 @@ int main(void)
![process-10](./assets/f835945d5de246bcabdff51dd984aaf2/process-10.png) ![process-10](./assets/f835945d5de246bcabdff51dd984aaf2/process-10.png)
根据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
![process-11](./assets/f835945d5de246bcabdff51dd984aaf2/process-11.png) ![process-11](./assets/f835945d5de246bcabdff51dd984aaf2/process-11.png)
@ -140,7 +141,7 @@ int main(void)
有一种情况,父进程创建了子进程,父进程先于子进程结束,子进程资源没有被释放,就会变成僵尸进程,持续占用系统资源(内核中进程树会保存进程的数据,树中节点会保存进程的一些数据)。 有一种情况,父进程创建了子进程,父进程先于子进程结束,子进程资源没有被释放,就会变成僵尸进程,持续占用系统资源(内核中进程树会保存进程的数据,树中节点会保存进程的一些数据)。
子进程结束前会向父进程发送SIGCHILD信号父进程收到信号后回收子进程资源然后父进程再结束。父进程可以写一个wait函数等待子进程发送SIGCHILD信号。 子进程结束前,会向父进程发送 SIGCHILD 信号,父进程收到信号后,回收子进程资源,然后父进程再结束。父进程可以写一个 wait 函数,等待子进程发送 SIGCHILD 信号。
![process-12](./assets/f835945d5de246bcabdff51dd984aaf2/process-12.png) ![process-12](./assets/f835945d5de246bcabdff51dd984aaf2/process-12.png)
@ -166,7 +167,7 @@ int main(void) {
} }
``` ```
使用wait函数就是要等待子进程打印完所有数字父进程才结束。 使用 wait 函数就是要等待子进程打印完所有数字,父进程才结束。
最后看看守护进程。 最后看看守护进程。
@ -176,11 +177,11 @@ int main(void) {
![process-13](./assets/f835945d5de246bcabdff51dd984aaf2/process-13.png) ![process-13](./assets/f835945d5de246bcabdff51dd984aaf2/process-13.png)
TPGID为-1的话就说明是守护进程。 TPGID 为-1 的话,就说明是守护进程。
如果要把一个进程变成守护进程,要先`kill`其父进程,同时摆脱终端的控制。 如果要把一个进程变成守护进程,要先`kill`其父进程,同时摆脱终端的控制。
要摆脱终端的控制就要关闭三个文件描述符号标准输入设备标准输出设备标准错误输出设备然后重定向当前进程IO操作到/dev/null (黑洞设备)。然后要创建新的会话,摆脱原有会话进程组的控制。 要摆脱终端的控制,就要关闭三个文件描述符号:标准输入设备,标准输出设备,标准错误输出设备,然后重定向当前进程 IO 操作到/dev/null (黑洞设备)。然后要创建新的会话,摆脱原有会话进程组的控制。
这里要提到进程的组织形式:多个进程组成一个进程组,多个进程组组成一个会话。这里不详细解释会话是什么。 这里要提到进程的组织形式:多个进程组成一个进程组,多个进程组组成一个会话。这里不详细解释会话是什么。
@ -198,11 +199,11 @@ TPGID为-1的话就说明是守护进程。
第二种: 第二种:
    1. 重设文件权限 `umask;` 1. 重设文件权限 `umask;`
    2. 创建子进程 `fork`;     2. 创建子进程 `fork`;
    3. 结束父进程;     3. 结束父进程;
    4. 创建新会话 `setsid;`     4. 创建新会话 `setsid;`
    5. 防止子进程成为僵尸进程 忽略SIGCHILD SIGUP信号;     5. 防止子进程成为僵尸进程 忽略 SIGCHILD SIGUP 信号;
    6. 改变当前工作目录 `chdir`;     6. 改变当前工作目录 `chdir`;
    7. 重定向文件描述符号 `open dup(fd,0) dup(fd,1)` .     7. 重定向文件描述符号 `open dup(fd,0) dup(fd,1)` .
@ -275,7 +276,7 @@ int main(void)
上文提到,`fork`子进程会拷贝父进程的数据,因此父子进程间通信还是比较简单的。 上文提到,`fork`子进程会拷贝父进程的数据,因此父子进程间通信还是比较简单的。
第一种通信方式,较为朴素,使用普通文件,进程A将要传递的信息放入这个文件进程B再去读这个文件即可。父子进程间可通过文件描述符号,非父子进程之间就只能通过具体文件来通信。 第一种通信方式,较为朴素,使用普通文件,进程 A 将要传递的信息放入这个文件,进程 B 再去读这个文件即可。父子进程间可通过文件描述符号,非父子进程之间就只能通过具体文件来通信。
第二种方式,文件映射虚拟内存 `mmap` 第二种方式,文件映射虚拟内存 `mmap`
@ -283,7 +284,7 @@ int main(void)
除此之外,还有信号、共享内存、消息队列、信号量和网络可用于通信。 除此之外,还有信号、共享内存、消息队列、信号量和网络可用于通信。
本文主要讲前3种。 本文主要讲前 3 种。
下面代码简单演示了第一种: 下面代码简单演示了第一种:
@ -306,7 +307,7 @@ int main(void)
exit(-1); exit(-1);
} }
printf("创建文件成功\n"); printf("创建文件成功\n");
if (fork()) if (fork())
{ {
int n = 0; int n = 0;
while(true) { while(true) {
@ -317,14 +318,14 @@ int main(void)
n++; n++;
} }
} }
else else
{ {
int m; int m;
while(true) { while(true) {
sleep(1); sleep(1);
int fd = open("test.dat",O_RDONLY); int fd = open("test.dat",O_RDONLY);
read(fd,&m,4); read(fd,&m,4);
close(fd); close(fd);
printf(">> %d\n",m); printf(">> %d\n",m);
} }
} }
@ -337,7 +338,7 @@ int main(void)
下面讨论管道。 下面讨论管道。
管道也是FIFO结构分为两种匿名管道和有名管道。 管道也是 FIFO 结构,分为两种,匿名管道和有名管道。
父子进程使用匿名管道。 父子进程使用匿名管道。
@ -396,7 +397,7 @@ int main(void)
接着演示有名管道,流程如下: 接着演示有名管道,流程如下:
| 进程A | 进程B | | 进程 A | 进程 B |
| ------------------- | ------------------ | | ------------------- | ------------------ |
| 创建管道文件 mkfifo | | | 创建管道文件 mkfifo | |
| 打开管道文件 | 打开管道文件 | | 打开管道文件 | 打开管道文件 |
@ -407,7 +408,7 @@ int main(void)
![process-19](./assets/f835945d5de246bcabdff51dd984aaf2/process-19.png) ![process-19](./assets/f835945d5de246bcabdff51dd984aaf2/process-19.png)
进程A的代码: 进程 A 的代码:
```c ```c
#include <unistd.h> #include <unistd.h>
@ -450,7 +451,7 @@ int main(void)
} }
``` ```
进程B 进程 B
```c ```c
#include <unistd.h> #include <unistd.h>

Some files were not shown because too many files have changed in this diff Show more