Linux系统概述
Linux文件与权限
linux目录说明
/boot: 存放的启动linux时使用的内核文件,包括链接文件及镜像文件
/etc : 存放所有系统需要的配置文件和子目录列表,更改目录下的文件可能会导致系统无法启动
/lib : 存放基本代码库(比如c++库),其作用类似于Windows下的DLL文件,几乎所有的应用程序都要用到这些分享库
/bin : 存放着一系列的普通命令
/sbin:存放着只有管理员才能使用的命令
/dev: Device(设备)的缩写,存放的时Linux的外部设备,在Linux中访问设备和访问 文件是一样的
/media:类Windows下的其它设备,如Upan光驱等
/mnt:用于临时挂在其它文件系统
/run:是一个临时文件系统,存储系统启动以来的信息,当系统重启时,这个目录下的文件应该被清除
/lost+found:一般情况下为空,用于存放系统非法关机后的产生的文件
/tmp:用于存放临时文件
/root: 系统管理员用户家目录
/home:除管理员以外的用户家目录所处的目录
/usr: 用户的应用程序和文件基本都放在这个目录下
/usr/bin:系统用户使用的应用程序和指令
/usr/sbin:超级用户使用的程序
/usr/src:内核源代码默认存放的位置
/usr/share:apt-get下载的默认目录
/var:存放经常需要更改的数据(/var/log)
/proc:管理内存空间,是系统内存映射的虚拟目录,可以直接访问这个目录来获取系统信息
/etc目录下的文件
文件 |
描述 |
附 |
/ect/host |
网址域名与其对应的IP地址建立一个关联的数据库 |
通常在网络验证时可以使用,让指定的域名指向本地 |
/etc/passwd |
账号信息文件 |
用户名:密码:UID:GID:描述信息:家目录:默认shell |
/etc/shadow |
密码文件 |
用户名:加密密码:最后一次修改时间:最小修改时间间隔:密码有效期:密码过期前的警告天数,密码过期后的宽限时间:账号失效时间:保留 |
/etc/group |
用户组配置文件 |
组名:密码:GID:用户列表 |
/etc/gshadow |
组用户的密码信息,通常没有设置 |
组名:加密密码:组管理员:组附加用户列表 |
/etc/hostname |
主机名称 |
hostnamectl set-hostname 主机名 |
/etc/profile |
环境变量配置文件 |
系统环境b变量 |
/etc/inittab |
系统运行级别 |
|
/etc/init.d |
通常用户管理某个服务 |
/etc/init.d/networking start 启动网卡 |
/etc/rc.local |
开机后会执行这个文件 |
遍历/etc/profile.d/下的脚本执行 |
/etc/profile.d |
开机后会这些这个目录下的sh脚本 |
|
etc/crontab |
计划任务文件 |
运行crontab就会创建 |
/etc/cron.d |
计划任务目录 |
可以添加任务sh文件,可是:分 时 日 月 周 命令 |
/proc目录下的文件
proc的由来:
linux内核是一个非常庞大、非常复杂的一个单独的程序,对于这样的一个程序来说调试是非常复杂的。像 kernel这样庞大的项目,给里面添加更改一个功能是非常麻烦的,因为你这添加的一个功能可能会影响其他已经有的。
早期内核版本中尽管调试很麻烦,但是高手们还可以凭借个人超凡脱俗的能力去驾驭但是到了2.4左右的版本的时候,这个难度已经非常大了。
为了降低内核调试和学习的难度,内核开发者们在内核中添加了一些属性专门用于调试内核,proc文件系统就是一个尝试。
proc文件系统的思路是:在内核中构建一个虚拟文件系统/proc,内核运行时将内核中一些关键的数据结构以文件的方式呈现在/roc目录中的一些特定文件中,这样相当于将不可见的内核中的数据结构以可视化的方式呈现给内核的开发者。
proc文件系统给了开发者一种调试内核的方法:我们通过实时的观察proc/xxx文件,来观看内核中特定数据结构的值。在我们添加一个新功能的前后来对比,就可以知道这个新功能产生的影响对还是不对。
proc目录下的文件大小都是0,因为这些文件本身并不存在于硬盘中,他也不是一个真实文件,他只是一个接口,当我们去读取这个文件时,其实内核并不是去硬盘上找这个文件,而是映射为内核内部一个数据结构被读取并且格式化成字符串返回给我们。所以尽管我们看到的还是一个文件内容字符串,和普通文件一样的;但是实际上我们知道这个内容是实时的从内核中数据结构来的,而不是硬盘中来的。
文件 |
描述 |
/proc/cpuinfo |
处理器信息相关的文件 |
/proc/devices |
系统已经加载的所有块设备和字符设备的信息 |
/proc/interrupts |
x86/x64体系架构系统上每个IRQ相关的中断号列表 |
/proc/meminfo |
系统中关于当前内存的利用状况等信息 |
/proc/iomem |
每个物理设备上的记忆体(RAM/ROM)在系统中映射信息 |
/proc/version |
当前系统运行的内核版本 |
/proc/N |
PID为N的进程 |
/proc/N/cmdline |
用于查看某个进程启动命令行 |
/proc/N/environ |
该程序运行时使用的环境变量 |
/proc/N/fd |
该程序使用的所有文件描述符 |
/proc/N/maps |
每个可执行文件在内存中映射区域及访问权限所组成的列表 |
/proc/N/mem |
进程所占内存,不可以直接读 |
/proc/N/status |
与stat提供的信息类似,可读性较好 |
/proc/N/stat |
进程当前的状态信息,可读性差 |
VSCODE 开发环境
VSCODE介绍
vscode是微软开发的一款轻量级的编辑器,满足现在绝大部分编程语言的开发,只要安装相应的插件,并且他是支持WIndows和Linux。
Vistual Studio Code作为广受好评的开发工具,已经被越来越多的开发着当作首选的开发工具
Unbutu安装vscode
- 通过snap命令安装sudo snap install –classic code # or code-insiders
- 通过下载deb包安装apt-get install xxx.deb 缺少依赖可以执行sudo apt-get install -f
VSCODE插件
中文插件 搜所 “chinese”
c/c++运行环境 搜索 “c/c”
扩展图标 搜索 “icons”
Linux C编译器与调试器
调试与生成
编写一行代码
1 2 3 4 5 6
| #include <stdio.h> int main() { printf("Hello World"); return 0; }
|
生成配置文件
无论是Linux还是Windows,用户配置文件都放在.vscode下
用户配置是针对某一个工程或文件夹而特别做到,所有配置都放在该文件下的.vscode隐藏文件夹中
配置文件launch.json
注意点击main.c文件后再添加配置,否则没有提示GCC配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| { "version": "0.2.0", "configurations":[ { "name": "gcc", "type":"cppdbg", "request": "launch", "args":[], “stopAtEntry": false,//设为true时程序将暂停在程序入口处,一般设置为true "cwd": "${workspaceFolder}",//调试程序时的工作目录 "environment":[], //(环境变量) "externalConsole":true,//调试时是否显示控制台窗口,一般设置为true显示控制台 "internalConsoleOptions": "neverOpen",//如果不设为 neverOpen,调试时会跳到“调试控制台”选项卡,你应该不需要对gdb手动输命令吧? "MIMode": "gdb", //指定连接的调试器,可以为gdb或1ldb。但目前1db在 windows下没有预编译好的版本。 "miDebuggerPath": "gdb.exe",//调试器路径, Windows下后缀不能省略, Linux下则去掉 "preLaunchTask": "build" //调试会话开始前执行的任务,一般为编译程序。与tasks.json的 label相对应 } ] }
|
配置文件Tasks.json
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| { "tasks": [ { "type": "cppbuild", "label": "C/C++: gcc", "command": "/usr/bin/gcc", "args": [ "-g", "${file}", "-o", "${fileDirname}/${fileBasenameNoExtension}" ], "options": { "cwd": "${workspaceFolder}" }, "problemMatcher": [ "$gcc" ], "group": { "kind": "build", "isDefault": true }, "detail": "调试器生成的任务。" } ], "version": "2.0.0" }
|
Linux编译器与库
GCC介绍
一、什么是Gcc
Linux系统下的Gcc(GNU C Compiler)是GNU推出的功能强大、性能优越的多平台编译器,是GNU的代表作品之一。gcc是可以在多种硬体平台上编译出可执行程序的超级编译器,其执行效率与一般的编译器相比平均效率要高20%~30%。
Gcc编译器能将C、C++语言源程序、汇程式化序和目标程序编译、连接成可执行文件,如果没有给出可执行文件的名字,gcc将生成一个名为a.out的文件。在Linux系统中,可执行文件没有统一的后缀,系统从文件的属性来区分可执行文件和不可执行文件。
二、gcc所遵循的部分约定规则
前面提到编译的后缀问题,而gcc则通过后缀来区别输入文件的类别,下面我们来介绍gcc所遵循的部分约定规则。
.c为后缀的文件,C语言源代码文件;
.a为后缀的文件,是由目标文件构成的档案库文件;
.C或.cc或.cxx为后缀的文件,是C++源代码文件;
.h为后缀的文件,是程序所包含的头文件;
.i为后缀的文件,是已经预处理过的C源代码文件;
.ii为后缀的文件,是已经预处理过的C++源代码文件;
.m为后缀的文件,是Objective-C源代码文件;
.o为后缀的文件,是编译后的目标文件;
.s为后缀的文件,是汇编语言源代码文件;
.S为后缀的文件,是经过预编译的汇编语言源代码文件。
三、Gcc的执行过程
虽然我们称Gcc是C语言的编译器,但使用gcc由C语言源代码文件生成可执行文件的过程不仅仅是编译的过程,而是要经历四个相互关联的步骤∶预处理(也称预编译,Preprocessing)、编译(Compilation)、汇编(Assembly)和连接(Linking)。
命令gcc首先调用cpp进行预处理,在预处理过程中,对源代码文件中的文件包含(include)、预编译语句(如宏定义define等)进行分析。
gcc mian.c -E -o main.i
接着调用cc1进行编译,这个阶段根据输入文件生成以.o为后缀的目标文件。汇编过程是针对汇编语言的步骤,调用as进行工作,一般来讲,.S为后缀的汇编语言源代码文件和汇编
gcc main.i -S -o main.S
s为后缀的汇编语言文件经过预编译和汇编之后都生成以.o为后缀的目标文件。
gcc main.S -c -o main.o
当所有的目标文件都生成之后,gcc就调用ld来完成最后的关键性工作,这个阶段就是连接。在连接阶段,所有的目标文件被安排在可执行程序中的恰当的位置,同时,该程序所调用到的库函数也从各自所在的档案库中连到合适的地方。
gcc main.o -o main
四、Gcc的基本用法和选项
在使用Gcc编译器的时候,我们必须给出一系列必要的调用参数和文件名称。Gcc编译器的调用参数大约有100多个,其中多数参数我们可能根本就用不到,这里只介绍其中最基本、最常用的参数
Gcc最基本的用法是∶gcc[options] [filenames] ,其中options就是编译器所需要的参数,filenames给出相关的文件名称。
五、Gcc的参数选项
-c,只编译,不连接成为可执行文件,编译器只是由输入的.c等源代码文件生成.o为后缀的目标文件,通常用于编译不包含主程序的子程序文件。
-o output_filename,确定输出文件的名称为output_filename,同时这个名称不能和源文件同名。如果不给出这个选项,gcc就给出预设的可执行文件a.out。
-g,产生符号调试工具(GNU的gdb)所必要的符号资讯,要想对源代码进行调试,我们就必须加入这个选项。
-O,对程序进行优化编译、连接,采用这个选项,整个源代码会在编译、连接过程中进行优化处理,这样产生的可执行文件的执行效率可以提高,但是,编译、连接的速度就相应地要慢一些。
-O2,比-O更好的优化编译、连接,当然整个编译、连接过程会更慢。
-Idirname,将dirname所指出的目录加入到程序头文件目录列表中,是在预编译过程中使用的参数。
静态库
之所以成为【静态库】,是因为在链接阶段,会将汇编生成的目标文件.o与引用到的库起链接打包到可执行文件中。因此对应的链接方式称为静态链接。
静态库优点,静态库对函数库的链接是放在编译时期完成的,程序在运行时与函数库再无瓜葛,移植方便。缺点浪费空间和资源,因为所有相关的目标文件与牵涉到的函数库被链接合成一个可执行文件
将所有要加入静态库的文件编译成obj文件
gcc -c fun.c -o fun.o
将.o文件打包生成静态库
ar -crs libfun.a fun.o
编译到main主文件中
gcc main.c -L ./ -lfun -o app
动态库
动态库是为了解决静态库的缺点实现的
动态库再程序编译时并不会链接到目标代码中,而是在程序运行时才被载入,不同的应用程序如果需要调用相同的库,那么在内存里只需要一份该共享库的实例,规避了浪费空间问题。动态库在运行程序时才被载入,也解决了静态库对程序的更新、部署和发布页会带来麻烦,用户只需要更新动态库即可
①生成动态库有两种方式及
gcc -fPIC -shared fun.c -o libfun.so
gcc -fPIC -c fun.c -o fun.o
gcc -shared fun.o -o libfun.so
②生成可执行代码
gcc -g main.o libfun.so -o app
③将动态库安装到系统目录或设置环境变量,否则程序无法运行
- cp libfun.so /usr/lib/
- exprot LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$(pwd)
Linux编程
Linux文件编程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| #include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <fcntl.h> #include <string.h> #include <errno.h>
void WriteFile(char* Name) { int fd = open(Name, O_RDWR | O_CREAT); if(fd == -1) { printf("open error:%s\n", strerror(errno)); return; } char buf[] = {"写入文件的内容\n"}; int ByteNum = write(fd, buf, strlen(buf)); if(ByteNum > 0) { printf("写入成功\n"); } else { printf("写入失败\n"); } close(fd); }
void ReadFile(char* Name) { int fd = open(Name, O_RDONLY); if(fd == -1) { printf("open error:%s\n", strerror(errno)); return; } struct stat fileinfo; fstat(fd,&fileinfo); char* Buffer = (char*)malloc(fileinfo.st_size); int ByteNum = read(fd ,Buffer, fileinfo.st_size); if(ByteNum > 0) { printf("读取成功\n"); } printf("%s\n", Buffer); free(Buffer); close(fd); }
int main() { WriteFile("WriteFile.txt"); ReadFile("WriteFile.txt"); return 0; }
|
Linux进程编程
环境变量操作
相关API
函数 |
描述 |
getenv |
获取指定的环境变量 |
setenv |
设置环境变量 |
unsetenv |
取消指定的环境变量 |
putenv |
设置环境变量 |
进程操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| #include <stdio.h> #include <dirent.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <sys/types.h> #include <sys/wait.h>
void EnumProcess() {
struct DIR *dir = opendir("/proc"); struct dirent * ent; int pid; while(ent = readdir(dir)) { if(ent->d_name[0] >='0' && ent->d_name[0]<='9' ) { sscanf(ent->d_name,"%d",&pid); char allpath[100]; sprintf(allpath,"/proc/%d/status",pid); int fd = open(allpath,O_RDONLY); char buf[1024]={}; read(fd,buf,1024); close(fd); char processName[100]; sscanf(buf,"Name: %s",processName); printf("PID:%d name:%s \n",pid,processName); } } closedir(dir); }
pid_t CreatProcess(char*path) { pid_t pid = fork(); if(pid==0) { execl(path,NULL); printf("child %d\n",getpid()); }else if (pid <0) { printf("创建错误\n"); } return pid; } void WaitProcess() { pid_t pid; pid = fork(); if(pid == 0) { printf("我是子进程 %d \n",getpid()); sleep(3); }else if (pid > 0) { int status; wait(&status); printf("我是父进程 %d\n",getpid()); }else{ printf("创建失败 %d\n"); } } int main() { EnumProcess(); CreatProcess("/home/pb/010editor/010editor"); WaitProcess(); return 0; }
|
Linux线程编程
tasks.json配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| { "tasks": [ { "type": "cppbuild", "label": "C/C++: gcc 生成活动文件", "command": "/usr/bin/gcc", "args": [ "-g", "${file}", "-lpthread", "-o", "${fileDirname}/${fileBasenameNoExtension}" ], "options": { "cwd": "${workspaceFolder}" }, "problemMatcher": [ "$gcc" ], "group": { "kind": "build", "isDefault": true }, "detail": "调试器生成的任务。" } ], "version": "2.0.0" }
|
线程创建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include <stdio.h> #include <unistd.h> #include <pthread.h>
void *thread_proc(void * arg) { for (int i = 0; i < 100; i++) { printf("[%d] child %08x \n",i,pthread_self()); } pthread_detach(pthread_self()); } int main() { pthread_t tid; pthread_create(&tid,NULL,thread_proc,NULL); pthread_join(tid,NULL);
}
|
线程同步
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| #include <stdio.h> #include <pthread.h> #include <unistd.h> #include <semaphore.h>
sem_t g_sem;
int g_source = 0;
void *workthread1(void * param){ for (size_t i = 0; i < 1000000; i++){ sem_wait(&g_sem); g_source += 1; sem_post(&g_sem);
}
return NULL; }
void *workthread2(void * param){ for (size_t i = 0; i < 1000000; i++){ sem_wait(&g_sem); g_source += 1; sem_post(&g_sem);
}
return NULL; }
int main(){
sem_init( &g_sem, 0, 1 );
pthread_t threads[2]={}; pthread_create(&threads[0],NULL,workthread1,0); pthread_create(&threads[1],NULL,workthread2,0); for (size_t i = 0; i < 2; i++) { pthread_join(threads[i],NULL); } printf("g_source = %d \n",g_source);
sem_destroy(&g_sem);
return 0; }
|
Linux网络编程
不同的协议层对数据包有不同的称谓,在传输层叫做段(segment),在网络层叫做数据报(datagram),在链路层叫做帧(frame)。数据封装成帧后发到传输介质上,到达目的主机后每层协议再剥掉相应的首部,最后将应用层数据交给应用程序处理
为了方便应用层对网络通讯,由加利福尼亚大学 Berkeley分校开发了一套网络通讯接口(SOCKET),最初是为UNIX操作系统开发的,后来随着uniⅸ操作系统的广泛使用,套接字成为当前最流行的网络通信应用程序接口之一。
Linux下的网络编程与 Windows下非常类似,使用也是 socket编程(windows抄袭的 Linux).
相关头文件
1 2 3 4 5
| #include<sys/types.h>/类型定义 #include<sys/socket.h>提供网络通信函数 #include<unistd. h>针对系统调用的封装fork, pipe read write等 #include<netinet/in.h>互联网地址族定义数据结构 sockaddr_in #include<arpa/inet.h>提供IP地址转换函数
|
相关API
函数 |
描述 |
int socket(int domain, int type, int protocol); |
创建 socket对象 |
int bind(int sockfd, const struct sockaddr *addrsocklen_t addrlen); |
绑定端口 |
int listen(int sockfd, int backlog); |
监听端口 |
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); |
连接主机 |
int accept(int sockfd, struct sockaddr *, socklen_t *addrlen); |
接受连接 |
ssize_t read(int fd, void *buf, size_t count); |
接收数据 |
ssize_t recv(int sockfd, void *buf, size_t len, int flags): |
接收数据 |
ssize_t write(int fd, const void *buf, size_t count); |
发送数据 |
ssize_t send(int sockfd, const void *buf, size_t len, int flags); |
发送数据 |
int close(int fd); |
关闭 socket对象 |
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen); |
UDP发送数据 |
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src addr, socklen_t *addrlen); |
UDP接收数据 |
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags); |
发送数据 |
ssize_t recvmsg (int sockfd, struct msghdr *msg, int fags); |
接收消息 |
TcpServer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| #include <sys/types.h> // 类型定义 #include <sys/socket.h> // 提供网络通信函数 #include<unistd.h> // 针对系统调用的封装 fork,pipe read write 等 #include<netinet/in.h> // 互联网地址族 定义数据结构sockaddr_in #include<arpa/inet.h> // 提供IP地址转换函数 #include <stdio.h>
int main() { int server_sock = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
struct sockaddr_in addr; addr.sin_port = htons(1234); addr.sin_family =AF_INET; addr.sin_addr.s_addr = inet_addr("127.0.0.1"); bind(server_sock,(struct sockaddr *)&addr,sizeof(addr));
listen(server_sock,SOMAXCONN); printf("listen......\n");
struct sockaddr_in clinet_addr={}; int addr_len =sizeof(clinet_addr);
int client_sock = accept(server_sock,(struct sockaddr *)&clinet_addr,&addr_len);
printf("connect .... \n"); send(client_sock,"hello",6,0); printf("send msg to clinet .... \n");
char buf[1024]={}; recv(client_sock,buf,1024,0); printf("recv client: %s \n",buf);
close(client_sock); close(server_sock); }
|
TcpClien
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| #include <sys/types.h> // 类型定义 #include <sys/socket.h> // 提供网络通信函数 #include<unistd.h> // 针对系统调用的封装 fork,pipe read write 等 #include<netinet/in.h> // 互联网地址族 定义数据结构sockaddr_in #include<arpa/inet.h> // 提供IP地址转换函数 #include <stdio.h> #include <stdlib.h> #include <string.h> int main() { int sock = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
struct sockaddr_in addr; addr.sin_port = htons(1234); addr.sin_family =AF_INET; addr.sin_addr.s_addr = inet_addr("127.0.0.1"); connect(sock,(struct sockaddr*)&addr,sizeof(addr)); printf("connect server.....\n");
char buf[1024]={}; recv(sock,buf,1024,0); printf("recv server msg: %s \n",buf);
printf("input send msg:"); scanf("%s",buf);
send(sock,buf,strlen(buf)+1,0); printf("send msg to server .... \n");
close(sock); }
|
Linux内存管理
内存相关API
函数 |
描述 |
int brk(void* addr); |
分配堆空间(linux系统API) |
void *sbrk(int ptr_t incrememt); |
获取系统维护堆指针 |
void *malloc(size_t size); |
c语言分配堆内存 |
void free(void *ptr); |
c语言释放堆内存 |
void mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offser); |
分配虚拟内存空间(liunx系统API) |
int munmap(void *addr, size_t length); |
释放虚拟内存空间 |
int mprotect(const void *start, size_t length, int prot); |
修改指定内存属性 |
分配空间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <sys/mman.h> #include <stdio.h> #include <error.h> #include <string.h> int main() { char* p; void *heap1 = sbrk(0); brk(heap1 + 1); p = (char*)heap1; p[0] ='a'; brk(heap1 + 100); strcpy(heap1, "hello"); printf(heap1); fflush(stdout);
brk(heap1); return 0; }
|
虚拟内存分配
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #include <sys/mman.h> #include <unistd.h> #include <stdio.h> #include <string.h>
int main() { void *padd; padd = mmap( 0, 4096, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, 0, 0 ); char* pbuf = (char*)padd;
strcpy(pbuf,"hello"); printf("%s\n",pbuf);
munmap(padd,4096);
}
|
修改内存保护属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include <unistd.h> #include <stdio.h> #include <sys/mman.h>
int main() { char * pName = "hello world"; mprotect( ((__int64_t)pName)/4096*4096, 4096, PROT_READ|PROT_WRITE|PROT_EXEC );
pName[0] = '1'; printf("%s\n",pName); }
|
Linx信号编程
发送信号相关API
函数 |
描述 |
int kill(pid_t pid, int signo); |
给进发送一个信号 |
int raise(int signo); |
给当前进程发送一个信号 |
void abort(void); |
是当前进程接收到SIGABRT信号而异常终止 |
usingned int alarm(usingned int seconds); |
设定一个闹钟,告诉内核多少秒后给当前进程发送SIGALRM信消息,默认终止当前进程 |
信号发送
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| #include <unistd.h> #include <stdio.h> #include <signal.h> #include <stdlib.h> int main(void) { pid_t pid = getpid(); printf("kill SIGTSTP\n"); kill(pid,SIGTSTP);
printf("raise SIGTSTP\n"); raise(SIGTSTP); printf("alarm(1) SIGALARM\n"); alarm(1); for (int i = 0;; i++){ printf("%d \n",i); } abort(); printf("run end \n"); return 0; }
|
信号闹钟
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <unistd.h> #include <stdio.h> #include <signal.h> #include <stdlib.h>
void alrmhandler(int sig){ printf("产生一个信号\n"); signal(SIGALRM,SIG_DFL); }
int main() { printf("pid:%d",getpid()); signal(SIGALRM,alrmhandler); kill(getpid(), SIGALRM); printf("正常执行\n"); getchar(); }
|
信号屏蔽
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| #include <unistd.h> #include <stdio.h> #include <signal.h> #include <stdlib.h>
void inthandler(int sig){ printf("产生一个信号\n"); } int main(void){ struct sigaction act = {}; act.sa_handler = inthandler;
act.sa_flags = 0; sigemptyset(&act.sa_mask); sigaction(SIGINT,&act,0); while (1){ printf("%d \n",getpid()); sleep(1); } return 0; }
|
动态与静态
动态
配置文件tasks.json
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| { "tasks": [ { "type": "cppbuild", "label": "C/C++: gcc 生成活动文件", "command": "/usr/bin/gcc", "args": [ "-g", "${file}", "-ldl", "-o", "${fileDirname}/${fileBasenameNoExtension}" ], "options": { "cwd": "${workspaceFolder}" }, "problemMatcher": [ "$gcc" ], "group": { "kind": "build", "isDefault": true }, "detail": "调试器生成的任务。" } ], "version": "2.0.0" }
|
简单示列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| int fun(int a,int b);
#include "fun.h"
int fun(int a,int b) { return a+b; }
include <stdio.h> #include "fun.h"
int fun(int a,int b) { return a+b; }
int main() { printf(" :%d \n",fun(10,30)); }
|
示列2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #include <stdio.h> #include <dlfcn.h>
int main() { void *handle; handle = dlopen("./libfun.so",RTLD_LAZY); if(handle == NULL) { printf("open error :%s\n",dlerror()); return 0; } typedef int (*FUN)(int,int); FUN pfun = dlsym(handle,"fun"); if(pfun == NULL) { printf("open error :%s\n",dlerror()); dlclose(handle); return 0; } printf(" %d \n",pfun(10,20)); dlclose(handle); }
|
Linux Shell编程
Linux MakeFile
Linux ELF文件格式