Socket是计算机之间进行网络通信的一套程序接口,相当于在发送端和接收端之间建立了一个通信管道。在实际应用中,一些远程管理软件和网络安全软件大多依赖于Socket来实现特定功能,由于TCP(Transmission Control Protocol,传输控制协议)方式在网络编程中应用得非常频繁,此处给出具体应用实例。 编写TCP时一般会用到的Socket模块,其方法主要包括:
·connect(address):连接远程计算机
·send(bytes[,flags]):发送数据
·recv(bufsize[,flags]):接收数据
·bind(address):绑定地址
·listen(backlog):开始监听,等待客户端连接
·accept():响应客户端的一个请求,接受一个连接
使用TCP进行通信,首先需要在客户端和服务端建立连接,并且要在通信结束后关闭连接以释放资源。由于TCP是面向连接的,因此相对于UDP提供更高的可靠性。
Demo: 设计一个对话系统。该应用分为两个部分,一部分为服务端,一部分为客户端。客户端发送请求信息,服务端返回应答信息。两部分代码如下所示。
(1)服务端代码 server.py
# coding=UTF-8
import socket
language = {'what is your name':'I am Tom','how old are you':'25','bye':'bye!'}
HOST = "127.0.0.1"
PORT = 6666
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind((HOST,PORT))
s.listen(1)
print("Listing at port 6666")
conn,addr = s.accept()
print('Connect by:', addr)
while True:
data = conn.recv(1024)
data = data.decode()
if not data:
break
print('Received message:', data)
conn.sendall(language.get(data,'Nothing').encode())
conn.close()
s.close()
(2)客户端代码 client.py
# coding=UTF-8
import socket,sys
HOST = "127.0.0.1"
PORT = 6666
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
try:
s.connect((HOST,PORT))
except Exception as e:
print('server not found!')
sys.exit()
while True:
c = input('YOU SAY:')
s.sendall(c.encode())
data = s.recv(1024)
data = data.decode()
print('Received:',data)
if c.lower()=='再见':
break
在一个窗口运行服务端程序,服务端开始进行监听;启动一个新的窗口并运行客户端程序,服务端程序提示连接已建立;在客户端输入要发送的信息后,服务端会根据提前建立的字典进行自动回复。对话效果,如图所示:
SPDK是由英特尔发起的,用于加速NVMe SSD作为后端存储使用的应用软件加速库。这个软件库的核心是用户态,异步,轮询方式的NVMe驱动。相比内核的NVMe驱动,SPDK可以大幅降低NVMe command的延迟,提高单CPU核的IOPS,形成一套高性价比的解决方案,如SPDK的vhost解决方案可以被应用到HCI中加速虚拟机的NVMe I/O。
SPDK的核心组件之一就是用户态NVMe驱动。
用户态驱动出现的目的就是减少软件本身的开销,包括这里所说的上下文切换,系统调用等。在用户态目前可以通过UIO(Userspace I/O)或VFIO(Virtual Function I/O)两种方式对硬件固态硬盘设备进行访问。 大页(Hugepage),通过使用HugePage分配大页可以提高性能。因为页大小的增加,可以减少缺页异常。例如,2MB大小的内容(假设是2MB对齐),如果是4KB大小的页粒度,则会产生512次缺页异常,但是使用2MB大小的页,只会产生一次缺页异常。大页的缺点:需要额外配置,需要应用程序事先预估使用多少内存,大页需要在进程启动之前实现启用和分配好内存。
Linux内核NVMe驱动和SPDK NVMe驱动实现的区别:
功能比较 | 内核NVMe驱动 | SPDK NVMe驱动 |
---|---|---|
工作模式 | 中断 | 异步轮询方式 |
I/O路径是否需要同步 | CPU之间需要同步 | CPU之间以无锁方式运行 |
是否需要系统调用 | 需要 | 不需要,直接通过 |
I/O内存页管理 | DMA内存页映射 | 大页,通过hugetlbfs |
块设备支持 | 通过内核通用块设备层 | 专门为Flash优化(此外SPDK也提供用户态块设备层) |
1)异步轮询方式 SPDK用户态驱动对设备完成状态的检测,是通过异步轮询的方式来实现的,进而避免了对中断的依赖。轮询到操作完成时会触发上层的回调函数,这样使得应用程序无须等待读或写操作的完成,就可以按需发送多个请求,再由回调函数处理。由此来提高应用的读/写性能。
2)无锁化 在数据通道上去掉对锁的依赖。这里主要考虑以下问题。 ·读/写处理要在一个CPU核上完成,避免核间的缓存同步(可以通过线程亲和性的方法,将某个处理线程绑定到某个特定的核上。同时通过轮询的方式占住该核的使用,避免操作系统调度其他的线程到该核上面) ·单核上的处理,对资源的分配是无锁化的。(mempool,无锁队列,环)
3)专门为Flash来优化 通过UIO或VFIO把PCI设备的BAR(Base Address Register)地址映射到应用进程的空间,这样SPDK用户态驱动就可以遵循NVMe的规范来初始化NVMe SSD,创建出最基本的I/O发送和完成队列,最终实现对NVMe SSD设备的I/O读或写操作。
最下层为驱动层,中间层为通用块层,最上层为存储协议层。
SPDK是专门为NVMe开发的用户空间驱动,放弃原有系统调用模式,是基于以下两个原因:
(1)NVMe设备的IOPS很高,如果按照传统的系统调用,需要先从内核空间返回用户空间,这些开销对NVMe而言就比较大了;
(2)内核块层是通用的,而作为存储产品这些设备是专用的,不需要传统的I/O调度器。
创池前,nvme盘在内核态;创池后,nvme盘被spdk纳管为用户态。
(modprobe命令用于智能地向内核中加载模块或者从内核中移除模块。 modprobe可载入指定的个别模块,或是载入一组相依的模块。)
nvme.conf.in 该文件存在则代表SPDK已经生效,不存在则代表未生效。
install_spdk.sh
spdk_setup.sh
https://www.intel.cn/content/www/cn/zh/developer/articles/tool/introduction-to-the-storage-performance-development-kit-spdk.html
https://www.cnblogs.com/whl320124/articles/9969395.html
https://github.com/spdk/spdk
https://rootw.github.io/2018/05/SPDK-all/
git –version 查看git版本
which -a git
git-cmd.exe
git-bash.exe
工程准备
git init 用于在本地目录下新建git 项目仓库
//处理子仓
git submodule update –init –recursive
工程克隆:git clone
查看工作区
查看工作区的修改内容:git diff
查看工作区文件状态:git status
文件修改后提交推送
新增/删除/移动文件到暂存区:git add/ git rm/ git mv
提交更改的文件:git commit
推送远端仓库:git push origin branch_name:new_branch_name
查看日志
查看当前分支上的提交日志:git log
分支管理
列出本地分支:git branch
新建分支:git branch / git checkout -b
删除分支:git branch -d
切换分支:git checkout
更新分支:git pull
合并分支:git merge
撤销操作
强制回退到历史节点:git reset
回退本地所有修改而未提交的:git checkout
撤销commit: git reset –soft HEAD^
撤销add: git reset –hard HEAD^
清理:git reset –hard; git clean -df
分支合并
合并目标分支内容到当前分支: git merge/ git rebase
本地仓特性分支:feat
远端仓master指定commit:123456
将特性rebase到指定commit:
1.git checkout 123456
2.git checkout -b newmaster(该分支的最新commitid为:123456)
3.git checkout feat
4.git rebase 123456
5.git push origin feat –force
git status 查看当前状态,本地是否有更改的文件
git checkout master 切换分支
git pull –rebase 远程拉取代码
git checkout . && git clean -xdf 丢弃本地所有修改
git blame 小乌龟->Blame previous revision
git pull –rebase
git add .
git commit –m ”Bug: BUG”
如果需要覆盖提交,则:git commit –amend
git review
指定分支提交:
git push origin develop:dev_codecheck
git config –global user.name lzy
git config –global user.email lzy@***
///默认git的日志时区是 UTC +0000,设置为中国的时区 UTC +8000
git config –global log.date local
git config –global credential.helper store
(保存git的用户名和密码,只要输入一次以后就不用输了
git config –system –unset credential.helper 清一下缓存密码
) git config –list
\1. git branch 查看本地分支
\2. git branch -a (查看所有分支)
其中有remotes/的为远程分支
\3. git checkout –b(创建新的分支)
git add .
git stash
git checkout ***
Git status
Git stash list
git stash drop stash@{0}
git status
git checkout [filepath/filename]
git reset HEAD [filepath/filename]
git rebase –abort
git reflog
git reset commitid
撤销已修改的文件 git reset HEAD
撤销未跟踪文件 git checkout
清除所有未跟踪文件 git clean -dxf
删除当前目录下没有被track过的文件和文件夹git clean -df
git log 查看有哪些commit
git log -m 查看merged的commit
git reset –hard [commitid]
(强制转换,修改的内容会被丢弃)
git cherry-pick [commitid] (git update-ref -d CHERRY_PICK_HEAD)
git reset –soft HEAD~1
(修改的内容会在缓存区)
git commit
cat .git/HEAD
查看当前 HEAD 指向哪儿
git reflog
git reset –soft HEAD~1
git reset file
git checkout file
git checkout origin/master
git checkout –b new 创建并切换到分支new
git cherry-pick commit-id
git commit –m “Bug: BUG***** ”
git review
当前分支所有超前master的提交: git format-patch -M master
某两次提交之间的所有patch: git format-patch 365a..4e16 –365a和4e16分别对应两次提交的名称
先检查patch文件:git apply –stat newpatch.patch 检查能否应用成功:git apply –check newpatch.patch
应用:git apply newpatch.patch
git rev-parse HEAD
获取当前最后一个的 commit hash
git rev-parse –short HEAD
git clone –bare $url
git rebase -i 命令可以压缩合并多次提交 格式:git rebase -i [startpoint [endpoint]
其中-i的意思是–interactive,即弹出交互式的界面让用户编辑完成合并操作,[startpoint] [endpoint]则指定了一个编辑区间,如果不指定[endpoint],则该区间的终点默认是当前分支HEAD所指向的commit(注:该区间指定的是一个前开后闭的区间)。
执行这个命令后会跳到一个vi编辑器 里面的提示有: pick:保留该commit(缩写:p) reword:保留该commit,但我需要修改该commit的注释(缩写:r) edit:保留该commit, 但我要停下来修改该提交(不仅仅修改注释)(缩写:e) squash:将该commit和前一个commit合并(缩写:s) fixup:将该commit和前一个commit合并,但我不要保留该提交的注释信息(缩写:f) exec:执行shell命令(缩写:x) drop:我要丢弃该commit(缩写:d)
例子: 在终端输入: git rebase -i HEAD~2 这里的 HEAD~2 表示合并最近两次的提交,进入编辑模式后,修改合入记录的pick,从上到下依次为旧commit->新commit,将最后一个commit改为squash,保存退出,然后会自动执行rebase操作并弹出commit合并界面,在将红框内的commit message修改掉,然后wq保持退出即可。再次push
1、git rebase -i HEAD~2 2表示多少个commit,从上到下以从新到老排序
2、将下面第2个pick修改为s,然后保存退出 pick 31bf5d1b IR2020060115552 update chronograf from v1.0.2 to v1.0.3 pick d6064738 BUG2020070903442 add database limit 修改为: pick 31bf5d1b IR2020060115552 update chronograf from v1.0.2 to v1.0.3 s d6064738 BUG2020070903442 add database limit
3、git push -f 推送到远端
A仓库B分支: git diff [commit1] [commit2] > x.patch B仓库B分支: git apply x.patch
git是用什么语言开发的?C语言
git 可以安装在哪些操作系统上? Linux, macOS, Solaris, Windows, Raspberry Pi
git init 初始化本地仓库
git add
git commit -m
git status 查看仓库当前状态
git diff查看修改内容
git diff HEAD --
git log查看提交历史
git reflog 查看历史命令
git reset–hard HEAD^回退版本
`HEAD`表示当前版本
上一个版本就是`HEAD^`
上上一个版本就是`HEAD^^`
git reset –hard commit_id在版本的历史之间穿梭
git checkout – file让这个文件回到最近一次git commit
或git add
时的状态。
本质:用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。
git rm file从版本库中删除该文件,再执行git commit
git push
命令把本地库的内容推送到远程
git clone
命令克隆一个仓库
git branch 查看分支
git branch -v
git rev-parse –abbrev-ref HEAD获取当前分支名
git branch
git checkout <name>
或者git switch <name>
切换分支
git checkout -b <name>
或者git switch -c <name>
创建+切换分支
git merge
git branch -d
git log --graph
命令可以看到分支合并图。
git log –stat
git stash 缓存工作现场
git stash pop
回到工作现场
git cherry-pick <commit>
命令,把bug提交的修改“复制”到当前分支,避免重复劳动。
git branch -D
git remote 查看远程库的信息
git remote –v 显示更详细的信息
修改:git remote set-url origin https://**
添加:git remote add upstream git@&&&&&&&&&
删除:git remote rm upstream
git checkout -b branch-name origin/branch-name在本地创建和远程分支对应的分支
git branch –set-upstream branch-name origin/branch-name建立本地分支和远程分支的关联
\1. 当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,git checkout –file
\2. 当你改乱了工作区某个文件的内容,还添加到了暂存区,想丢弃修改,先执行git reset HEAD file,回到场景一,然后执行git checkout – < file >
\3. 提交了不合适的修改到版本库,撤销提交,用版本回退的方法
直接退回到某个patch:
\1. reset
\2. patch cherry pick 一下重新提交
https://blog.csdn.net/csdnlyu/article/details/79352811
如果配置了ssh, 但是无法git clone,执行:
git config –global http.sslVerify false
git config -l 检查参数配置是否正确
git remote set-url origin http://***
git remote –v
git pull –rebase 的时候可能会出现
fatal:Unable to fine remote helper for ‘http’的问题
https://blog.csdn.net/benben_2015/article/details/78803753
export LD_LIBRARY_PATH=/lib64:$LD_LIBRARY_PATH加个这个就没问题了
git config –global http.sslVerify false
可以在控制面板的用户账户中进行修改。
1.问题:git下载代码时,报“git出现RPC failed; curl 56 OpenSSL SSL_read: SSL_ERROR_SYSCALL, errfno 10054”的问题
2.解决步骤:
2.1 首先输入如下命令: git config http.sslVerify “false”
2.2 如果输入上面命令提示报“ fatal: not in a git directory ”,则输入如下命令:git init
2.3 重新下载代码: git clone https://git.com……
2.4 如果还是git代码还是下载失败,则需要修改git缓存的大小为100MB,输入如下命令: git config –global http.postBuffer 100M
执行下面命令
git config --global core.autocrlf false
git config --global http.sslVerify false
现象 1.修改一行代码之后,git diff 发现整个文件都被修改;
2.git diff -b 正确显示修改的部分;
3.git diff -R 被修改的每行后面都增加了一个“^M”
4.采用网上常说的:
git config --global core.whitespace cr-at-eol //该命令可以让git diff的时候忽略换行符的差异
不能解决问题
解决方法 git diff –ignore-space-at-eol > ~test.diff git checkout . git apply –ignore-whitespace ~test.diff
Git进行代码管理的过程中尽量采用rebase 而不是merge
git fetch upstream
git checkout master
git merge upstream/master
git push
git checkout [AB共同的master commit id]
git switch -c x
git checkout master
git fetch B y:x
git checkout -b x B/y
git fetch *** 获取主干tag后,
git push -f origin --tags
git describe 与git log是否一致
git tag -d 23.3.0.1 本地删除tag
git push origin :refs/tags/23.3.0.1 远程删除tag
git tag -a 23.3.0.1 -m "view" 新建tag
git push -f origin 23.3.0.1 --tags 同步到远程仓
git tag -a tagName -m "commit"
git push origin --tag # 自己的仓库
https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000
https://blog.csdn.net/ligang2585116/article/details/71094887
https://education.github.com/git-cheat-sheet-education.pdf
4/6 points (graded)
版本控制工具一节已经结束,快来做几道题看看自己掌握得如何吧~
SVN 和 Git 的相同点是?
diff 工具产生的差异结果包含哪些内容?
以下哪些是集中式版本控制工具?
以下说明正确的有?
SVN 相对于 Git 存在一些差异有?
不知道选啥
Git 相对于 SVN 有哪些优点?
sudo aptitude install git
sudo aptitude install git-doc git-svn git-email gitk
yum install git
yum install git-svn git-email gitk
Git基本配置
系统配置
git config –system core.autocrlf
用户配置
git config –global user.name
仓库配置
git config –local remote.origin.url
换行符:CRLF, LF
Linux下面安装 Git 的方式有哪些?
关于 Git 安装的操作,正确的有?
Git 和 TortoiseGit 的关系是?
关于 Git 配置的说明,正确的有?
版本库(Repository)
工作区(Working Directory)
暂存区(stage)
Git版本控制下的文件状态只有三种
已提交(committed)
已修改(modified)
已暂存(staged)
本节小测
4/4 points (graded)
将工作区中所有的新增文件添加到Git暂存区,以便提交,可以用什么命令?
在本地Git工程中,如果在分支A上进行了一系列git add/rm/mv/commit/pull操作,会直接影响Git远端服务器上分支A的节点内容。
在Windows开发环境和默认Git配置下,如果在本地Git工程已存在名为Demo的分支,同时新建分支,并命名为demo。那么在demo分支推送远端服务器过程中,会出现分支名不识别。
正确
错误
在本地Git工程中,执行git pull的更新操作相对于执行git fetch的更新操作来得安全些,可以让用户在确认更新内容符合预期后再自行决定节点合并。
课后小测
11/11 points (graded)
单选题:
\1. Git 是分布式版本控制系统,下列说法错误的是?
\2. 以下哪些是项目内部开源的好处?
①促进软件架构和代码的质量提升。 ②减少重复开发。 ③提高产出效率。 ④促进公司软件开发人员的整体能力提升。
\3. 在Git内文件的状态有以下哪些?
①已修改(modified)②已提交(committed)③已暂存(staged)
\4. 可以查看当前工作区是否干净或修改的文件处于什么状态的语句是?
\5. 查看本地和远端所有分支的命令是?
\6. 基于本地分支master,新建分支DTS并将分支转到DTS的命令行是?
\7. 如果把项目中文件 hello.c 的内容破坏了,如何使其还原至原始版本?
\8. 下列表述关于组织/仓库创建的说法错误的是?
\9. 以下哪个是修改完本地代码后提交并推送到远程仓库的正确操作顺序?
①$ git status ②$ git add [需提交的fileName] ③$ git commit ④$ git push
\10. 以下哪些操作可以本地获得基线上最新的代码?()
①$ git push ②$ git pull ③$ git pull –rebase ④$ git fetch origin + git merge origin/master
\11. 关于代码平台的 Merge Request,下列说法错误的是()
在缓冲区和控制信息间插入一个canary word
检查该值是否被修改,判断是否发生了溢出攻击
缓冲区溢出后通过执行shellcode达到攻击的目的。
设置堆栈不可执行,一旦堆栈中的数据被执行,立刻报告错误。即使溢出成功,也不能执行shellcode.
节省物理内存、提高内存利用率,阻止mmap将代码段映射并使用mprotect将其改为可写。
防止全局偏移表被恶意重写
以下哪个选项可以阻止通过重写GOT表中函数地址实现的攻击?
-WI, -z, relro, -z,now
ASLR是基础,只有操作系统开启ASLR功能,-fPIE –pie选项添加的随机化特征才会在程序加载和运行时展现。
在ASLR基础上,编译时开启-fPIE/-pie 选项会新增哪个段地址随机化?
.test或.code
##### 7.FORTIFY_SOURCE
使用栈保护选项-fstack-protector后的程序,如果遇到栈上发生溢出,给出的错误提示是“stack smashing detected”
ROP(Return-oriented Programming) 一种新型的基于代码复用技术的攻击,以下选项可以降低该攻击的成功率,提高攻击难度。
A.-WI,-z,noexecstack
B.-WI,-z,relro, -z,now
C.-fstack-protector-all
D.-fPIE -pie
在程序中一般都会用到命令行选项, 我们可以使用getopt 和getopt_long函数来解析命令行参数
getopt主要用来处理短命令行选项, 例如./test -v
中-v
就是一个短选项. 使用该函数需要引入头文件<unistd.h>
, 下面是该函数的定义
int getopt(int argc, char * const argv[], const char * optstring);
其中 argc 和 argv 是main函数中的传递的参数个数和内容, optstring用来指定可以处理哪些选项, 下面是optstring的一个示例:
"a:bc"
该示例表明程序可以接受3个选项: -a -b -c
, 其中 a
后面的 :
表示该选项后面要跟一个参数, 即如 -a text
的形式, 选项后面跟的参数会被保存到 optarg 变量中. 下面是一个使用示例:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv) {
int ch;
while((ch = getopt(argc, argv, "a:b")) != -1) {
switch(ch) {
case 'a':
printf("option a: %s\n", optarg);
break;
case 'b':
printf("option b \n");
break;
case '?': // 输入未定义的选项, 都会将该选项的值变为 ?
printf("unknown option \n");
break;
default:
printf("default \n");
}
}
}
执行 ./test -a aa -b -c
输出结果如下:
option a: aa
option b
unknown option
getopt_long支持长选项的命令行解析, 所为长选项就是诸如--help
的形式, 使用该函数, 需要引入<getopt.h>
下面是函数原型:
#include <getopt.h>
int getopt_long(int argc,
char * const argv[],
const char *optstring,
const struct option *longopts,
int *longindex);
int getopt_long_only(int argc,
char * const argv[],
const char *optstring,
const struct option *longopts,
int *longindex);
其中 argc , argv , optstring 和getopt中的含义一样, 下面解释一下longopts 和longindex
longopts
longopts 指向一个struct option 的数组, 下面是option的定义:
struct option {
const char *name;
int has_arg;
int *flag;
int val;
};
下面是各字段的含义
help
下面是longopts的一个示例
struct option opts[] = {
{"version", 0, NULL, 'v'},
{"name", 1, NULL, 'n'},
{"help", 0, NULL, 'h'}
};
我们来看{"version", 0, NULL, 'v'}
, version 即为长选项的名称, 即按如下形式--version
, 0 表示该选项后面不带参数, NULL 表示直接将v返回(字符v在ascii码中对应的数值), 即在使用getopt_long遍历到该条选项时, getopt_long 返回值为字符v对应的ascii码值.
longindex
longindex表示长选项在longopts中的位置, 例如在上面的示例中, version 对应的 longindex 为0, name 对应的 longindex 为1, help对应的 longindex 为2, 该项主要用于调试, 一般设为 NULL 即可.
下面是一个使用示例:
void use_getpot_long(int argc, char *argv[]) {
const char *optstring = "vn:h";
int c;
struct option opts[] = {
{"version", 0, NULL, 'v'},
{"name", 1, NULL, 'n'},
{"help", 0, NULL, 'h'},
{NULL,0,NULL,0}
};
while((c = getopt_long(argc, argv, optstring, opts, NULL)) != -1) {
switch(c) {
case 'n':
printf("username is %s\n", optarg);
break;
case 'v':
printf("version is 0.0.1\n");
break;
case 'h':
printf("this is help\n");
break;
case '?':
printf("unknown option\n");
break;
case 0 :
printf("the return val is 0\n");
break;
default:
printf("------\n");
}
}
}
然后我们运行程序 ./test --name lzy --version --help --haha
, 下面是运行结果:
username is lzy
version is 0.0.1
this is help
unknown option
当然我们也可以使用短选项 ./test -n lzy -v -h
下面我们对程序做一下修改, 这一次将 struct option 中的 flag 和 longindex 设为具体的值
void use_getpot_long2(int argc, char *argv[]) {
const char *optstring = "vn:h";
int c;
int f_v = -1, f_n = -1, f_h = -1, opt_index = -1;
struct option opts[] = {
{"version", 0, &f_v, 'v'},
{"name", 1, &f_n, 'n'},
{"help", 0, &f_h, 'h'},
{NULL,0,NULL,0}
};
while((c = getopt_long(argc, argv, optstring, opts, &opt_index)) != -1) {
switch(c) {
case 'n':
printf("username is %s\n", optarg);
break;
case 'v':
printf("version is 0.0.1\n");
break;
case 'h':
printf("this is help\n");
break;
case '?':
printf("unknown option\n");
break;
case 0 :
printf("f_v is %d \n", f_v);
printf("f_n is %d \n", f_n);
printf("f_h is %d \n", f_h);
break;
default:
printf("------\n");
}
printf("opt_index is %d\n\n", opt_index);
}
}
运行程序: ./test --name lzy --version --help
, 下面是运行结果:
f_v is -1
f_n is 110
f_h is -1
opt_index is 1
f_v is 118
f_n is 110
f_h is -1
opt_index is 0
f_v is 118
f_n is 110
f_h is 104
opt_index is 2
我们可以看到当给 flag 指定具体的指针之后, getopt_long 会返回0, 因此会去执行case 0, 并且 val 的值赋给了 flag 指向的变量. 下面我们用短选项执行一下程序 ./test -n lzy -v -h
, 下面是运行结果
username is lzy
opt_index is -1
version is 0.0.1
opt_index is -1
this is help
opt_index is -1
我们看到使用短选项的时候 getopt_long 就相当于 getopt , flag 和 longindex都不起作用了.
下面解释一下 getopt_long 和 getopt_long_only的区别, 首先用下列选项运行一下 use_getopt_long ./test -name lzy -version -help
, 下面是输出结果:
username is ame
opt_index is -1
version is 0.0.1
opt_index is -1
./test2: invalid option -- 'e'
unknown option
opt_index is -1
./test2: invalid option -- 'r'
unknown option
opt_index is -1
./test2: invalid option -- 's'
unknown option
opt_index is -1
./test2: invalid option -- 'i'
unknown option
opt_index is -1
./test2: invalid option -- 'o'
unknown option
opt_index is -1
username is -help
opt_index is -1
我们看到使用短选项标识符 -
指向长选项时, 程序还是会按短选项来处理, 即一个字符一个字符的解析. 下面我们将 use_getopt_long 做一下更改, 即将 getopt_long
改为 getopt_long_only
, 如下所示:
void use_getpot_long3(int argc, char *argv[]) {
const char *optstring = "vn:h";
int c;
struct option opts[] = {
{"version", 0, NULL, 'v'},
{"name", 1, NULL, 'n'},
{"help", 0, NULL, 'h'},
{NULL, 0, NULL, 0}
};
while((c = getopt_long_only(argc, argv, optstring, opts, NULL)) != -1) {
switch(c) {
case 'n':
printf("username is %s\n", optarg);
break;
case 'v':
printf("version is 0.0.1\n");
break;
case 'h':
printf("this is help\n");
break;
case '?':
printf("unknown option\n");
break;
case 0 :
printf("the return val is 0\n");
break;
default:
printf("------\n");
}
}
}
下面再运行程序 ./test -name lzy -version -help
, 下面是运行结果:
username is lzy
version is 0.0.1
this is help
即使用 getopt_long_only 时, -
和 --
都可以作用于长选项, 而使用 getopt_only 时, 只有 --
可以作用于长选项.
http://blog.zhangjikai.com/2016/03/05/%E3%80%90C%E3%80%91%E8%A7%A3%E6%9E%90%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%8F%82%E6%95%B0–getopt%E5%92%8Cgetopt_long/