Linux系统远程操作和程序编译
一、了解和熟悉Linux系统的远程终端登录、远程图形桌面访问、 X图形窗口访问和FTP文件传输操作
将虚拟机Ubuntu系统的网络连接设置为“桥接模式”,并配置Ubuntu的IP、网关参数,保证其他同学的win10系统能够ping通你的Ubuntu; 将树莓派配置在自动联网模式,保证其他同学的win10系统能够ping通你的Ubuntu; 然后,在Ubuntu和树莓派上分别用 sudo adduser添加2个用户名classmate1,、classmate2,密码均为123456;
ubantu
1. 将虚拟机Ubuntu系统的网络连接设置为“桥接模式”,并配置Ubuntu的IP、网关参数,保证其他同学的win10系统能够ping通你的Ubuntu;在Ubuntu和树莓派上分别用 sudo adduser添加2个用户名classmate1,、classmate2,密码均为123456;
(1)设置桥接模式
Win10主机的网络查看:
ipconfig
(2)配置Ubantu的网络参数:使用静态ip配置sudo nano /etc/netplan/01-netcfg.yaml
保存文件并运行以下命令应用配置:
使用另一台电脑使用ping命令观察是否成功:
(3)添加用户
(4)安装ssh服务
sudo apt updatesudo apt install openssh-serversudo systemctl enable sshsudo systemctl start ssh
让同学使用创建账号在win10下用putty或xshell等远程登录你的Ubunt系统、树莓派系统(注意要打开SSH服务)
ubantu
使用Putty连接虚拟机
打开Putty输入自己的虚拟机的IP地址,port选为22。随后点击Open进行访问。
进入终端,使用我们创建的账号进入虚拟机
树莓派
打开PuTTY,在Session中输入:
Host Name: 树莓派/Ubuntu服务器的IP地址
Port: 22(默认SSH端口)
配置X11转发:
Connection > SSH > X11
勾选"Enable X11 forwarding"
X display location设为"localhost:0"
如果正确配置了PuTTY的X11转发,不需要手动设置DISPLAY变量。SSH连接会自动设置正确的DISPLAY环境变量
可以通过以下命令验证:
echo $DISPLAY
若没有,可以通过以下命令进行DISPLAY环境配置
set DISPLAY=<电脑IP地址>:0
测试图形程序
输入以下命令进行测试:
xeyes
通过Putty进行图像界面软件的运行
图形界面文件的打开
首先通过VNC远程登录到树莓派系统桌面
由于博主之前已经配置好桌面系统环境,这里就不赘述了,没下载VNC的可以看这个博客:https://blog.csdn.net/2201_75516394/article/details/146102066?spm=1001.2014.3001.5501
输入以下命令打开对应的图形界面
gedit <文件名>
若没下载gedit,可以通过下面命令进行下载
sudo apt updatesudo apt install gedit -y
可以在桌面系统看见helloworld.c文件已经被打开
图形界面软件的打开
输入以下命令
firefox
3. 让同学在win10下用 ftp或xshel等软件l远程登录你的Ubunt系统、树莓派系统,上传和下载文件(注意要打开SSH服务)
ubantu
我们使用FileZilla
输入完主机、用户名、密码、端口号等信息之后,连接,随后在左侧选择要上传的文件夹即可
在客户端windows下安装X server仿真软件(这里我们使用免费软件 Xming), 以及终端软件,这次实验采用 putty。
安装好之后打开让它在后台运行。
打开PuTTY,进入Connection→SSH→Auth→X11 forwarding并勾选,然后使用classmate1访问虚拟机
输入指令连接windows系统:
set DISPLAY=xxx.xxx.xxx.xxx:0
输入命令打开windows下载的Xing:
gedic
树莓派
在电脑命令行中使用ssh命令登录树莓派
sftpd的下载
输入以下命令下载vsftpd:sudo apt-get install vsftpd
修改配置文件
输入以下命令进入文件vsftpd.v并对应将图片中圈出的语句前的#删掉
cd /etc/sudo nano vsftpd.conf
重新启动服务
sudo service vsftpd restart
ftp远程登录树莓派
打开cmd命令行
输入ftp <树莓派的IP地址>然后对应输入用户名和密码进行登录
文件上传
首先我们在电脑中找到我们要上传的文件路径
然后我们在命令行中写入下面代码
mkdir week8 //新建文件夹,用于存放待会上传的文件ls //查看所有文件夹,可以看见我们新创建的week8cd week8 //打开week8文件夹lcd E:\ //进入本地目录中,LCD 后面跟自己文件所在的目录put helloworld.c //将helloworld.c文件上传至该文件夹中
通过ls命令我们可以查看该文件是否上传成功
可以看到文件已经成功上传。
思考题
X窗口协议与远程桌面VNC-viewer协议有什么差异?
X Window System (X11) 的 X 协议和 VNC (Virtual Network Computing) 的 VNC-viewer 协议都是用于远程访问和控制图形用户界面的工具,但它们在设计、功能和使用场景上有一些关键差异:
1. 协议类型
X 协议:是X Window System的一部分,专门设计用于通过网络访问和控制运行X程序的图形界面。X 协议是X Window System的原生协议,提供了丰富的图形界面操作能力。
VNC-viewer 协议:是VNC(Virtual Network Computing)协议的一部分,VNC是一个更通用的远程桌面访问解决方案,支持多种操作系统和多种类型的客户端。VNC-viewer通常用于连接到运行VNC服务器的远程桌面环境。
2. 图形界面
X 协议:提供了完整的图形界面访问能力,允许用户通过网络操作运行的X应用程序,就像在本地机器上操作一样。
VNC-viewer 协议:也提供图形界面访问,但其功能可能受到VNC服务器配置和客户端能力的限制。VNC-viewer通常用于访问远程桌面环境,可能包括多种应用程序和桌面环境。
3. 性能和优化
X 协议:通常针对X Window System进行了优化,提供了较好的性能和较低的延迟,特别是在处理图形密集型任务时。
VNC-viewer 协议:性能可能受到多种因素的影响,包括网络延迟、服务器负载和客户端性能。VNC-viewer可能需要更多的网络带宽来处理图形数据。
4. 安全性
X 协议:安全性可能依赖于X Window System的安全性设置,包括加密和认证机制。
VNC-viewer 协议:VNC提供了一套完整的安全特性,包括加密连接、用户认证和访问控制。VNC-viewer通常用于需要高安全性的环境。
5. 易用性和配置
X 协议:配置可能相对简单,特别是在X Window System环境中。
VNC-viewer 协议:可能需要更多的配置,特别是在设置VNC服务器和客户端时。VNC-viewer通常需要配置网络、安全和用户访问策略。
6. 跨平台支持
X 协议:主要设计用于X Window System环境,对其他操作系统的支持可能有限。
VNC-viewer 协议:VNC提供了跨平台支持,可以在多种操作系统上运行客户端和服务器,包括Linux、Windows、macOS等。
你会发现新建的账号无法使用sudo命令,请问如何解决这个问题?
在 Ubuntu 中,新建的用户无法使用 sudo 命令是因为该用户未被添加到 sudo 用户组(或 /etc/sudoers 文件中未授权)。我们可以通过root账户操作:
切换到root用户:
su - root
或
sudo - i
将新用户添加到sudo组:
usermod -aG sudo 新用户名
验证是否生效:
groups 新用户名
三、编写一个打开图片进行特效显示的代码 test1.cpp
3.1 下载安装 opencv
3.1.1 下载安装包并解压
下载安装资源压缩包
将下载好的压缩包opencv-3.4.11放在主目录文件夹下
解压缩(打开虚拟机终端,输入命令 unzip opencv-3.4.11.zip)
unzip opencv-3.4.11.zip
解压缩以后主目录文件夹如下:
进入到解压后的文件夹中
cd opencv-3.4.11
3.1.2 使用 cmake安装opencv
(1)首先进入 root 用户,并更新一下
sudo su
sudo apt-get update
(2)执行以下命令安装 cmake
sudo apt-get install cmake
这里直接按enter键默认继续安装
(3)cmake安装完成
(4)安装依赖库
sudo apt-get install build-essential libgtk2.0-dev libavcodec-dev libavformat-dev libjpeg.dev
libtiff5.dev libswscale-dev libjasper-dev
(5)创建build文件夹,进入build文件夹
(6)使用cmake 编译参数
cmake ..
运行结果如下:
3.1.3 使用make创建并编译安装
(1)在build文件夹下进行
输入以下命令
sudo make
(2)编译完成
(3)安装
输入以下命令
sudo make install
(4)安装完成
3.1.4 配置环境
(1)修改 opencv.conf文件,打开后的文件是空的,添加opencv库的安装路径:/usr/local/lib
输入以下命令
sudo gedit /etc/ld.so.conf.d/opencv.conf
输入路径/usr/local/lib 并保存,记得点小叉叉退出文件编辑界面
出现警告是正常的
(2)更新系统共享链接
sudo ldconfig
(3)配置bash,修改 bash.bashrc 文件
输入以下命令
sudo gedit /etc/bash.bashrc
(4)在文件末尾加入
PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig
export PKG_CONFIG_PATH
(5)然后执行以下命令使得配置生效
source /etc/bash.bashrc
(6)输入以下命令更新
sudo updatedb
(7)查看 opencv 的版本信息
如下图所示,说明安装成功
3.2 打开图片进行特效显示
3.2.1 示例图片
3.2.2 具体过程
(1)新建文件夹code
输入以下命令创建新的文件夹
mkdir code
cd code
(2)新建文件 test1.cpp
输入以下命令新建 test1.cpp 文件
vim test1.cpp
(3)test1.cpp代码
#include <opencv2/highgui.hpp>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main(int argc, char** argv)
{
CvPoint center;
double scale = -3;
IplImage* image = cvLoadImage("lena.jpg");
argc == 2? cvLoadImage(argv[1]) : 0;
cvShowImage("Image", image);
if (!image) return -1; center = cvPoint(image->width / 2, image->height / 2);
for (int i = 0;i<image->height;i++)
for (int j = 0;j<image->width;j++) {
double dx = (double)(j - center.x) / center.x;
double dy = (double)(i - center.y) / center.y;
double weight = exp((dx*dx + dy*dy)*scale);
uchar* ptr = &CV_IMAGE_ELEM(image, uchar, i, j * 3);
ptr[0] = cvRound(ptr[0] * weight);
ptr[1] = cvRound(ptr[1] * weight);
ptr[2] = cvRound(ptr[2] * weight);
}
Mat src;Mat dst;
src = cvarrToMat(image);
cv::imwrite("test.png", src);
cvNamedWindow("test",1); imshow("test", src);
cvWaitKey();
return 0;
}
输入代码以后,按Esc 输入 :wq保存退出
vim修改代码,按i进入插入模式;按Esc再输入 :wq 保存并退出
(4)把图片粘贴到code目录下
(5)编译 test1.cpp 文件
g++ test1.cpp -o test pkg-config --cflags --libs opencv
(6)运行
输入以下命令运行
./test
运行结果
3.2.3 相关问题
1、请解释 gcc test1.cpp -o test1 pkg-config --cflags --libs opencv这条编译命令,它是如何获得opencv头文件、链接lib库文件的路径的?
命令解析:
gcc:GNU 编译器,用于编译 C/C++ 程序。
test1.cpp:源代码文件,包含 OpenCV 的代码。
-o test1:指定输出的可执行文件名称为 test1。
`pkg-config --cflags --libs opencv`:这是一个子命令用于,动态获取 OpenCV 的编译和链接参数。
pkg-config:一个工具,用于查询已安装库的编译和链接参数。
--cflags:获取 OpenCV 的头文件路径(-I 参数)。
--libs:获取 OpenCV 的库文件路径(-L 参数)和库名称(-l 参数)。
(1)pkg-config --cflags --libs opencv 会返回 OpenCV 的头文件路径和库文件路径,例如:
-I/usr/local/include/opencv4 -L/usr/local/lib -lopencv_core -lopencv_imgproc
-lopencv_highgui ...
(2)这些参数会被传递给 gcc,确保编译器能够找到 OpenCV 的头文件和库文件。
2、改用make+makefile方式编译 上述程序(用变量命名格式写makefile文件,并包括 clean选项)
将以下内容保存为 Makefile 文件,放在 code 目录下
定义变量
CXX = g++
CXXFLAGS = `pkg-config --cflags opencv`
LDFLAGS = `pkg-config --libs opencv`
SRC = test.cpp
OBJ = test.o
TARGET = test
默认目标
all: $(TARGET)
编译规则
$(TARGET): $(OBJ)
$(CXX) $(CXXFLAGS) $(LDFLAGS) -o $(TARGET) $(OBJ)
生成对象文件
$(OBJ): $(SRC)
$(CXX) $(CXXFLAGS) -c $(SRC) -o $(OBJ)
清理
clean:
rm -f $(OBJ) $(TARGET)
在终端运行以下命令
cd ~/code
make
运行程序
./test
清理编译文件
make clean
3、用cmake方式进行编译
将以下内容保存为 CMakeLists.txt 文件,放在 ~/code 目录下:
#CMake 最低版本要求
cmake_minimum_required(VERSION 3.10)
项目名称
project(OpenCVTest)
查找 OpenCV
find_package(OpenCV REQUIRED)
添加可执行文件
add_executable(test test.cpp)
链接 OpenCV 库
target_link_libraries(test ${OpenCV_LIBS})
(1)在 ~/code 目录下创建一个构建目录并进入:
cd ~/code
mkdir build
cd build
(2)运行 cmake 和 make:
cmake ..
make
编译完成后,可执行文件 test 会生成在 build 目录中
(3)运行程序
./test
(4)清理
删除 build 目录
rm -rf ~/code/build
四、talk程序的运用&Linux进程间通信程序的编写
使用talk程序和其他用户交流
首先我们通过who指令查询,可以看到当前已经登录用户信息和终端
然后输入talk指令连接对应用户进行通信
talk username pts/
g-iqeL2aNl-1744558582735)
然后等待对方用户连接
上方是我发的信息,下方是对面发的信息
S-1744558582735)
但是当我们尝试在talk程序下输入中文进行交流时,会出现中文乱码。所以接下来我们尝试自己编写一个c语言程序,实现Linux进程之间的通信。
用c语言写一个linux进程之间通信(聊天)的简单程序
服务器端程序socket_server.c编写
通过nano socket_server.c 新建服务器端程序文件,并进行如下代码编写。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <pthread.h>
#define PORT 8080#define BUFFER_SIZE 1024
void *handle_client(void *arg) {
int client_socket = *((int *)arg);
char buffer[BUFFER_SIZE];
int bytes_read;
while (1) {
bytes_read = read(client_socket, buffer, BUFFER_SIZE);
if (bytes_read <= 0) {
break; }
buffer[bytes_read] = '\0';
printf("Received: %s", buffer);
printf("Enter reply (or 'exit' to quit): ");
fgets(buffer, BUFFER_SIZE, stdin);
if (strncmp(buffer, "exit", 4) == 0) {
write(client_socket, "Server exiting...", 17);
break;
}
write(client_socket, buffer, strlen(buffer));
}
close(client_socket);
pthread_exit(NULL);}int main() {
int server_fd, client_socket;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
pthread_t thread_id; // 创建socket文件描述符
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
} // 设置socket选项 if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt,
sizeof(opt))) {
perror("setsockopt");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT); // 绑定socket到端口
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) { perror("bind failed");
exit(EXIT_FAILURE);
} // 开始监听
if (listen(server_fd, 3) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}
printf("Server started on port %d. Waiting for connections...\n", PORT);
while (1) {
// 接受新连接
if ((client_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0)
{
perror("accept");
continue;
}
printf("New connection from %s\n", inet_ntoa(address.sin_addr)); // 为每个客户端创建新线程
if (pthread_create(&thread_id, NULL, handle_client, (void *)&client_socket) < 0) { perror("could not create thread");
continue;
} // 分离线程,使其结束后自动释放资源
pthread_detach(thread_id);
}
close(server_fd);
return 0;
}
客户端程序socket_client.c编写
通过nano socket_client.c 新建客户端代码文件,并进行如下代码编写
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <pthread.h>
#define BUFFER_SIZE 1024
void *receive_messages(void *arg) {
int sock = *((int *)arg);
char buffer[BUFFER_SIZE];
int bytes_read;
while (1) {
bytes_read = read(sock, buffer, BUFFER_SIZE);
if (bytes_read <= 0) {
printf("Server disconnected\n");
exit(EXIT_SUCCESS);
}
buffer[bytes_read] = '\0';
printf("\nReceived: %s", buffer);
printf("Enter message (or 'exit' to quit): ");
fflush(stdout);
}
return NULL;}int main(int argc, char const *argv[]) {
if (argc != 2) {
printf("Usage: %s <server_ip>\n", argv[0]);
exit(EXIT_FAILURE);
}
int sock = 0;
struct sockaddr_in serv_addr;
char buffer[BUFFER_SIZE] = {0};
pthread_t thread_id; // 创建socket
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket creation error");
exit(EXIT_FAILURE);
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(8080); // 将IP地址从字符串转换为二进制形式
if (inet_pton(AF_INET, argv[1], &serv_addr.sin_addr) <= 0) {
perror("invalid address");
exit(EXIT_FAILURE);
} // 连接服务器
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { perror("connection failed");
exit(EXIT_FAILURE);
}
printf("Connected to server at %s\n", argv[1]); // 创建接收消息的线程
if (pthread_create(&thread_id, NULL, receive_messages, (void *)&sock) < 0) {
perror("could not create thread");
exit(EXIT_FAILURE);
} // 主线程处理发送消息
printf("Enter message (or 'exit' to quit): ");
while (fgets(buffer, BUFFER_SIZE, stdin)) {
if (strncmp(buffer, "exit", 4) == 0) {
write(sock, "Client exiting...", 17);
break;
}
write(sock, buffer, strlen(buffer));
printf("Enter message (or 'exit' to quit): ");
}
close(sock);
return 0;
}
程序编译与使用
在服务器和客户端机器上分别编译:
# 服务器端gcc socket_server.c -o server -lpthread
# 客户端gcc socket_client.c -o client -lpthread
接下来我们进行通讯尝试:
在服务器机器上启动服务器程序:
./server
在客户端机器上启动客户端程序,指定服务器IP地址:
./client <服务器IP地址>