分类目录归档:技术

解决 Fedora 无法访问码云私有库问题

本文首发于:https://blog.frytea.com/archives/526/

近期在 Fedora 33 系统上总是无法访问 gitee 私有库,得到以下报错:

$ git clone git@gitee.com:songtianlun/workflow.git
正克隆到 'workflow'...
git@gitee.com: Permission denied (publickey).
fatal: 无法读取远程仓库。

请确认您有正确的访问权限并且仓库存在。

使用 ssh -v git@gitee.com 命令检查得到以下输出:

$ ssh -v git@gitee.com
OpenSSH_8.4p1, OpenSSL 1.1.1g FIPS  21 Apr 2020
...
debug1: send_pubkey_test: no mutual signature algorithm
...
git@gitee.com: Permission denied (publickey).

这条日志引起我的注意,搜索了一下发现有人遇到跟我一样的问题,初步判定为 Fedora 的漏洞引起,目前发现在 Fedora 33 系统与 gitee 通信会出现问题,解决方法很简单:

修改”~/.ssh/config”文件,添加如下配置(若没有该目录则新建),实测有用:

Host gitee.com
PubkeyAcceptedKeyTypes=+ssh-rsa

问题暂时得到解决,引发问题的根本原因还需要进一步探索。

参考文献

为 Windows 10 新增物理网卡子接口

本文首发于:https://blog.frytea.com/archives/525/

在进行网络设备调试时常常会使用到多 IP ,Linux 上可以很方便的为物理网卡配置子接口解决,但是在 Windows 上如果物理网卡必须使用 DHCP 就无法再配置多 IP 了,只能暂时的使用 USB 转网卡解决。近期突然灵机一动,能不能使用 Windows 10 天然支持的 WSL 来解决这个问题,经过本人实测,是可以的,方法也很简单。

环境为 Win 10 专业版,安装了 WSL Ubuntu,具体安装方法请自行百度。

安装完毕后进入 WSL, CMD 里输入 ubuntu 即可,使用 ifconfig 先看一下当前的网卡:

ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 30.30.253.241  netmask 255.255.255.240  broadcast 30.30.253.255
        inet6 fe80::5d98:1dfb:7083:f7ed  prefixlen 64  scopeid 0xfd<compat,link,site,host>
        ether 6c:4b:90:d4:a9:20  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

其中的 eth0 就是对应的 win10 物理网卡在 WSL 中的映射。

使用以下命令为该网卡配置一个子接口:

sudo ip addr add 192.168.8.123/24 dev eth0 label eth0:1

此时使用 ifconfig 看不到这个子接口,但是实际上已经生效:

可以发现在 Win10 上 ping 一个该网段 IP ,配置该子接口后立刻就通了,说明配置完成。

疑难解答

如果您配置时得到下面报错:

RTNETLINK answers: Permission denied

使用管理员权限打开 CMD,运行 ubuntuwsl ,之后再配置即可成功。(感谢 GITHUB 社区)

拓展阅读

三步激活 StarUML V4.0.1 (WIN10为例) | 仅供学习

StarUML 是一个开源的 UML 工具列表软件,它遵守 GNU GPL 的一个修订版。StarUML 项目宣称的目标是代替大型的商业 UML 工具软件,如 IBMRational RoseBorland 公司的 TogetherStarUML 支持 UML2.0 定义的大多数图,是一款跨平台且轻量高效的现代 UML 绘图工具。

但由于其商业性质,免费版仅提供一段时间的试用预览,到期后导出图片会有 未注册 字样水印,非常影响成图质量。经过一番研究,StarUML 采用 Electron 框架,使用 node.js 实现,仅需简单修改其中许可证判定代码即可完成破解,在此感谢万能的 GitHub 社区。

本教程激活 StarUML V4.0.1 ,平台为 Win10

学(ji)习(huo)步骤

第一步,解包

app.asar 文件是 Electron 程序的主业务文件,是一种压缩格式的文件。我们需要修改的部分就被压缩在这里,具体文件位置为:

C:\\Program Files\\StarUML
├─locales
├─resources
| └─app.asar
└─swiftshader

app.asar 文件可以使用编辑器直接打开,但如果直接修改会导致程序无法正常运行,因此需要解包修改再压缩。

解包前需要确认您的电脑已经安装 node.js ,可在 CMD 执行以下命令,若回显版本号说明已安装,若没有安装请移步:https://nodejs.org/en/

C:\\Program Files>node -v
v12.18.3

之后全局安装 asar 工具:

npm install -g asar
或者
cnpm install -g asar

C:\\Program Files>asar -V
v3.0.3

// 出现版本号说明安装成功

解压 app.asar 文件:

asar extract app.asar ./asar/

使用上面命令将 app.asar 解压到同级目录 asar 下,前提是 cd 到文件所在目录,并创建好 asar 文件。

2021年9月26日更新 实测 Ubuntu 20.04 下 StartUML 4.1.2 成功

$ sudo find / -name "app.asar"
/opt/StarUML/resources/app.asar

第二步,激活

解压后在 asar 目录下,找到这个文件:asar\\src\\engine\\license-manager.js,使用你偏好的编辑器打开,修改其中这段代码:

  checkLicenseValidity () {
    this.validate().then(() => {
      setStatus(this, true)
    }, () => {
      //setStatus(this,false)  <-- comment this line
      setStatus(this, true) //<-- add this line
      //UnregisteredDialog.showDialog() <-- comment this line
    })
  }

注意其中注释的部分,总结来看就是将 false 改为 true,再将 false 的后续动作注释即可。

MaxOS, Linux 类比修改,下面是我在 GitHub 找到的 v3.0.* 文件位置,待验证。。。 Mac OS: /Applications/StarUML.app/Contents/www/license/node/ Linux: /opt/staruml/www/license/node/

第三步,压缩

修改完成后,将修改后的内容重新打包回 app.asar ,使用以下命令压缩即可,其中 pack 是我前一步解压的目录:

asar pack asar app.asar

注:建议在此前备份旧的 app.asar 文件,以免造成无法挽回的损失。

若不出意外,到这里 StarUML 就已经成功激活了。

https://imagehost-cdn.frytea.com/images/2021/04/07/20210407210104e7b6dba384d8b246.png

[tip type=”info” title=”资源”] 为方便有需求的朋友,在这里直接给出我修改好的 app.aspr 及对应版本的安装包,安装好后替换 app.aspr 即可。

[file href=”https://res.frytea.com/Dev/%E5%AE%9E%E7%94%A8%E5%B7%A5%E5%85%B7/StarUML_with_aspr.zip?download”]StarUML_with_aspr.zip[/file]

若链接失效,请留言,或前往我的 资源站 查找。

[/tip]

总结

本文介绍了一种简单的直接使用官网版本激活 StarUML 的方法,避免了不明来源的破解程序对你电脑的侵害。

特别说明:本方法仅用于学习用途,请勿商用,若产生纠纷本文作者概不负责。

参考文献

Windows CMD 换肤

由于经常使用到 Windows 下的 CMD 终端进行各种操作,特别是集成了 Ubuntu 子系统之后,在 Windows 平台上进行 C 开发更方便了。为了呼出 WSL 子系统,我习惯在 CMD 下进行,可默认皮肤不够美观,为了让自己心情愉悦,检索了一番换肤方法,并在此简单记录。

环境准备

首先下载换肤工具:ColorTool

解压后在 CMD 下进入解压后的目录,运行例如以下命令即可:


C:\Users\lenovo\Downloads\ColorTool>ColorTool.exe -b schemes\OneHalfDark.itermcolors

效果预览

目录中提供了几种配色,在这里简单展示一下效果:

  • deuteranopia.itermcolors

  • OneHalfDark.itermcolors

  • OneHalfLight.itermcolors

  • solarized_dark.itermcolors

  • solarized_light.itermcolors

个人比较喜欢 OneHalfDark.itermcolors 这一款,你最喜欢哪一个呢?

参考文献

解决 Win10 热点无法上网及与 Hyper-V 虚拟网卡冲突问题

最近给 Win10 电脑加装了一个无线网卡,想要开热点使用,但是发现手机连上之后显示无法联网,在这里记录解决办法。

首先查看一下当前电脑上网使用的网卡是哪一张,我当前使用的是 Hyper-V 虚拟的一个桥接网卡 vEthernet (NetBridge) 开启 wifi 热点后新增的网卡是 本地连接* 12

现在进入上网网卡的属性,进入共享标签,发现 Internet 连接共享是关闭状态,现在将它打开,并将家庭网络连接指定为 wifi 热点的网卡,确认即可。

再次尝试连接即可。

补充:开启连接共享时报错 “无法启用 internet 连接共享,为 lan 连接配置的 ip 地址需要使用自动 ip 寻址” ,将不需要的 Vmware 虚拟网卡卸载后解决,猜测可能是占用了某些共享需要用到的网段导致。

参考文献

NMAP 基础扫描

以下几个示例带领你快速了解nmap的基本扫描方法,更多详情请查阅nmap手册。

1、Nmap 简单扫描

$ nmap <target ip address>
$ nmap 192.168.41.41

直接指定需要扫描的主机IP开始扫描,返回详细描述。

2、Nmap 扫描并输出详细信息

$ nmap <target ip address> -vv
$ nmap 192.168.41.41 -vv

3、Nmap 指定端口范围扫描

$ nmap -p(range) <target IP>
# (rangge)为要扫描的端口(范围),端口大小不能超过65535

# nmap 192.168.41.41 -p1-50
Starting Nmap 7.80 ( https://nmap.org ) at 2021-01-26 16:37 CST
Nmap scan report for bogon (192.168.41.41)
Host is up (0.0038s latency).
Not shown: 46 closed ports
PORT   STATE    SERVICE
5/tcp  filtered rje
21/tcp filtered ftp
23/tcp open     telnet
27/tcp filtered nsw-fe
MAC Address: 00:11:22:33:44:41 (Cimsys)

Nmap done: 1 IP address (1 host up) scanned in 7.99 seconds

4、Nmap 指定端口扫描

$ nmap -p(port1,port2,port3,...) <target ip> 

$ nmap -p23 192.168.41.41
Starting Nmap 7.80 ( https://nmap.org ) at 2021-01-26 16:44 CST
Nmap scan report for bogon (192.168.41.41)
Host is up (0.0083s latency).

PORT   STATE SERVICE
23/tcp open  telnet
MAC Address: 00:11:22:33:44:41 (Cimsys)

Nmap done: 1 IP address (1 host up) scanned in 0.04 seconds

5、Nmap Ping 扫描

$ nmap -sP <target ip> 
$ nmap -sP 192.168.41.41
Starting Nmap 7.80 ( https://nmap.org ) at 2021-01-26 16:45 CST
Nmap scan report for bogon (192.168.41.41)
Host is up (0.0064s latency).
MAC Address: 00:11:22:33:44:41 (Cimsys)
Nmap done: 1 IP address (1 host up) scanned in 0.02 seconds

6、Nmap路由跟踪

$ nmap --traceroute <target ip> 
$  nmap --traceroute 119.29.29.29
Starting Nmap 7.80 ( https://nmap.org ) at 2021-01-26 16:50 CST
Nmap scan report for pdns.dnspod.cn (119.29.29.29)
Host is up (0.019s latency).
Not shown: 998 filtered ports
PORT   STATE SERVICE
53/tcp open  domain
80/tcp open  http

TRACEROUTE (using port 80/tcp)
HOP RTT      ADDRESS
1   1.99 ms  bogon (192.168.6.1)
2   2.71 ms  bogon (192.168.169.9)
3   2.71 ms  bogon (192.168.169.1)
4   19.82 ms hn.kd.ny.adsl (123.14.80.1)
5   4.74 ms  hn.kd.ny.adsl (125.40.240.77)
6   6.17 ms  pc93.zz.ha.cn (61.168.37.93)
7   ... 8
9   44.19 ms no-data (125.39.198.178)
10  ... 19
20  19.46 ms pdns.dnspod.cn (119.29.29.29)

Nmap done: 1 IP address (1 host up) scanned in 10.55 seconds

7 Nmap 扫描某网段IP

$ nmap -sP <network address > </CIDR >
$ nmap -sP 192.168.8.0/24
Starting Nmap 7.80 ( https://nmap.org ) at 2021-01-26 16:53 CST
Nmap scan report for bogon (192.168.8.129)
Host is up (0.00084s latency).
Nmap done: 256 IP addresses (1 host up) scanned in 19.87 seconds

8、Nmap 探测操作系统类别

$ nmap -O <target ip> 
$  nmap -O 119.29.29.29
Starting Nmap 7.80 ( https://nmap.org ) at 2021-01-26 16:59 CST
Nmap scan report for pdns.dnspod.cn (119.29.29.29)
Host is up (0.021s latency).
Not shown: 998 filtered ports
PORT   STATE SERVICE
53/tcp open  domain
80/tcp open  http
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: specialized|media device|WAP
Running (JUST GUESSING): Linux 2.6.X|3.X (88%), Hikvision embedded (88%)
OS CPE: cpe:/o:linux:linux_kernel:2.6.10 cpe:/h:hikvision:ds-7600 cpe:/o:linux:linux_kernel:3.0 cpe:/o:linux:linux_kernel:3.10
Aggressive OS guesses: HIKVISION DS-7600 Linux Embedded NVR (Linux 2.6.10) (88%), Linux 3.0 (88%), DD-WRT v24-sp2 (Linux 3.10) (87%)
No exact OS matches for host (test conditions non-ideal).

OS detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 8.49 seconds

9、Nmap 万能扫描

此选项设置包含了1-10000的端口ping扫描,操作系统扫描,脚本扫描,路由跟踪,服务探测。 命令语法:

$ nmap -A <target ip>
$ nmap -A 119.29.29.29
Starting Nmap 7.80 ( https://nmap.org ) at 2021-01-26 17:08 CST
Nmap scan report for pdns.dnspod.cn (119.29.29.29)
Host is up (0.020s latency).
Not shown: 998 filtered ports
PORT   STATE SERVICE VERSION
53/tcp open  domain  ISC BIND 9.11.3
| dns-nsid:
|_  bind.version: 9.11.3
80/tcp open  http    Http Server
| fingerprint-strings:
|   FourOhFourRequest, GetRequest:
|     HTTP/1.0 404 Not Found
|     Connection: close
|     Server: Http Server
|     Content-Type: text/html
|     Content-Length: 174
|     
<html>
|     
<head><title>Server error message</title></head>
|     
<body>
|     
<h1>Error 404: Not Found</h1>
|     request could not be completed because:
|     Document not found!
|     </body>
|_    </html>
|_http-server-header: Http Server
|_http-title: Server error message
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port80-TCP:V=7.80%I=7%D=1/26%Time=600FDC05%P=x86_64-redhat-linux-gnu%r(
SF:GetRequest,11E,"HTTP/1\.0\x20404\x20Not\x20Found\r\nConnection:\x20clos
SF:e\r\nServer:\x20Http\x20Server\r\nContent-Type:\x20text/html\r\nContent
SF:-Length:\x20174\r\n\r\n
<html>\n<head><title>Server\x20error\x20message<
SF:/title></head>\n<body>\n<h1>Error\x20404:\x20Not\x20Found</h1>\nThe\x20
SF:request\x20could\x20not\x20be\x20completed\x20because:\n\x20Document\x2
SF:0not\x20found!\n</body>\n</html>\n")%r(FourOhFourRequest,11E,"HTTP/1\.0
SF:\x20404\x20Not\x20Found\r\nConnection:\x20close\r\nServer:\x20Http\x20S
SF:erver\r\nContent-Type:\x20text/html\r\nContent-Length:\x20174\r\n\r\n<h
SF:tml>\n
<head><title>Server\x20error\x20message</title></head>\n<body>\n<
SF:h1>Error\x20404:\x20Not\x20Found</h1>\nThe\x20request\x20could\x20not\x
SF:20be\x20completed\x20because:\n\x20Document\x20not\x20found!\n</body>\n
SF:</html>\n");
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: media device|specialized|WAP|security-misc
Running (JUST GUESSING): Linux 3.X|2.6.X (88%), Hikvision embedded (87%), Imperva SecureSphere (86%)
OS CPE: cpe:/o:linux:linux_kernel:3.0 cpe:/o:linux:linux_kernel:2.6.10 cpe:/h:hikvision:ds-7600 cpe:/o:linux:linux_kernel:3.10 cpe:/o:imperva:securesphere
Aggressive OS guesses: Linux 3.0 (88%), HIKVISION DS-7600 Linux Embedded NVR (Linux 2.6.10) (87%), DD-WRT v24-sp2 (Linux 3.10) (87%), Linux 2.6.32 (86%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 20 hops

TRACEROUTE (using port 53/tcp)
HOP RTT      ADDRESS
1   1.83 ms  bogon (192.168.6.1)
2   2.69 ms  bogon (192.168.169.9)
3   2.12 ms  bogon (192.168.169.1)
4   4.01 ms  hn.kd.ny.adsl (123.14.80.1)
5   3.74 ms  hn.kd.ny.adsl (125.40.240.77)
6   5.07 ms  pc73.zz.ha.cn (61.168.37.73)
7   ...
8   17.91 ms dns2.online.tj.cn (117.8.222.2)
9   30.42 ms no-data (125.39.198.178)
10  ... 19
20  20.12 ms pdns.dnspod.cn (119.29.29.29)

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 164.97 seconds

10、Nmap 命令混合式扫描

$ nmap -vv -p1-1000 -O <target ip>
$ nmap -vv -p1-50 -O 192.168.41.41
Starting Nmap 7.80 ( https://nmap.org ) at 2021-01-26 17:12 CST
Initiating ARP Ping Scan at 17:12
Scanning 192.168.41.41 [1 port]
Completed ARP Ping Scan at 17:12, 0.01s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 17:12
Completed Parallel DNS resolution of 1 host. at 17:12, 0.00s elapsed
Initiating SYN Stealth Scan at 17:12
Scanning bogon (192.168.41.41) [50 ports]
Discovered open port 23/tcp on 192.168.41.41
Increasing send delay for 192.168.41.41 from 0 to 5 due to 13 out of 43 dropped probes since last increase.
Completed SYN Stealth Scan at 17:12, 4.52s elapsed (50 total ports)
Initiating OS detection (try #1) against bogon (192.168.41.41)
Retrying OS detection (try #2) against bogon (192.168.41.41)
Retrying OS detection (try #3) against bogon (192.168.41.41)
Retrying OS detection (try #4) against bogon (192.168.41.41)
Retrying OS detection (try #5) against bogon (192.168.41.41)
Nmap scan report for bogon (192.168.41.41)
Host is up, received arp-response (0.0030s latency).
Scanned at 2021-01-26 17:12:37 CST for 16s

PORT   STATE    SERVICE     REASON
1/tcp  closed   tcpmux      reset ttl 64
2/tcp  filtered compressnet no-response
3/tcp  closed   compressnet reset ttl 64
4/tcp  closed   unknown     reset ttl 64
5/tcp  closed   rje         reset ttl 64
6/tcp  closed   unknown     reset ttl 64
7/tcp  closed   echo        reset ttl 64
8/tcp  closed   unknown     reset ttl 64
9/tcp  closed   discard     reset ttl 64
10/tcp closed   unknown     reset ttl 64
11/tcp closed   systat      reset ttl 64
12/tcp closed   unknown     reset ttl 64
13/tcp closed   daytime     reset ttl 64
14/tcp closed   unknown     reset ttl 64
15/tcp closed   netstat     reset ttl 64
16/tcp closed   unknown     reset ttl 64
17/tcp closed   qotd        reset ttl 64
18/tcp closed   msp         reset ttl 64
19/tcp closed   chargen     reset ttl 64
20/tcp closed   ftp-data    reset ttl 64
21/tcp closed   ftp         reset ttl 64
22/tcp closed   ssh         reset ttl 64
23/tcp open     telnet      syn-ack ttl 64
24/tcp closed   priv-mail   reset ttl 64
25/tcp closed   smtp        reset ttl 64
26/tcp closed   rsftp       reset ttl 64
27/tcp closed   nsw-fe      reset ttl 64
28/tcp closed   unknown     reset ttl 64
29/tcp closed   msg-icp     reset ttl 64
30/tcp closed   unknown     reset ttl 64
31/tcp closed   msg-auth    reset ttl 64
32/tcp closed   unknown     reset ttl 64
33/tcp closed   dsp         reset ttl 64
34/tcp closed   unknown     reset ttl 64
35/tcp closed   priv-print  reset ttl 64
36/tcp closed   unknown     reset ttl 64
37/tcp closed   time        reset ttl 64
38/tcp closed   rap         reset ttl 64
39/tcp closed   rlp         reset ttl 64
40/tcp closed   unknown     reset ttl 64
41/tcp closed   graphics    reset ttl 64
42/tcp closed   nameserver  reset ttl 64
43/tcp closed   whois       reset ttl 64
44/tcp closed   mpm-flags   reset ttl 64
45/tcp closed   mpm         reset ttl 64
46/tcp closed   mpm-snd     reset ttl 64
47/tcp closed   ni-ftp      reset ttl 64
48/tcp filtered auditd      no-response
49/tcp closed   tacacs      reset ttl 64
50/tcp closed   re-mail-ck  reset ttl 64
MAC Address: 00:11:22:33:44:41 (Cimsys)
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=7.80%E=4%D=1/26%OT=23%CT=1%CU=37223%PV=Y%DS=1%DC=D%G=Y%M=001122%T
OS:M=600FDD15%P=x86_64-redhat-linux-gnu)SEQ(SP=105%GCD=1%ISR=10C%TI=Z%CI=RD
OS:%TS=A)SEQ(SP=104%GCD=1%ISR=10C%TI=Z%CI=RI%II=I%TS=A)OPS(O1=M5A0ST11NW7%O
OS:2=M5A0ST11NW7%O3=M5A0NNT11NW7%O4=M5A0ST11NW7%O5=M5A0ST11NW7%O6=M5A0ST11)
OS:WIN(W1=6F90%W2=6F90%W3=6F90%W4=6F90%W5=6F90%W6=6F90)ECN(R=Y%DF=Y%T=40%W=
OS:7080%O=M5A0NNSNW7%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=AS%RD=0%Q=)T2(R=Y%
OS:DF=Y%T=2B%W=80%S=A%A=Z%F=R%O=WANM109T10S%RD=0%Q=)T3(R=N)T4(R=Y%DF=Y%T=38
OS:%W=400%S=A%A=Z%F=R%O=WANM109T10S%RD=0%Q=)T5(R=Y%DF=Y%T=40%W=0%S=A%A=S+%F
OS:=AR%O=%RD=0%Q=)T5(R=N)T6(R=Y%DF=Y%T=34%W=8000%S=A%A=Z%F=R%O=WANM109T10S%
OS:RD=0%Q=)T7(R=Y%DF=N%T=26%W=FFFF%S=A%A=Z%F=R%O=WFNM109T10S%RD=0%Q=)U1(R=Y
OS:%DF=N%T=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T
OS:=40%CD=S)

Uptime guess: 34.815 days (since Tue Dec 22 21:38:45 2020)
Network Distance: 1 hop
TCP Sequence Prediction: Difficulty=261 (Good luck!)
IP ID Sequence Generation: All zeros

Read data files from: /usr/bin/../share/nmap
OS detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 15.95 seconds
           Raw packets sent: 187 (12.130KB) | Rcvd: 159 (10.202KB)

参考文献

Linux 多进程通信开发之 UNIX domain Socket 通信机制从 TCP 切换为 UDP

Unix domain socket 或者 IPC socket是一种终端,可以使同一台操作系统上的两个或多个进程进行数据通信。提供 UDP 和 TCP 两种通信机制。

因为一些原因,项目现有软件架构采用的都是 IPC sockte 中的 TCP 通信机制,虽然保证了通信的可靠性,但近期需要对该程序进行热迁移(基于 criu ),有连接的 IPC 套接字状态很难被保存和恢复,而 无连接的 UDP 只需要保证服务端先冻结、先恢复即可实现程序整体状态迁移,因此写下本文,记录迁移过程,最后提供示例程序,可以自行通过 BCompare 等文本对比工具对比差异。

模式差异

至于两种通信机制的差异,除了在创建套接字时声明的差异,具体的通信流程也有差异,在这里找到 CSDN @frank909 的两张图片,感觉很有代表性,在这里引用一下。

首先是 TCP的:

https://imagehost-cdn.frytea.com/images/2021/01/13/202101131815253d04d69f377dae01.png

https://imagehost-cdn.frytea.com/images/2021/01/13/202101131815253d04d69f377dae01.png

接下来是 UDP 的:

https://imagehost-cdn.frytea.com/images/2021/01/13/2021011318145069f28463e2e1709c.png

二者的差异,主要是这几点:

  • 服务端:

(1)TCP连接在 bind() 之后需要 listen() ,而UDP不需要。

(2)TCP连接在 bind()listen() 后还需要 accept(),用来得到客户端连接描述符,而UDP不需要。

(3)收发数据,TCP使用 recv() , send() , 而 UDP 使用 recvfrom(), sendto()

(4)释放连接,TCP在客户端释放连接后需先释放 accept() 得到的客户端连接描述符,再 close(socket) ,而UDP直接 close(socket)

  • 客户端:

(1)TCP连接在bind()后需要 connect(),而UDP不需要。

(2)收发数据,TCP使用 recv() , send() , 而 UDP 使用 recvfrom(), sendto()

具体的差异可以查看下方的示例程序。

参考程序

TCP-Server

/*************************************************************************
  > File Name: ipc_tcp_server.cpp
  > Author: TianLun Song
  > Mail: songtianlun@frytea.com
  > Blog: https://blog.frytea.com
  > Created Time: Wed 13 Jan 2021 09:53:06 AM CST
 ************************************************************************/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <memory.h>
#include <unistd.h>
#include <stdio.h>
#include 
<iostream>

using namespace std;
const char* server_file = "/tmp/ipc_tcp_server.sock";

int main(int argc,char** argv)
{
    int socket_fd = socket(AF_UNIX,SOCK_STREAM,0);
    if (socket_fd < 0)
    {
        perror("socket");
        return -1;
    }
    struct sockaddr_un addr;
    memset(&addr,0,sizeof(addr));
    addr.sun_family = AF_UNIX;
    strcpy(addr.sun_path,server_file);
    if (access(addr.sun_path,0) != -1)
    {
        remove(addr.sun_path);
    }
    if (bind(socket_fd,(sockaddr*)&addr,sizeof(addr)) < 0)
    {
        perror("bind");
        return -1;
    }
    /* --------DIFF, ipc tcp only------------ */
    if (listen(socket_fd,12) < 0)
    {
        perror("listen");
        return -1;
    }
    /* ---------------end------------ */
    struct sockaddr_un clientaddr;
    socklen_t addrlen = sizeof(clientaddr);
    char msg_buf[1024];
    /* --------DIFF, ipc tcp only------------ */
    int newcon = -1;
    newcon = accept(socket_fd,(sockaddr*)&clientaddr,&addrlen);
    if (newcon < 0)
    {
        perror("accept");
        return -1;
    }
    /* ---------------end------------ */
    while (1)
    {
        memset(msg_buf,'\0',1024);
        int rsize = recv(newcon,msg_buf,sizeof(msg_buf),0);
        if (rsize < 0)
        {
            perror("server recv error!");
            break;
        }
        cout << "I'm Unix socket(TCP) server, recv a msg:" << msg_buf << " from: " << clientaddr.sun_path << endl;
        strcpy(msg_buf, "OK,I got it!");
        int ssize = send(newcon, msg_buf, sizeof msg_buf, 0);
        if (ssize < 0)
        {
            perror("server send error!");
            break;
        }
        sleep(1);
    }
    /* --------DIFF, ipc tcp only------------ */
    if (close(newcon) < 0)
    {
        perror("close accept");
        return -1;
    }
    /* ---------------end------------ */
    if (close(socket_fd) < 0)
    {
        perror("close socket");
        return -1;
    }
    return 0;
}

TCP-Client

/*************************************************************************
  > File Name: ipc_tcp_client.cpp
  > Author: TianLun Song
  > Mail: songtianlun@frytea.com
  > Blog: https://blog.frytea.com
  > Created Time: Wed 13 Jan 2021 10:14:09 AM CST
 ************************************************************************/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <memory.h>
#include <unistd.h>
#include <stdio.h>
#include 
<iostream>

using namespace std;

const char* server_file = "/tmp/ipc_tcp_server.sock";
const char* client_file = "/tmp/ipc_tcp_client.sock";

int main(int argc,char** argv)
{
    int socket_fd = socket(AF_UNIX,SOCK_STREAM,0);
    if (socket_fd < 0)
    {
        perror("client socket");
        return -1;
    }
    struct sockaddr_un client_addr;
    memset(&client_addr,0,sizeof(client_addr));
    client_addr.sun_family = AF_UNIX;
    strcpy(client_addr.sun_path,client_file);

    if (access(client_addr.sun_path,0) != -1)
    {
        remove(client_addr.sun_path);
    }

    if(bind(socket_fd,(sockaddr*)&client_addr,sizeof(client_addr)) < 0)
    {
        perror("client bind");
        return -1;
    }
    struct sockaddr_un serveraddr;
    memset(&serveraddr,0,sizeof(serveraddr));
    socklen_t addrlen = sizeof(serveraddr);
    serveraddr.sun_family = AF_UNIX;
    strcpy(serveraddr.sun_path,server_file);
    char msg_buf[1024];
    /* --------DIFF, ipc tcp only------------ */
    int newcon = -1;
    newcon = connect(socket_fd,(sockaddr*)&serveraddr,addrlen);
    if (newcon < 0){
        perror("client connect");
    }
    /* ---------------end------------ */
    while(1)
    {
        strcpy(msg_buf, "How are you !!!");
        int ssize = send(socket_fd, msg_buf, sizeof msg_buf, 0);
        if (ssize < 0)
        {
            perror("client send");
            continue;
        }
        int rsize = recv(socket_fd, msg_buf, sizeof(msg_buf), 0);
        if (rsize < 0)
        {
            perror("client recv");
            continue;
        }
        cout << "I'm Unix socket(TCP) client,receive a msg :" << msg_buf << endl;
        sleep(1);
    }
    if (close(socket_fd) < 0)
    {
        perror("close");
        return -1;
    }
    return 0;
}

UDP-Server

/*************************************************************************
  > File Name: ipc_udp_server.cpp
  > Author: TianLun Song
  > Mail: songtianlun@frytea.com
  > Blog: https://blog.frytea.com
  > Created Time: Wed 13 Jan 2021 09:53:06 AM CST
 ************************************************************************/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <memory.h>
#include <unistd.h>
#include <stdio.h>
#include 
<iostream>

using namespace std;
const char* server_file = "/tmp/ipc_udp_server.sock";

int main(int argc,char** argv)
{
    int socket_fd = socket(AF_UNIX,SOCK_DGRAM,0);
    if (socket_fd < 0)
    {
        perror("socket");
        return -1;
    }
    struct sockaddr_un addr;
    memset(&addr,0,sizeof(addr));
    addr.sun_family = AF_UNIX;
    strcpy(addr.sun_path,server_file);
    if (access(addr.sun_path,0) != -1)
    {
        remove(addr.sun_path);
    }
    if (bind(socket_fd,(sockaddr*)&addr,sizeof(addr)) < 0)
    {
        perror("bind");
        return -1;
    }
    struct sockaddr_un clientaddr;
    socklen_t addrlen = sizeof(clientaddr);
    char msg_buf[1024];
    while (1)
    {
        memset(msg_buf,'\0',1024);
        int rsize = recvfrom(socket_fd,msg_buf,sizeof(msg_buf),0,(sockaddr*)&clientaddr,&addrlen);
        if (rsize < 0)
        {
            perror("server recv error!");
            break;
        }
        cout << "I'm Unix socket(UDP) server, recv a msg: " << msg_buf  << " from: " << clientaddr.sun_path << endl;
        strcpy(msg_buf, "OK,I got it!");
        int ssize = sendto(socket_fd, msg_buf, sizeof msg_buf,0,(sockaddr*)&clientaddr,addrlen);
        if (ssize < 0)
        {
            perror("server send error!");
            break;
        }
        sleep(1);
    }
    if (close(socket_fd) < 0)
    {
        perror("close socket");
        return -1;
    }
    return 0;
}

UDP-Client

/*************************************************************************
  > File Name: ipc_udp_client.cpp
  > Author: TianLun Song
  > Mail: songtianlun@frytea.com
  > Blog: https://blog.frytea.com
  > Created Time: Wed 13 Jan 2021 10:14:09 AM CST
 ************************************************************************/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <memory.h>
#include <unistd.h>
#include <stdio.h>
#include 
<iostream>

using namespace std;

const char* server_file = "/tmp/ipc_udp_server.sock";
const char* client_file = "/tmp/ipc_udp_client.sock";

int main(int argc,char** argv)
{
    int socket_fd = socket(AF_UNIX,SOCK_DGRAM,0);
    if (socket_fd < 0)
    {
        perror("client socket");
        return -1;
    }
    struct sockaddr_un client_addr;
    memset(&client_addr,0,sizeof(client_addr));
    client_addr.sun_family = AF_UNIX;
    strcpy(client_addr.sun_path,client_file);

    if (access(client_addr.sun_path,0) != -1)
    {
        remove(client_addr.sun_path);
    }

    if(bind(socket_fd,(sockaddr*)&client_addr,sizeof(client_addr)) < 0)
    {
        perror("client bind");
        return -1;
    }
    struct sockaddr_un serveraddr;
    memset(&serveraddr,0,sizeof(serveraddr));
    socklen_t addrlen = sizeof(serveraddr);
    serveraddr.sun_family = AF_UNIX;
    strcpy(serveraddr.sun_path,server_file);
    char msg_buf[1024];
    while(1)
    {
        strcpy(msg_buf, "How are you !!!");
        int ssize = sendto(socket_fd, msg_buf, sizeof msg_buf, 0, (sockaddr*)&serveraddr,addrlen);
        if (ssize < 0)
        {
            perror("client sendto");
            continue;
        }
        int rsize = recvfrom(socket_fd, msg_buf, sizeof(msg_buf), 0,(sockaddr*)&serveraddr, &addrlen);
        if (rsize < 0)
        {
            perror("client recv");
            continue;
        }
        cout << "I'm Unix socket(TCP) client,receive a msg :" << msg_buf << endl;
        sleep(1);
    }
    if (close(socket_fd) < 0)
    {
        perror("close");
        return -1;
    }
    return 0;
}

源文件

ipc_test.zip

参考文献

无需代理直接加速各种 GitHub 资源拉取 | 国内镜像赋能 | 助力开发

常见的github加速方法如修改 hosts 文件、魔法上网、设置 proxy 等方法在此不在赘述,本文主要介绍在不进行多余网络配置的情况下,直接使用提供了github国内镜像服务的网站进行github各种资源拉取加速,在这里向各位提供github国内镜像服务的大佬们致敬。

加速地址一览

加速 clone

# 方法一:手动替换地址
#原地址
$ git clone https://github.com/kubernetes/kubernetes.git
#改为
$ git clone https://github.com.cnpmjs.org/kubernetes/kubernetes.git
#或者
$ git clone https://hub.fastgit.org/kubernetes/kubernetes.git
#或者
$ git clone https://gitclone.com/github.com/kubernetes/kubernetes.git

# 方法二:配置git自动替换
$ git config --global url."https://hub.fastgit.org".insteadOf https://github.com
# 测试
$ git clone https://github.com/kubernetes/kubernetes.git
# 查看git配置信息
$ git config --global --list
# 取消设置
$ git config --global --unset url.https://github.com/.insteadof

加速 release

# 原地址
wget https://github.com/goharbor/harbor/releases/download/v2.0.2/harbor-offline-installer-v2.0.2.tgz
# 加速下载方法一
wget https://download.fastgit.org/goharbor/harbor/releases/download/v2.0.2/harbor-offline-installer-v2.0.2.tgz
# 加速下载方法二
wget https://hub.fastgit.org/goharbor/harbor/releases/download/v2.0.2/harbor-offline-installer-v2.0.2.tgz

加速 raw

# 原地址
$ wget https://raw.githubusercontent.com/kubernetes/kubernetes/master/README.md
# 加速下载方法一
$ wget https://raw.staticdn.net/kubernetes/kubernetes/master/README.md
# 加速下载方法二
$ wget https://raw.fastgit.org/kubernetes/kubernetes/master/README.md

参考文献

一种C程序使用IPC多进程共享内存并实现热迁移的方法

在软件中,术语共享内存指可被多个进程存取的内存,一个进程是一段程序的单个运行实例。在这种情况下,共享内存被用作进程间的通讯。——WikiPedia

在Linux系统中,有多种C语言支持的共享内存使用方法,包括以下几种:

  1. 基于传统 SYS V 的共享内存;
  2. 基于 POSIX mmap 文件映射实现共享内存;
  3. 通过 memfd_create()fd 跨进程共享实现共享内存;
  4. 多媒体、图形领域广泛使用的基于 dma-buf 的共享内存。

CRIU 是用于 Linux 操作系统的软件工具。使用此工具,可以冻结正在运行的应用程序,并将其作为文件集合检查点到持久性存储中。然后,人们可以使用这些文件从冻结点还原并运行应用程序。但不是所有程序都支持通过CRIU进行热迁移,例如使用了 SYS V 的C程序就不可以使用 CRIU 进行 进程热迁移。

这篇文章讨论如何使用CRIU迁移使用了共享内存的程序,主要讨论其中的前两种共享内存方法,最终介绍一种支持热迁移的C程序共享内存使用方法

共享简单实现

System V ,曾经也被称为 AT&T System V,是Unix操作系统众多版本中的一支, SYS V 共享内存历史悠久、年代久远、API怪异,对应内核代码 linux/ipc/shm.c ,使用命令 ipcs 看到的就是这种内存; Posix 表示可移植操作系统接口(Portable Operating System Interface ,缩写为 POSIX ),POSIX标准定义了操作系统应该为应用程序提供的接口标准,是IEEE为要在各种 UNIX 操作系统上运行的软件而定义的一系列API标准的总称,其正式称呼为IEEE 1003,而国际标准名称为ISO/IEC 9945。

下面列举了两种共享内存的C程序使用方法。

(1)Sys V 共享内存

ipc_share_mem_write ,共享内存写入示例程序。

/*************************************************************************
    > File Name : ipc_share_mem_write.c
    > Author : TL Song
    > EMail : songtianlun@frytea.com
    > Created Time : Wed 30 Dec 2020 03:17:00 PM CST
 ************************************************************************/

#include <stdio.h>
#include <sys/shm.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char ** argv)
{
    key_t key = ftok("/dev/shm/myshm2",0);
    int shm_id = shmget(key, 0x400000, IPC_CREAT | 0666);
    char *p = (char *)shmat(shm_id, NULL, 0);
    char cIndex = 'A';
    int i = 0;
    for(i=0;i<100;i++){
        memset(p, cIndex++, 0x400000);
        printf("Write '%c' to mem '%p'\n", *p, p);
        sleep(1);
    }
    shmdt(p);
    return 0;
}

ipc_share_mem_read ,共享读取示例程序。

/*************************************************************************
    > File Name : ipc_share_mem_read.c
    > Author : TL Song
    > EMail : songtianlun@frytea.com
    > Created Time : Wed 30 Dec 2020 03:22:20 PM CST
 ************************************************************************/

#include <stdio.h>
#include <unistd.h>
#include <sys/shm.h>

int main(int argc, char ** argv)
{
    key_t key = ftok("/dev/shm/myshm2", 0);
    int shm_id = shmget(key, 0x400000, 0666);
    char *p = (char *)shmat(shm_id, NULL, 0);
    int i = 0;
    for(i = 0 ;i < 100; i++)
    {
        printf("Read '%c' in mem '%p'\n", *p, p);
        sleep(1);
    }
    shmdt(p);
    return 0;
}

直接编译运行即可:

$ gcc ipc_share_mem_write.c -o ipc_share_mem_write
$ gcc ipc_share_mem_read.c -o ipc_share_mem_read
$ touch /dev/shm/myshm2

# 在两个终端分别执行程序即可。

可以使用 free 命令查看执行前后共享内存空间的变化情况:

[root@criu_go ~]# free
              total        used        free      shared  buff/cache   available
Mem:        3880732     3193512      421660        9216      265560      461624
Swap:       4063228           0     4063228
[root@criu_go ~]# free
              total        used        free      shared  buff/cache   available
Mem:        3880732     3193744      417308       13312      269680      457292

(2)POSIX 共享内存

ipc_share_mem_posix_write ,共享内存写入示例程序。

/*************************************************************************
    > File Name : ipc_share_mem_posix_write.c
    > Author : TL Song
    > EMail : songtianlun@frytea.com
    > Created Time : Wed 30 Dec 2020 03:17:00 PM CST
 ************************************************************************/

#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char ** argv)
{
    int fd = shm_open("posixsm", O_CREAT | O_RDWR, 0666);
    ftruncate(fd, 0x400000);

    char *p = mmap(NULL, 0x400000, PROT_READ| PROT_WRITE, MAP_SHARED, fd, 0);
    char cIndex = 'A';
    int i = 0;
    for(i=0;i<100;i++){
        memset(p, cIndex++, 0x400000);
        printf("Write '%c' to mem '%p'\n", *p, p);
        sleep(1);
    }
    munmap(p, 0x400000);
    return 0;
}

ipc_share_mem_posix_read ,共享读取示例程序。

/*************************************************************************
    > File Name : ipc_share_mem_posix_read.c
    > Author : TL Song
    > EMail : songtianlun@frytea.com
    > Created Time : Wed 30 Dec 2020 03:22:20 PM CST
 ************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc, char ** argv)
{
    int fd = shm_open("posixsm", O_RDONLY, 0666);
    ftruncate(fd, 0x400000);

    char *p = mmap(NULL, 0x400000, PROT_READ, MAP_SHARED, fd, 0);
    int i = 0;
    for(i = 0 ;i < 100; i++)
    {
        printf("Read '%c' in mem '%p'\n", *p, p);
        sleep(1);
    }
    munmap(p, 0x400000);
    return 0;
}

直接编译运行即可:

$ gcc ipc_share_mem_posix_write.c -o ipc_share_mem_posix_write -lrt
$ gcc ipc_share_mem_posix_read.c -o ipc_share_mem_posix_read -lrt

# 在两个终端分别执行程序即可。

之后可以在 /dev/shm//run/shm 下面看到一个文件。

进程热迁移

上文简单提到了 criu 工具,本文的目标即迁移使用了共享内存的C程序,实测使用了 Sys V 共享内存的C程序无法迁移,报错如下:

Task 4526 with SysVIPC shmem map @7fdff5956000 doesn't live in IPC ns

使用POSIX mmap 文件映射实现共享内存的C程序可以使用 criu 实现进程热迁移,只需迁移共享内存文件及相关程序和文件即可实现本机和跨主机间的进程迁移,前提是内核、criu版本保持一致。迁移方法很简单,至于criu的安装,使用以下命令安装即可。

$ yum install criu -y 
$ criu check
Looks good.

CLI 进行进程迁移

# 获取进程 PID
$ ps -ef | grep ipc
root     15748 15340  0 10:56 pts/1    00:00:00 ./checkpoint_demo
root     15751 15479  0 10:56 pts/2    00:00:00 grep --color=auto checkpoint_demo

# 对进程打快照
$ criu dump -D ./migrate_imgs/ -j -t 15748

# 略去拷贝快照文件夹的步骤,自行拷贝至目标机器任意位置即可

# 拷贝共享内存文件至目标位置
$ scp /dev/shm/posixsm root@192.168.7.48:/dev/shm/posixsm 

# 拷贝程序文件至目标位置,注意迁移前后的程序和依赖文件的路径必须保持一致
$ scp PracticeDev/clang/ipc_test/ipc_share_mem_posix_write root@192.168.7.48:/root/PracticeDev/clang/ipc_test

# 恢复进程状态
$ criu restore -D ./migrate_imgs/ -j

使用该方法可以将使用了共享内存的C程序冻结,之后恢复进程状态,Posix 共享内存的API略有不同,但使用方法类似,至于更进一步的探索,还需继续努力。

参考文献

Docker 热迁移探索,基于CRIU实现运行状态迁移

版本限制

  • Docker: 17.06.0-ce
  • criu: 3.12
  • kernal: 3.10.0-957.el7.x86_64 || 5.10.2-1.el7.elrepo.x86_64 (Optional)
  • os: CentOS Linux release 7.9.2009 (Core) (Optional)

实测在该环境下,容器热迁移可用,热迁移成功与否与 dockercriu 版本强相关。

环境部署


# Docker 安装
$ sudo yum install -y yum-utils
$ sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo
$ sudo yum list docker-ce --showduplicates | sort -r
$ sudo yum install docker-ce-17.06.0.ce
$ sudo systemctl start docker

# 启动Docker实验特征
vim **/etc/docker/daemon.json
+ { "experimental": true }**

# criu 安装
$ sudo yum install criu -y

# Go安装(可选)
wget https://golang.org/dl/go1.15.6.linux-amd64.tar.gz
tar -C /usr/local -xzf go1.15.6.linux-amd64.tar.gz
vim /etc/profile
+ export PATH=$PATH:/usr/local/go/bin
source /etc/profile

热迁移示例

# On the host
$ docker run -d --name looper2 --security-opt seccomp:unconfined busybox \
         /bin/sh -c 'i=0; while true; do echo $i; i=$(expr $i + 1); sleep 1; done'

# wait a few seconds to give the container an opportunity to print a few lines, then
$ docker checkpoint create looper2 checkpoint2

# check your container & print log file
$ docker logs looper2

# On the client
$ docker create --name looper-clone --security-opt seccomp:unconfined busybox \
         /bin/sh -c 'i=0; while true; do echo $i; i=$(expr $i + 1); sleep 1; done'

$ docker start --checkpoint=checkpoint2 looper-clone

# check your container
$ docekr ps
$ docker logs looper-clone

以上步骤将容器 looper2 的状态迁移到 looper-clone ,可实现当前容器、跨容器的状态迁移,甚至可以实现跨主机的迁移,只需将该目录 /var/lib/docker/{docker id}/checnkpoints/ 下面与检查点同名的文件夹迁移即可。

附件

附件一:C测试程序

/*************************************************************************
    > File Name : fork_test.c
    > Author : TL Song
    > EMail : songtianlun@frytea.com
    > Created Time : Wed 23 Dec 2020 08:46:47 AM CST
 ************************************************************************/

#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>

int main(int arg,char* argv[]) {

    int i = 0;
    time_t t;
    struct tm *timeinfo;
    // int 被 typedef为 pid_t
    pid_t pid=fork();

    // 当pid==0时,是子进程代码运行区域。其他则是父进程运行区域。
    if(pid<0) {
        printf("Create child process failure ...\n");
    }else if(pid==0) {
        //子进程执行体
        printf("Hi i am child process ,my processId is %i \n",getpid());
        i = 0;
        while (1) {
            sleep(1);
            time(&t);
            timeinfo = localtime(&t);
            printf("Child: %d %s\n", i++, asctime(timeinfo));
            fflush(stdout);
            if(i>100)
                break;
        }
    }
    else{
        //父进程执行体
        printf("parent process is run ,myid is %i \n",getpid());
        i = 0;
        while (1) {
            sleep(1);
            time(&t);
            timeinfo = localtime(&t);
            printf("Main : %d %s\n", i++, asctime(timeinfo));
            fflush(stdout);
            if(i>100)
                break;
        }
    }
    // 执行体结束标志
    if(pid==0) {
        printf("pid=%i child process end ... \n",getpid());
    }
    else {
        // 睡眠5s,等待子先进程结束
        sleep(5);
        printf("pid=%i Parent process End ... \n",getpid());
    }

    return 0;
}

参考文献