一 、前言
最近在研究国外非常受欢的迎知名计算机课程CS631 - APUE,其中配合使用的书籍是《UNIX环境高级编程》一书。目的在于更好的了解类Unix下系统软件(比如一些经常用到的系统命令ls、mv、cat等等)的实现。以及高级 I/O、文件、信号、 进程关系和进程间通信怎么工作。
在官方课程教学中使用的是NetBSD 9.2系统。对于不想新安装一个虚拟机系统来占用磁盘,或者就想在Ubuntu下进行研究学习的。前期一些必要的工作可以方便后面更好的实践自己的代码。
为了能快速的查看编辑一些源码。也介绍了一下如何通过Windows的Clion编辑虚拟环境代码,以及ssh连接访问虚拟机系统。
本次使用环境
VMware 16
Ubuntu 20.04
本次使用软件
clion
Tabby Terminal
sshd
samba
cmake
make
gcc
二 、apue下载编译及遇到的问题
1. 下载apue
wget http://www.apuebook.com/src.3e.tar.gz
tar -zxvf src.3e.tar.gz
2. 安装bsd库
为了使用BSD的实现的C语言标准库,如想要在课程中验证或者使用NetBSD系统源码的一些实现,也可能会用到。
sudo apt-get install libbsd-dev
安装好后可以在/usr/include/bsd 目录下找到一些如stdio.h头文件
使用BSD目录的头文件
#include <bsd/stdlib.h>
int main(int argc, char *argv[]) {
setprogname(argv[0]);
}
上面面C代码中,使用使用setprogname()函数,在ubuntu默认c标准库上的stdlib.h中是没有这个函数的。
可以看到我找在
/usr/include目录及子目录下到stdlib.h的头文件很多,但是只有/bsd目录下的stdlib头包含有setprogname()函数的声明。
如何链接到使用bsd库的函数
gcc -c test.c -o test.o -lbsd
3. 编译apue源码
进入到apue解压目录
运行make,出现如下错误
解决1
打开apue.3e/include目录下的apue.h文件,在其中添加#include <sys/sysmacros.h>
再次make,出现
解决办法
打开apue.3e/stdio/buf.c,删掉如下代码(文件的89~93行)
#ifdef _LP64
#define _flag __pad[4]
#define _ptr __pad[1]
#define _base __pad[2]
#endif
将buf.c文件的95-121行换成下面代码
int
is_unbuffered(FILE *fp)
{
return(fp->_flags & _IONBF);
}
int
is_linebuffered(FILE *fp)
{
return(fp->_flags & _IOLBF);
}
int
buffer_size(FILE *fp)
{
#ifdef _LP64
return(fp->_IO_buf_end - fp->_IO_buf_base);
#else
return(BUFSIZ); /* just a guess */
#endif
}
#else
#error unknown stdio implementation!
#endif
也可以直接克隆我修改后的代码 apue.3e-ubuntu20.04 (github.com),再次make,应该就不会有错误了。
最后将apue.h头文件和库文件libapue.a复制到系统目录下。就可以使用了。
sudo cp /path/to/apue.3e/lib/libapue.a /usr/lib
sudo cp /path/to/apue.3e/include/apue.h /usr/include
在编译的时候-lapue链接库
gcc -c test.c -o test.o -lapue
三 、设置虚拟机网络
主机网络信息
设置虚拟机网卡模式
选择桥接模式 -> 配置适配器 -> 找到你使用的网卡(通常和主机一个网卡)。乱选网卡或多选都可能导致虚拟机无法联网。
根据主机IP,给虚拟机设置独立本地IP
根据主机网络信息,网段,选择给虚拟机分配一个固定IP,子网掩码和网关跟主机一样。便于后面主机使用ssh连接虚拟机。
四 、windows使用Tabby通过ssh连接虚拟机
Ubuntu 上安装 sshd 的详细步骤:
- 打开终端,更新软件包列表:
sudo apt-get update
- 安装
openssh-server:
sudo apt-get install openssh-server
3. 启动 sshd 服务:
sudo systemctl start sshd
如果您使用的是旧版 Ubuntu,则需要将 start 替换为 restart。
- 验证服务是否成功启动:
sudo systemctl status sshd
如果服务已成功启动,则状态应显示为 "active (running)"
- 启用
sshd开机自启动:
sudo systemctl enable sshd
6. 确认 sshd 是否已添加到开机启动项:
sudo systemctl is-enabled sshd
Tabby连接虚拟机
1. 下载并安装 Tabby
从 tabby.rs 的官方网站下载 Tabby,根据您的操作系统选择适当的安装包进行下载和安装。
2. 打开终端并启动 Tabby, 创建新会话
配置和连接 -> 新建配置 -> 搜索ssh
3. 配置连接参数
在弹出的 "Connection" 窗口中输入连接参数:
- "Host": 输入虚拟机的IP地址或主机名。
- "Port": 输入 SSH 服务监听的端口,默认为22。
- "Username": 输入虚拟机上的用户名,用于身份验证。
- "Authentication": 设置身份验证方式。可以选择密码验证、密钥文件验证等。
4. 连接到虚拟机
完成参数配置后,选择想要连接的虚拟机即可。
五、 windows 通过samba 访问虚拟机文件系统
1. 安装Samba
sudo apt-get install samba
2. 安装过程中
系统将提示您输入一个管理员密码。请确保使用强密码以保护您的系统。
3. 安装完成后
打开smb.conf文件并进行配置。您可以使用以下命令来打开smb.conf文件:
sudo nano /etc/samba/smb.conf
然后,您可以通过修改文件中的参数来配置Samba服务器,例如共享目录、访问权限等。
最末添加内容如:
[Project]
comment = project
path = /home/xxx # 此处为你想要在虚拟机分享的文件路径
browseable = yes
writable = yes
create mask = 0700
directory mask = 0700
valid users = xxxname # 一个名字
force user = xxxname
force group = xxxname
public = yes
available = yes
4. 配置完毕后
运行以下命令重启Samba服务:
sudo service smbd restart
后续操作
sudo smbpasswd -a <用户名> # 和smb.config中user对应
sudo systemctl restart smbd
sudo systemctl enable smbd
sudo systemctl enable smbd # 开机自启(可选)
5. 在windows访问Ubuntu samba服务
Win+r 输入网络配置的虚拟机ip,如
如果win出现你不能访问此共享文件夹,因为你组织的安全策略阻止未经身份验证来宾访问 提示
解决办法:win+r 输入gpedit.msc
1.找到计算机配置---管理模板---网络---Lanman工作站
2.找到“启用不安全的来宾登录"。
3.选择启用
4.将其固定到快速访问
右键 -> 选择映射网路文件夹,选择一个就行
现在就可以快速地在虚拟机和主机间快速共享文件了。
六、Clion远程调试
此处打开Clion,你就可以看见前面通过samba共享的文件目录了。
配置Clion远程调试环境
- 设置ssh连接
settings -> toolchains 添加一个配置环境 -> cedentials 右侧添加ssh配置 -> 按照前面tabby ssh连接差不多设置
- 设置远程编译器
在toolchain的剩余选项中,设置好cmake,make,g++,gcc GDB等路径即可。
配置代码Deployment
- 选择SFTP,设置好远程环境根目录
- 设置本地映射的文件夹
七、 编写一段引用apue.h的代码
../01_basic/01_ls.c 文件,模仿ls命令的简单实现
#include "apue.h"
#include <dirent.h>
#include <bsd/stdlib.h>
int main(int argc, char *argv[]) {
setprogname(argv[0]);
DIR *dp;
struct dirent *dirp;
if (argc != 2)
err_quit("usage: %s directory_name", getprogname());
// 打开arg[1]目录,赋值给dp目录指针
if ((dp = opendir(argv[1])) == NULL)
err_sys("can't open %s", argv[1]);
// 读取目录
while ((dirp = readdir(dp)) != NULL)
printf("%s\n", dirp->d_name);
closedir(dp);
exit(0);
}
1、使用gcc编译
2、使用Makefile编译
在当前目录下生成目标文件的Makefile
CC := gcc
CFLAGS := -Wall
LDLIBS := -lapue -lbsd
# 获取所有 .c 文件的名称
SRCS := $(wildcard *.c)
# 将 .c 文件的名称替换为对应的 .o 和可执行文件的名称
OBJS := $(patsubst %.c, %.o, $(SRCS))
BINS := $(patsubst %.c, %, $(SRCS))
# 定义默认目标(即 all),使其依赖于所有的可执行文件
all: $(BINS)
# 生成可执行文件的规则
%: %.o
$(CC) $(CFLAGS) $< $(LDLIBS) -o $@
# 生成 .o 文件的规则
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
# 清理规则
clean:
rm -f $(OBJS) $(BINS)
运行Makefile
统一输出到子目录output文件夹下的Makefile2
CC := gcc
CFLAGS := -Wall
LDLIBS := -lapue -lbsd
# 获取所有 .c 文件的名称
SRCS := $(wildcard *.c)
# 定义输出目录和可执行文件
OUT_PUT_DIR := output
# 将 .c 文件的名称替换为对应的 .o 和可执行文件的名称
OBJS := $(patsubst %.c, output/%.o, $(SRCS))
BINS := $(patsubst %.c, output/%, $(SRCS))
# 创建输出目录
$(OUT_PUT_DIR):
mkdir -p $(OUT_PUT_DIR)
# 生成可执行文件的规则
output/%: output/%.o
$(CC) $(CFLAGS) $< $(LDLIBS) -o $@
# 生成 .o 文件的规则
output/%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
# 定义默认目标(即 all),使其依赖于所有的可执行文件
all: output $(BINS)
# 清理规则
clean:
rm -f $(OBJS) $(BINS)
rm -rf $(OUT_PUT_DIR)
运行Makefile2,make -f Makefile2 all
.
3、 使用Cmake编译
01_basic同级目录的CMakelists.txt
cmake_minimum_required(VERSION 3.14)
project(C_Unix)
# 使用C99
set(CMAKE_C_STANDARD 99)
#
add_executable(01_basic_01_ls 01_basic/01_ls.c)
add_executable(01_basic_02_cp 01_basic/02_cp.c)
# 指定APUE库的头文件和库文件路径
include_directories(/usr/include)
link_directories(/usr/lib)
# 定义变量来保存需要链接的库的名称
set(LINK_LIBS apue bsd)
# 链接APUE BSD库
target_link_libraries(01_basic_01_ls ${LINK_LIBS})
target_link_libraries(01_basic_02_cp ${LINK_LIBS})
运行Cmake
mkdir build
cd build
cmake ..
make
八、 总结
- 经过上面一系列步骤,基本可以实现apue环境编程需求或者有设置类似环境开发的需求。
- 课程很不错,书籍也很经典,值得学习。