Linux高并发服务器开发-Linux系统编程入门
环境准备
Ubuntu18
Xshell
Xftp
VsCode
vscode ssh 远程连接ubuntu
报错 过程试图写入管道不存在
替换ssh路径
这是我自己解决的方法,可能是因为win10内置的SSH的问题,由于装了Git,且自带ssh,因此可以在环境变量中将win10内置ssh的路径替换为git的
环境变量——系统变量——path
查找ssh路径
git bash here
where ssh
我自己的环境变量
%SYSTEMROOT%\System32\OpenSSH\
将其替换为git的ssh路径
GCC
安装命令 (version>4.8.5)
sudo apt install gcc g++
查看版本
gcc/g++ -v/--version
编译
g++ test.c -o app
工作流程
gcc编译选项 | 说明 |
---|---|
-E | 预处理指定的源文件,不进行编译 |
-S | 编译指定的源文件,但是不进行汇编 |
-c | 编译、汇编指定源文件,但不链接 |
-o | 将file1编译成可执行文件file2 |
-I | 指定include包含文件的搜索目录 |
-g | 在编译的时候,生成调试信息,程序可以被调试器调试 |
-D | 在程序编译的时候,指定一个宏 |
-w | 不生成任何警告信息 |
-Wall | 生成所有警告信息 |
-l | 在程序编译的时候,指定使用的库 |
-L | 指定编译时搜索的库的路径 |
-shared | 生成共享目标文件,通常在建立共享库时 |
-fpic/-fPIC | 生成与位置无关的代码 |
gcc 和 g++的区别
_cplusplus宏只标志编译器把代码按照C还是C++解释,如果是.c 并且采用gcc编译,该宏就是未定义,否则就是已定义的
编译可以用gcc/g++,链接可以用gcc/gcc -lstdc++
gcc命令不能自动和C++程序的库链接,通常用g++,编译阶段,g++会自动调用gcc,二者等价
动态库和静态库
命名规则
-
Linux:
- libxxx.a(静态库)
- libxxx.so(动态库)
-
Windows
- libxxx.lib(静态库)
- libxxx.dll
静态库和动态库的制作
gcc获得.o文件
静态库
gcc -c xxx.c xxx.c xxx.c
动态库,得到和位置无关的代码
gcc -c -fpic/-fPIC xxx.c xxx.c
打包.o文件
静态库ar工具(achieve)打包
ar rcs libxxx.a xxx.o xxx.o xxx.o
r- 将文件插入备存文件中
c- 建立备存文件
s- 索引
动态库
gcc -shared xxx.o xxx.o -o libxxx.so
静态库和动态库的使用
以此为例
静态库的使用
gcc main.c -o app -I ./include/ -l calc -L./lib
动态库的使用
gcc main.c -o app -I ./include/ -l calc -L./lib
动态库使用需要动态载入才能使用,不推荐修改 /lib/, /usr/lib目录,因为里面有系统的库。
方法1,添加环境变量 LD_LIBRARY_PATH
显示动态库的依赖关系
ldd app
查看环境变量
env
添加环境变量
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/xxx/xxx/xxx/lib
显示添加的环境变量的值
echo $LD_LIBRARY_PATH
运行更新配置:
source xxx
- 临时配置:程序文件夹library目录下配置
- 永久配置:
- 用户级别:在 home目录下,.bashrc里配置
- 系统级别:在 /etc/profile里配置
方法2,修改 /etc/ld.so.cache文件列表
sudo vim /etc/ld.so.conf
添加lib的路径
sudo ldconfig
静态库和动态库的对比
静态库优缺点
动态库优缺点
Makefile
自动化编译,定义一系列规则指定文件编译顺序等。通过make命令,整个工程完全自动编译。
规则
一个Makefile文件可以有一个或多个规则
目标... : 依赖...
命令(shell命令)
...
Makefile 中的其他规则一般是为第一条规则服务的
工作原理
变量
模式匹配
%.o:%.c
- %: 通配符,匹配一个字符串
- 两个%匹配同一个字符串
%.o:%.c
gcc -c $< -o $@
函数
$(wildcard PATTERN...)
$(patsubst <pattern>,<replacement>,<text>)
clean
.PHONY:clean //clean伪目标,不生成特定的文件
clean:
rm ... -f
执行
make clean
GDB调试
GDB的功能
准备工作
关闭编译器优化选项(-O),打开调试选项(-g), '-Wall' 尽量在不影响程序行为的情况下打开所有warning。
gcc -g -Wall program.c -o program
'-g' 作用是在可执行文件中加入源代码的信息,比如可执行文件中第几条机器指令对应源代码的第几行,并不是把整个源代码嵌入可执行文件中,所以在调试时必须保证gdb能找到源文件。
GDB命令-启动、退出、查看代码
GDB命令-断点代码
GDB命令-调试命令
文件IO相关
标准C库IO函数
标准C库IO函数与Linux系统IO函数的关系
虚拟地址空间
文件描述符
Linux系统文件IO相关函数
open(), close()
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
//打开一个已存在的文件
int open(const char*pathname, int flags);
参数:
-pathname:要打开的文件路径
-flags:对文件操作权限设置还有其他设置
O_RDONLY, O_WRONLY, O_RDWR 三个设置互斥
返回值:返回一个新的文件描述符,调用失败,返回-1
errno:属于linux系统函数库,库里面一个全局变量,记录最近的错误号
#include...
//创建一个新文件
int open(const char *pathname, int flags, mode_t mode);
参数:
-flags:对文件操作权限设置还有其他设置
-必须项:O_RDONLY, O_WRONLY, O_RDWR 三个设置互斥
-可选项:O_CREAT 文件不存在,创建新文件
flags参数是一个int数据,占4个字节,32位,每一位就是一个标志位
-mode:八进制的数,表示创建出的新的文件的操作权限
最终的权限:mode& ~umask
mode:比如:0775
000 111 111 101
rwx rwx rwx rwx
0 7 7 5
int fd = open("creat.txt", O_RDWR|O_CREAT, 0777);
#include<stdio.h>
//打印errno对应的错误描述
void perror(const char*s);
s参数:用户描述,比如hello,最终输出hello:xxx(实际错误信息)
#include<unistd.h>
int close(int fd);
fd:文件描述符
read(),write()
#include <unistd.h>
//读文件
ssize_t read(int fd, void *buf, size_t count);
参数:
-fd:文件描述符,open得到,通过文件描述符操作这个文件
-buf:需要读取数据存放的地方,数组的地址(传出参数)
-count:指定的数组的大小
返回值:
-成功:
>0:返回实际读取到的字节数
=0:文件已经读取完
-失败:-1,并且设置errno
//写文件
ssize_t write(int fd, const void *buf, size_t count);
参数:
-fd:文件描述符,open得到,通过文件描述符操作这个文件
-buf:需要往磁盘写入的数据
-count:写入的数据的实际大小
返回值:
-成功:实际写入的字节数
-失败:-1,并设置errno
//频繁读写操作
char buf [1024]={0};
int len=0;
while((len=read(fd1,buf,sizeof(buf)))>0){
write(fd2,buf,len);
}
lseek()
//标准C库的函数
#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);
//Linux系统函数
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
参数:
-offset:偏移量
-whence:
SEEK_SET
设置文件指针偏移量
SEEK_CUR
设置偏移量:当前位置+第二个参数offset的值
SEEK_END
设置偏移量,文件大小+第二个参数offset的值
返回值:返回文件指针的位置
作用:
1.移动文件指针到头文件
lssek(fd,0,SEEK_SET);
2.获取当前文件指针的位置
lseek(fd,0,SEEK_CUR);
3.获取文件的长度
lseek(fd,0,SEEK_END);
4.扩展文件的长度,当前文件10b -> 110b,增加了100个字节
lseek(fd,100,SEEK_END);
注意;扩展文件后,写入数据。
stat(),lstat()
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
//获取一个文件相关的信息
int stat(const char *pathname, struct stat *statbuf);
参数:
-statbuf:结构体变量,传出参数,用于保存获取的文件信息
返回值
-成功:返回0
-失败:返回-1,并设置errno
//获取软链接文件的信息
int lstat(const char *pathname, struct stat *statbuf);
文件属性操作函数
access()
#include <unistd.h>
//判断文件是否有某个权限
int access(const char *pathname, int mode);
参数:
-mode:
R_OK:判断是否有读权限
W_OK:判断是否有写权限
X_OK:判断是否有执行权限
F_OK:判断文件是否存在
返回值:
-成功:返回0
-失败:返回-1
chmod(), chown()
#include <sys/stat.h>
//修改文件的权限
int chmod(const char *pathname, mode_t mode);
参数:
-mode:需要修改的权限值,八进制数
返回值:
-成功:返回0
-失败:返回-1
#include <unistd.h>
//修改文件owner,group
int chown(const char *pathname, uid_t owner, gid_t group);
参数:
-owner:所有者id
-group: 所在组id
truncate()
#include <unistd.h>
#include <sys/types.h>
//缩减或扩展文件的尺寸至指定的大小
int truncate(const char *path, off_t length);
参数:
-length:最终文件变成的大小
返回值:
-成功:返回0
-失败:返回-1
目录操作函数
mkdir(), rmdir()
#include <sys/stat.h>
#include <sys/types.h>
//创建一个目录
int mkdir(const char *pathname, mode_t mode);
参数:
-mode:需要修改的权限值,八进制数
返回值:
-成功:返回0
-失败:返回-1
#include <unistd.h>
//只能删除一个空目录
int rmdir(const char *pathname);
rename()
#include <stdio.h>
//重命名目录
int rename(const char *oldpath, const char *newpath);
chdir(), getcwd()
#include <unistd.h>
//修改进程的工作目录
int chdir(const char *path);
#include <unistd.h>
//获取当前工作目录
char *getcwd(char *buf, size_t size);
参数:
-buf:
存储的路径,执行一个数组
-size:
数组的大小
返回值:返回的指向一块内存,这个数据就是第一个参数
目录遍历函数
opendir()
#include <sys/types.h>
#include <dirent.h>
//打开一个目录
DIR *opendir(const char *name);
参数:
-name:需要打开的目录名称
返回值:
DIR*类型,目录流信息。
错误返回NULL
readdir()
#include <dirent.h>
//读取目录中的数据
struct dirent *readdir(DIR *dirp);
参数:
-dirp:dirp是opendir返回的结果
返回值:
struct dirent ,代表读取文件的信息
失败返回NULL
closedir
#include <sys/types.h>
#include <dirent.h>
//关闭目录
int closedir(DIR *dirp);
文件描述符函数
dup(),dup2()
#include <unistd.h>
//复制文件描述符
int dup(int oldfd);
//重定向文件描述符号
int dup2(int oldfd, int newfd);
oldfd必须有效
fcntl()
#include <unistd.h>
#include <fcntl.h>
//
int fcntl(int fd, int cmd, ... /* arg */ );
参数:
-cmd:对文件描述符如何操作
-F_DUPFD : 复制文件描述符,复制的第一个参数fd,得到一个新的文件描述符(返回值)
int ret = fcntl(fd,F_DUPFD);
-F_GETFL : 获取指定的文件描述符文件状态flag,获取的flag和open函数的flag相同
-F_SETFL : 设置文件描述符文件状态flag
必选项:O_RDONLY,O_WRONLY,O_RDWR不可以被修改
可选项:O_APPEND,O_NONBLOCK
O_APPEND 表示追加数据
O_NONBLOCK 设置成非阻塞
int flag = fcntl(fd, F_GETFL);
flag |= O_APPEND;
int ret = fcntl(fd, F_SETFL, flag)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话