目录
4.more 分屏显示:空格键(翻页) 回车键(一行一行往下翻)
5.head 默认打印文件前十行(head -n 打印文件前n行)
7.tail 默认打印文件末尾十行(tail -n 打印文件后n行)
9.grep 过滤、筛选所要的字符:(-i 忽略大小写 -c 只显示统计行数 -v 不包含匹配内容)
10.wc统计文件中单词的个数(-w)、字符个数(-c)、行数(-l)
(ps -f 显示更详细信息)(ps -ef 显示全部终端信息)
2.kill 结束进程:(-9 强制结束)(pkill 进程名称 这一名称的所有进程全部结束)
1.tar:将文件打包或解包:c 创建包文件 f 指定目标为文件而不是设备 v 显示详细过程 t 显示包中的内容而不释放
2.OSI参考模型(开放式系统互联参考模型):下层为上层服务
6.ET模式:边缘触发只有数据到来才触发,不管缓存区中是否还有数据。
Ctrl shift + 放大终端
Ctrl - 缩小终端
Ctrl Alt t 打开新终端
普通用户:
管理员用户:
一.目录结构:
/ 根目录(文件夹)
bin 里面存放命令
boot 与内核有关文件、系统启动
etc 系统配置文件
lib 库文件
home 家目录
二.文件类型:
d 目录(文件夹)
- 普通文件
p 管道文件
l 链接文件
c,b设备文件
s 套接字文件
三.基础命令:
ls 显示当前位置有哪些文件(ls -l 显示常信息)(ls -a 显示隐藏文件)
类型 权限 属主 属组
cd 进入文件 (cd ~进入家目录)(cd .. 返回上一层)(cd . 代表当前位置)cd - 在最近去过的两个文件之间移动
rm 删除普通文件(rm -r 删除目录文件(文件夹))
权限修改:
chmod u(属主),g(同组人),o(其他人),a(所有人)
r 读 4 w 写 2 x 执行 1 - 无 0
数字法修改权限:
chmod 777 文件名
pwd 显示当前位置
touch 创建普通文件
mkdir 创建目录文件(文件夹)
安装软件(命令):
1.进入管理员模式:sudo su
apt install 软件名称
如果安装有问题执行该命令:apt update
2.退出管理员:exit
关机与重启:
shutdown -h now 立刻关机 halt 关机 init 0 关机
shutdown -r now 立刻重启 reboot 重启 init 6 重启
四.vim的使用
1.vim的三种命令模式:
命令模式、编辑模式、末行模式
2.模式之间的切换:
在命令模式下,输入i、a、o进入编辑模式;
编辑模式下,按下esc
,可以回到命令模式;
命令模式下,输入:、?、\
后,进入末行模式;
末行模式下(:wq 保存并退出、:q 只退出、:w 只保存、:q!不保存退出)
3.文件编译生成:
gcc -o main main.c
./main
C语言 预编译 编译 汇编 链接
gcc -c main.c -o main.o -i main.i
4.其他操作:
复制 nyy 粘贴 p 删除 ndd
u 撤销 Ctrl + r 恢复一次 清空一行 cc
跳转到第n行 :n 跳转到第一行 gg 跳转到最后一行 G
从光标所在行删除到末尾 dG
五.文件的操作命令:
1.cp 拷贝文件:
2.mv 移动文件、重命名:
3.cat 打印文件内容:适合小文件
合并文件内容:
向文件写入数据:Ctrl+c 退出
4.more 分屏显示:空格键(翻页) 回车键(一行一行往下翻)
5.head 默认打印文件前十行(head -n 打印文件前n行)
6.less 可以反复查看文件内容(退出 q)
7.tail 默认打印文件末尾十行(tail -n 打印文件后n行)
8.find 搜索文件:find 路径 -name 文件名
9.grep 过滤、筛选所要的字符:(-i 忽略大小写 -c 只显示统计行数 -v 不包含匹配内容)
10.wc统计文件中单词的个数(-w)、字符个数(-c)、行数(-l)
六.进程管理:
1.ps 显示当前终端中运行的进程(命令):
(ps -f 显示更详细信息)(ps -ef 显示全部终端信息)
2.kill 结束进程:(-9 强制结束)(pkill 进程名称 这一名称的所有进程全部结束)
3.后台运行任务以及用jobs查看:
Ctrl + c 结束前台运行的进程
Ctrl + z 停止一个前台运行的进程
4.将前后台运行的进程互换:
bg % 任务号 把前台运行的移动到后台(得先停止前台运行的进程)
fg % 任务号 后台移动到前台执行
七.文件压缩与解压命令:
1.tar将文件解包或打包:
c 创建包文件 f 指定目标为文件而不是设备 v 显示详细过程
t 显示包中的内容而不释放 x 释放包中内容
2.压缩:gzip 文件名称
创建新用户:adduser 用户名称
修改密码: passwd 用户名称
删除用户:deluser 用户名称 userdel -r 用户名称
八.makefile文件:
九.GDB调试:
1.编译时生成调试版 -g
2.GDB安装
3. l 显示代码 b 行号 在第几行加断点 info break 显示断点信息
r 启动程序 n 单步执行 p 打印 q 退出
c 继续执行 从当前位置运行到下一个断点 s 进入函数 finish 跳出函数
l xx.c : n 行号 跳转到xx.c的第n行 delete 删除断点
十.库文件:预先编译好的方法的集合
printf()库函数 头文件声明 /usr/include 库文件存放的标准目录 /usr/lib
1.静态库:libxx.a
a.将所有的.c编译,生成.o
b.做库 ar crv -o libfo.a fo.o
c.使用库 gcc -o main main.c -L -lfo
fo.h 在当前位置 #include "fo.h"
mv fo.h /usr/include #include<fo.h>
gcc -o main main.c -L -lfo
mv libfo.a /usr/lib
gcc -o main main.c -lfo
程序使用静态库,编译后,用到的库方法会复制到生成的可执行程序中。 直接运行当前程序,不需要再去链接静态库
2.共享库(win动态链接库):libxx.so
gcc -shared -fPIC -o libxx.so fo.o
程序使用共享库,编译链接后,可执行程序只标记用到的库方法,并不包含。运行程序时,动态链接库用到的库。
3.ldd 文件名(文件使用了哪些库(只能看共享库))
系统默认优先使用共享库(减少磁盘所占用内存)
4.设置环境变量使先从当前开始寻找库文件:
十一.进程复制与替换:
进程:一个正在运行的程序
1.主函数的三个内容:
参数个数、参数内容、环境变量
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
//参数个数 参数内容 环境变量
int main(int argc, char* argv[],char* envp[])
{
printf("argc=%d\n",argc);
for(int i =0;i < argc;i++)
{
printf("argv[%d]=%s\n",i,argv[i]);
}
for(int i = 0; envp[i] != NULL; i++)
{
printf("envp[%d]=%s\n",i,envp[i]);
}
exit(0);
}
逻辑地址:是指程序在运行过程中使用的地址,也称为虚拟地址(Virtual Address)。它是由CPU生成的,用于访问内存中的数据。逻辑地址的大小和位数取决于处理器的架构和操作系统的设计,通常是一个定长的二进制数值。在执行指令时,CPU通过将逻辑地址转化为物理地址来获取数据。
物理地址:是指内存中实际的地址,也称为实地址(Real Address)。物理地址表示内存模块中每个存储单元(通常是字节)的唯一标识符,因此具有唯一性,且直接与内存相关联。物理地址通常是一个以十六进制表示的数字,它确定了计算机中的实际内存位置。
PCB 进程描述符(结构体)内核实现:task_struct
2.简单的fork()进程:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
//参数个数 参数内容 环境变量
int main(int argc, char* argv[],char* envp[])
{
char * s = NULL;
int n = 0;
pid_t pid = fork();
if( pid == -1 )
{
exit(1);
}
if( pid == 0 )
{
n = 3;
s = "child";
}
else
{
n = 7;
s = "parent";
}
for( int i = 0; i < n; i++)
{
printf("s=%s\n",s);
sleep(1);
}
exit(0);
}
3.孤儿进程:
父进程先于子进程结束,子进程变为孤儿进程,系统重新分配一个父进程
4.僵死进程:
子进程先结束,父进程未获取子进程的退出码
wait()用来获取退出码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/wait.h>
int main()
{
int n =0;
char* s = NULL;
pid_t pid = fork();
if( pid == -1 )
{
exit(0);
}
if( pid == 0 )
{
s = "child";
n = 3;
}
else
{
s = "parent";
n = 7;
int val = 0;
wait(&val); //可能阻塞进程
if( WIFEXITED(val) ) //判断子进程是否正常退出
{
printf("val=%d\n",WEXITSTATUS(val)); //获取退出码
}
}
for(int i = 0; i < n; i++)
{
printf("s=%s,curr_pid=%d,ppid=%d\n",s,getpid(),getppid());
sleep(1);
}
exit(3);
}
地址空间 32位 小于3G
物理内存大小 分配内存够用则成功,否则失败。(不考虑虚拟内存)
若涉及虚拟内存则:物理内存剩余大小+虚拟内存剩余空间。
5.进程替换:
fork()+exec()
execl execlp execle execv execvp (库函数)
execve(系统调用)
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>
int main(int argc, char* argv[],char* envp[])
{
printf("pid =%d\n",getpid());
//execl("/usr/bin/ps","ps","-f",(char*)0); 失败才返回
//execlp("ps","ps","-f",(char*)0);
//execle("/usr/bin/ps","ps","-f",(char*)0,envp); 多了一个环境变量
/*
char* myargv[] = {"ps","-f",0};
execv("/usr/bin/ps",myargv);
*/
/*
char* myargv[] = {"ps","-f",0};
execvp("ps",myargv); 不需要加路径
*/
char* myargv[] = {"ps","-f",0};
execve("/usr/bin/ps",myargv,envp);
printf("execl err\n");
exit(1);
}
十二.系统调用:
open read write close
read() 返回值为0,说明读到文件末尾了
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<fcntl.h>
// ./mycp 将13.png 在复制一份 14.png
int main(int argc,char* argv[])
{
if( argc != 3 )
{
printf("argc err\n");
exit(1);
}
char* filename = argv[1];
char* newfilename = argv[2];
int fdr = open(filename,O_RDONLY);
int fdw = open(newfilename,O_WRONLY|O_CREAT,0600);
if( fdr == -1 || fdw == -1 )
{
printf("open file err\n");
exit(1);
}
char buff[1024] = {0};
int num =0;
while((num=read(fdr,buff,1024))>0)
{
write(fdw,buff,num);
}
close(fdr);
close(fdw);
exit(0);
}
父进程打开的文件,fork之后,子进程也可以访问。
父子进程共享打开的文件(文件偏移量)
写时拷贝:
写时拷贝技术实际上是一种拖延战术,是为了提高效率而产生的技术,这怎么提高效率呢?实际上就是在需要开辟空间时,假装开了空间,实际上用的还是原来的空间,减少开辟空间的时间,等到真正要使用新空间的时候才去真正开辟空间。
十三.进程间通信:
1.进程间通信:(IPC机制)
管道:可以进行数据传输,具有单向导通性以及阻塞
共享内存:多个进程共享一块数据,可以随时读取以及更改
信号量集:同步保护资源
消息队列:最符合通信思想,单纯收发数据
套接字:用于网络通信
2.信号:
软中断模拟机制,类似于通知
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<signal.h>
#include<unistd.h>
void fun(int sig)
{
printf("sig=%d\n",sig);
signal(sig,SIG_DFL);
}
int main()
{
signal(SIGINT,fun);
//signal(SIGINT,SIG_IGN); //忽略信号
while(1)
{
printf("hello pid=%d\n",getpid());
sleep(1);
}
}
3.管道:(通信方式:半双工)
管道写入的数据在内存中
有名管道:mkfifo(任意两个进程之间通信)
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<fcntl.h>
#include<string.h>
int main()
{
int fd = open("./fifo",O_WRONLY);//写入数据
if( fd == -1 )
{
exit(1);
}
printf("fd=%d\n",fd);
while(1)
{
printf("input:\n");
char buff[128] = {0};
fgets(buff,128,stdin);
if( strncmp(buff,"end",3) == 0 )
{
break;
}
write(fd,buff,strlen(buff));
}
close(fd);
}
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<fcntl.h>
#include<string.h>
int main()
{
int fd = open("./fifo",O_RDONLY);//读数据
if( fd == -1 )
{
exit(1);
}
printf("fd=%d\n",fd);
while(1)
{
char buff[128] = {0};
int n = read(fd,buff,127);//阻塞
if( n == 0 )
{
break;
}
printf("buff=%s\n",buff);
}
close(fd);
exit(0);
}
无名管道:(父子进程或有亲缘关系的进程)
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
int main()
{
int fd[2]; // fd[0] r, fd[1] w
if(pipe(fd) == -1)
{
exit(1);
}
pid_t pid = fork();
if( pid == -1 )
{
exit(1);
}
if( pid == 0 )
{
close(fd[1]);
char buff[128] = {0};
read(fd[0],buff,127);
printf("child read:%s\n",buff);
close(fd[0]);
}
else
{
close(fd[0]);
printf("input:\n");
char buff[128] = {0};
fgets(buff,128,stdin);
write(fd[1],buff,strlen(buff));
}
exit(0);
}
4.信号量:
p 获取资源 信号量-1 信号量为0时阻塞
v 释放资源 信号量+1
创建,初始化 semget、semctl
p操作 semop
v操作 semop
删除信号量 semctl
sem.h
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/sem.h>
union semun
{
int val;
};
void sem_init();
void sem_p();
void sem_v();
void sem_destroy();
sem.c
#include "sem.h"
static int semid = -1;
void sem_init()
{
semid = semget((key_t)1234,1,IPC_CREAT|IPC_EXCL|0600);//全新创建
if( semid == -1 )//全新创建失败,那么之间获取已存在的信号量id
{
semid = semget ((key_t)1234,1,IPC_CREAT|0600);
if( semid == -1 )
{
printf("semget err\n");
return;
}
}
else//全新创建成功,初始化
{
union semun a;
a.val = 1;
if (semctl(semid,0,SETVAL,a) == -1)
{
printf("semget err\n");
}
}
}
void sem_p()
{
struct sembuf buf;
buf.sem_num = 0;//信号量的下标,目前只有一个所以为0
buf.sem_op = -1;//p
buf.sem_flg = SEM_UNDO;
if( semop(semid,&buf,1) == -1 )
{
printf("semop p err\n");
}
}
void sem_v()
{
struct sembuf buf;
buf.sem_num = 0;//信号量的下标,目前只有一个所以为0
buf.sem_op = 1;//v
buf.sem_flg = SEM_UNDO;
if( semop(semid,&buf,1) == -1 )
{
printf("semop v err\n");
}
}
void sem_destroy()
{
if(semctl(semid,0,IPC_RMID) == -1)
{
printf("semctl destroy err\n");
}
}
a.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include "sem.h"
int main()
{
sem_init();
for( int i = 0; i < 5; i++)
{
sem_p();
printf("A");
fflush(stdout);
int n = rand() % 3;
sleep(n);
printf("A");
fflush(stdout);
sem_v();
n = rand() % 3;
sleep(n);
}
}
b.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include "sem.h"
int main()
{
sem_init();
for( int i = 0; i < 5; i++)
{
sem_p();
printf("B");
fflush(stdout);
int n = rand() % 3;
sleep(n);
printf("B");
fflush(stdout);
sem_v();
n = rand() % 3;
sleep(n);
}
sleep(10);
sem_destroy();
}
5.消息队列:
消息:结构体
若消息类型为0则不区分消息类型全部接收消息
手动删除消息队列: ipcrm -q 队列id
msgget() 获得消息若没有则创建
msgrcv() 读取消息
msgsnd() 发送消息
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/msg.h>
#include<unistd.h>
struct mess
{
long type; //消息类型
char buff[128];
};
int main()
{
int msgid = msgget((key_t)1234,IPC_CREAT|0600);
if ( msgid == -1 )
{
exit(0);
}
struct mess m;
m.type = 1;
strcpy(m.buff,"hello1");
msgsnd(msgid,&m,128,0);
exit(0);
}
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/msg.h>
#include<unistd.h>
struct mess
{
long type;
char buff[128];
};
int main()
{
int msgid = msgget((key_t)1234,IPC_CREAT|0600);
if ( msgid == -1 )
{
exit(0);
}
struct mess m;
msgrcv(msgid,&m,128,1,0);
printf("read msg:%s\n",m.buff);
exit(0);
}
十四.线程:(进程内部的一条执行路径)
1.线程实现:
Linux系统为内核级
用户级线程:由用户自己(并发、交替进行)
内核级线程:(并行、同时进行)
组合:用户+内核
pthread_create()
pthread_exit()
pthread_join()
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
int g_val =1;
void* fun(void* arg)
{
for(int i =0; i < 1000; i++)
{
printf("g_val=%d\n",g_val++);
}
}
int main()
{
pthread_t id[5];
for(int i =0; i < 5; i++)
{
pthread_create(&id[i],NULL,fun,NULL);
}
for( int i =0; i < 5; i++)
{
pthread_join(id[i],NULL);
}
exit(0);
}
./main2 > file.txt
显示重复内容:uniq -d 参数
2.共享内存 :
ipcs:用于显示与进程间通信有关的信息。
a.c:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/shm.h>
#include "sem.h"
int main()
{
int shmid = shmget((key_t)1234,128,IPC_CREAT|0600);
if( shmid == -1 )
{
exit(1);
}
char * s = (char*)shmat(shmid,NULL,0);
if( s == (char*)-1 )
{
exit(1);
}
sem_init();
while(1)
{
char buff[128] = {0};
fgets(buff,128,stdin);
sem_p(0);//ps1
strcpy(s,buff);
sem_v(1);//vs2
if( strncmp(buff,"end",3) == 0 )
{
break;
}
}
shmdt(s);
exit(0);
}
b.c:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/shm.h>
#include "sem.h"
int main()
{
int shmid = shmget((key_t)1234,128,IPC_CREAT|0600);
if( shmid == -1 )
{
exit(1);
}
char * s = (char*)shmat(shmid,NULL,0);
if( s == (char*)-1 )
{
exit(1);
}
sem_init();
while(1)
{
sem_p(1);//ps2
if(strncmp(s,"end",3) == 0 )
{
break;
}
printf("s=%s\n",s);
sem_v(0);//vs1
}
sem_destroy();
shmdt(s);
shmctl(shmid,IPC_RMID,NULL);
exit(0);
}
sem.c
#include "sem.h"
static int semid = -1;
void sem_init()
{
semid = semget((key_t)1234,2,IPC_CREAT|IPC_EXCL|0600);
if( semid == -1 )
{
semid = semget((key_t)1234,2,IPC_CREAT|0600);
if( semid == -1 )
{
printf("semget err\n");
return;
}
}
else
{
union semun a;
int arr[2] ={1,0};
for(int i =0;i<2;i++)
{
a.val = arr[i];
if (semctl(semid,i,SETVAL,a) == -1)
{
printf("semctl setval err\n");
}
}
}
}
void sem_p(int index)
{
struct sembuf buf;
buf.sem_num = index;
buf.sem_op = -1;
buf.sem_flg = SEM_UNDO;
if( semop(semid,&buf,1) == -1 )
{
printf("semop p err\n");
}
}
void sem_v(int index)
{
struct sembuf buf;
buf.sem_num= index;
buf.sem_op = 1;
buf.sem_flg = SEM_UNDO;
if( semop(semid,&buf,1) == -1 )
{
printf("semop v err\n");
}
}
void sem_destroy()
{
if( semctl(semid,0,IPC_RMID) == -1 )
{
printf("semctl destroy err\n");
}
}
sem.h
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/sem.h>
union semun
{
int val;
};
void sem_init();
void sem_p(int index);
void sem_v(int index);
void sem_destroy();
3.线程同步:(同步方法:信号量、互斥锁、条件变量、读写锁)
并发运行:在一段时间内,两者都有执行
并行:在某一刻,两个都有执行(必须有多个处理器)
信号量、互斥锁、条件变量、读写锁
三个线程:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
#include<semaphore.h>
sem_t sem1,sem2,sem3;
void* fun1(void* arg)
{
for( int i=0 ; i < 5; i++)
{
sem_wait(&sem1);//可能阻塞
printf("A");//p操作
fflush(stdout);
sem_post(&sem2);//v操作
int n = rand() % 3;
sleep(n);
}
}
void* fun2(void* arg)
{
for( int i=0 ; i < 5; i++)
{
sem_wait(&sem2);//p操作
printf("B");
fflush(stdout);
sem_post(&sem3);//v操作
int n = rand() % 3;
sleep(n);
}
}
void* fun3(void* arg)
{
for(int i =0; i < 5; i++)
{
sem_wait(&sem3);//p操作
printf("C");
fflush(stdout);
sem_post(&sem1);//v操作
int n = rand() % 3;
sleep(n);
}
}
int main()
{
sem_init(&sem1,0,1);
sem_init(&sem2,0,0);
sem_init(&sem3,0,0);
pthread_t id1,id2,id3;
pthread_create(&id1,NULL,fun1,NULL);
pthread_create(&id2,NULL,fun2,NULL);
pthread_create(&id3,NULL,fun3,NULL);
pthread_join(id1,NULL);
pthread_join(id2,NULL);
pthread_join(id3,NULL);
sem_destroy(&sem1);
sem_destroy(&sem2);
sem_destroy(&sem3);
exit(0);
}
3.1互斥锁:
互斥锁是是一种用于多线程编程的同步原语,用于确保在多个线程访问共享资源时的互斥性。在多线程环境中,当多个线程同时访问共享资源时,可能会导致数据的竞争和不一致问题。为了避免这种问题,需要确保在任何时候只有一个线程能够访问共享资源,而其他线程需要等待直到资源可用。互斥锁提供这样一种机制,即在某个线程访问共享资源时,它会有互斥锁,其他线程需要等待互斥锁的释放才能访问共享资源。一旦线程完成对共享资源的访问,它会释放互斥锁,以便其他线程可以获取互斥锁并访问共享资源。
pthread_mutex_init()
pthread_mutex_lock() //加锁,可能阻塞
pthread_mutex_unlock()
pthread_mutex_destoy()
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
#include<semaphore.h>
pthread_mutex_t mutex;
void* fun1(void* arg)
{
for(int i =0; i<5 ;i++)
{
pthread_mutex_lock(&mutex);// lock == p 可能阻塞
printf("A");
fflush(stdout);
int n = rand() % 3;
sleep(n);
printf("A");
fflush(stdout);
pthread_mutex_unlock(&mutex);// unlock == v
n =rand() % 3;
sleep(n);
}
}
void* fun2(void* arg)
{
for(int i =0; i<5 ;i++)
{
pthread_mutex_lock(&mutex);// lock == p 可能阻塞
printf("B");
fflush(stdout);
int n = rand() % 3;
sleep(n);
printf("B");
fflush(stdout);
pthread_mutex_unlock(&mutex);// unlock == v
n =rand() % 3;
sleep(n);
}
}
int main()
{
pthread_mutex_init(&mutex,NULL);
pthread_t id1,id2;
pthread_create(&id1,NULL,fun1,NULL);
pthread_create(&id2,NULL,fun2,NULL);
pthread_join(id1,NULL);
pthread_join(id2,NULL);
pthread_mutex_destroy(&mutex);
exit(0);
}
3.2条件变量:
先在条件变量队列上阻塞、等待通知
pthread_cond_init()
pthread_cond_wait()
pthread_cond_signal() 只唤醒一个线程
pthread_cond_broadcast() 唤醒所有线程
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
pthread_cond_t cond;
pthread_mutex_t mutex;
void* funa(void* arg)
{
char* s = (char*)arg;
while(1)
{
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond,&mutex);//添加到条件变量的等待队列中
pthread_mutex_unlock(&mutex);
if( strncmp(s,"end",3) == 0 )
{
break;
}
printf("FunA:%s\n",s);
}
}
void* funb(void* arg)
{
char* s = (char*)arg;
while(1)
{
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond,&mutex);//添加到条件变量的等待队列中
pthread_mutex_unlock(&mutex);
if( strncmp(s,"end",3) == 0 )
{
break;
}
printf("FunB:%s\n",s);
}
}
int main()
{
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&cond,NULL);
pthread_t id1,id2;
char buff[128] = {0};
pthread_create(&id1,NULL,funa,buff);
pthread_create(&id2,NULL,funb,buff);
while(1)
{
char tmp[128] = {0};
fgets(tmp,128,stdin);
strcpy(buff,tmp);
if( strncmp( tmp,"end",3 ) == 0 )
{
pthread_mutex_lock(&mutex);
pthread_cond_broadcast(&cond);//唤醒所有线程
pthread_mutex_unlock(&mutex);
break;
}
else
{
pthread_mutex_lock(&mutex);
pthread_cond_signal(&cond);//唤醒一个线程(随机)
pthread_mutex_unlock(&mutex);
}
}
pthread_join(id1,NULL);
pthread_join(id2,NULL);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
exit(0);
}
3.3读写锁:
R R 可以 R W 不可以 W W 不可以
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
pthread_rwlock_t lock;//读写锁初始化
void* fun_r1(void* arg)
{
for(int i =0; i < 10; i++)
{
pthread_rwlock_rdlock(&lock);
printf("fun r1 start\n");
int n = rand() % 3;
sleep(n);
printf("fun r1 end\n");
pthread_rwlock_unlock(&lock);
n = rand() % 3;
sleep(3);
}
}
void* fun_r2(void* arg)
{
for(int i =0; i < 10; i++)
{
pthread_rwlock_rdlock(&lock);
printf("fun r2 start\n");
int n = rand() % 3;
sleep(n);
printf("fun r2 end\n");
pthread_rwlock_unlock(&lock);
n = rand() % 3;
sleep(3);
}
}
void* fun_w(void* arg)
{
for(int i =0;i<10;i++)
{
pthread_rwlock_wrlock(&lock);
printf("--------fun write start\n");
int n = rand() % 3;
sleep(n);
printf("--------fun write end\n");
pthread_rwlock_unlock(&lock);
n = rand() % 3;
sleep(n);
}
}
int main()
{
pthread_rwlock_init(&lock,NULL);
pthread_t id1,id2,id3;
pthread_create(&id1,NULL,fun_r1,NULL);
pthread_create(&id2,NULL,fun_r2,NULL);
pthread_create(&id3,NULL,fun_w,NULL);
pthread_join(id1,NULL);
pthread_join(id2,NULL);
pthread_join(id3,NULL);
pthread_rwlock_destroy(&lock);
exit(0);
}
4.线程安全:
线程安全指的是在多个线程并发访问一个数据源时,不会出现数据不一致的情况或者数据污染。
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
void* fun(void* arg)
{
char str[] = "a b c d e f";
char* ptr = NULL;
char *s = strtok_r(str," ",&ptr);
while(s != NULL)
{
printf("fun s=%s\n",s);
sleep(1);
s = strtok_r(NULL," ",&ptr);
}
}
int main()
{
pthread_t id;
pthread_create(&id,NULL,fun,NULL);
char arr[] = "1 2 3 4 5 6";
char* ptr = NULL;
char* s= strtok_r(arr," ",&ptr);
while( s != NULL )
{
printf("main s=%s\n",s);
sleep(1);
s = strtok_r(NULL," ",&ptr);
}
pthread_join(id,NULL);
exit(0);
}
5.生产者消费者:
生产者消费者问题(英语:Producer-consumer problem),也称有限缓冲问题(英语:Bounded-buffer problem),是一个多线程同步问题的经典案例。该问题描述了共享固定大小缓冲区的两个线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。
- 在缓冲区为空时,消费者不能再进行消费
- 在缓冲区为满时,生产者不能再进行生产
- 在一个线程进行生产或消费时,其余线程不能再进行生产或消费等操作,即保持线程间的同步
- 注意条件变量与互斥锁的顺序
十五.网络编程:(进程间通信)
Linux上查看网络命令为:ifconfig Windows上为:ipconfig
1.网络协议三要素: 语法、语义、时序
2.OSI参考模型(开放式系统互联参考模型):下层为上层服务
应用层:使用应用程序通过网络服务
表示层:表示层用于处理交互数据的表示方式,例如格式转换、数据的加密和解密、数据压缩和恢复功能。
会话层:负责维护通信中两个节点之间的会话建立维护和断开,以及数据的交换
传输层:提供端到端之间的数据传输服务,实现对数据进行控制和操作的功能
网络层:单位 分组,在数据链路层的基础之上,提供点到点之间的通信,提供路由功能,实现拥塞控制、网络互联等功能
数据链路层:单位 帧,在物理层的基础之上,提供结点到结点之间的服务,采取差错控制和流量控制的方法实现网络互联
物理层:单位 bit,利用传输介质为网络通信结点之间的建立
3.TCP/IP参考模型:
应用层:会话层、表示层、应用层(http、https)
传输层:(tcp、udp)
网际层:网络层(ip协议)
网络接口层:物理层和数据链路层(arp、rarp)
4.TCP 服务器 客户端编程流程:
套接字socket()
套接字地址:ip + 端口
tcp三次握手建立连接(connect)
四次挥手断开连接(close)
tcp协议是面向连接的、可靠的流式服务:应答确认-超时重传(可靠的)、乱序重排、滑动窗口进行流量控制。
服务器端(先运行、后台)./ser:
socket() bind() listen() accept() recv() send() close()
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
int main()
{
int sockfd = socket(AF_INET,SOCK_STREAM,0);//tcp
if(sockfd == -1)
{
exit(1);
}
struct sockaddr_in saddr,caddr;
memset(&saddr,0,sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
saddr.sin_port = htons(6000);
int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
if(res == -1)
{
printf("bind err\n");
exit(1);
}
listen(sockfd,5);
while( 1 )
{
int len = sizeof(caddr);
int c = accept(sockfd,(struct sockaddr*)&caddr,&len);//可能阻塞
if(c<0)
{
continue;
}
printf("accept c=%d\n",c);
char buff[128] = {0};
int n = recv(c,buff,127,0);
printf("buff=%s\n",buff);
send(c,"ok",2,0);
close(c);
}
}
客户端 ./cli:
socket() connect() send() recv() close()
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
int main()
{
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if( sockfd == -1)
{
exit(1);
}
struct sockaddr_in saddr;
memset(&saddr,0,sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(6000);//服务器端口
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
int n = connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
if(n == -1)
{
printf("connect err\n");
exit(1);
}
char buff[128] = {0};
fgets(buff,128,stdin);
send(sockfd,buff,strlen(buff)-1,0);
memset(buff,0,sizeof(saddr));
recv(sockfd,buff,127,0);
printf("buff=%s\n",buff);
close(sockfd);
exit(0);
}
客户端向服务器端发送消息:
查看进程端口号:netstat -natp
5.TCP状态转移:
TCP 11种状态转换图:
LISTEN:等待从任何远端TCP 和端口的连接请求。
SYN_SENT:发送完一个连接请求后等待一个匹配的连接请求。
SYN_RECEIVED:发送连接请求并且接收到匹配的连接请求以后等待连接请求确认。
ESTABLISHED:表示一个打开的连接,接收到的数据可以被投递给用户。连接的数据传输阶段的正常状态。
FIN_WAIT_1:等待远端TCP 的连接终止请求,或者等待之前发送的连接终止请求的确认。
FIN_WAIT_2:等待远端TCP 的连接终止请求。
CLOSE_WAIT:等待本地用户的连接终止请求。
CLOSING:等待远端TCP 的连接终止请求确认。
LAST_ACK:等待先前发送给远端TCP 的连接终止请求的确认(包括它字节的连接终止请求的确认)
TIME_WAIT:等待足够的时间过去以确保远端TCP 接收到它的连接终止请求的确认。
TIME_WAIT 两个存在的理由:
1.可靠的实现tcp全双工连接的终止;
2.允许老的重复分节在网络中消逝。
CLOSED:不在连接状态(这是为方便描述假想的状态,实际不存在)
TIME_WAIT状态存在的原因:
可靠的终止TCP连接。
保证迟来的TCP报文有足够的时间被识别并被丢弃。
6.UDP 服务器 客户端编程流程:
无连接、不可靠、数据报服务
服务器端:./udpser
socket() bind() recvfrom() sendto()
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<netinet/in.h>
int main()
{
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
if(sockfd == -1)
{
exit(1);
}
struct sockaddr_in saddr,caddr;
memset(&saddr,0,sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(6000);
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
if(res == -1)
{
printf("bind err\n");
exit(1);
}
while(1)
{
int len = sizeof(caddr);
char buff[128] = {0};
recvfrom(sockfd,buff,127,0,(struct sockaddr*)&caddr,&len);//阻塞
printf("buff = %s\n",buff);
sendto(sockfd,"ok",2,0,(struct sockaddr*)&caddr,sizeof(caddr));
}
close(sockfd);
}
客户端:./udpcli
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<netinet/in.h>
int main()
{
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
if(sockfd == -1)
{
exit(1);
}
struct sockaddr_in saddr;
memset(&saddr,0,sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(6000);
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
while(1)
{
printf("input:\n");
char buff[128] = {0};
fgets(buff,128,stdin);
if( strncmp(buff,"end",3) == 0)
{
break;
}
sendto(sockfd,buff,strlen(buff)-1,0,(struct sockaddr*)&saddr,sizeof(saddr));
memset(buff,0,128);
int len = sizeof(saddr);
recvfrom(sockfd,buff,127,0,(struct sockaddr*)&saddr,&len);
printf("buff=%s\n",buff);
}
close(sockfd);
exit(0);
}
7.HTTP协议:(端口:80)
https:(端口:443)更安全
http请求方法:
状态码分类:
1xx:
2xx:
3xx:
4xx:
5xx:
HTTP在传输层应用(TCP协议)
我们为服务器端,浏览器为客户端。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<pthread.h>
#include<fcntl.h>
#define PATH "/home/dz/study/0517"
int socket_init();
char* get_filename(char buff[])
{
char* ptr = NULL;
char* s = strtok_r(buff," ",&ptr);
if(s == NULL)
{
return NULL;
}
printf("请求方法: %s\n",s);
s =strtok_r(NULL," ",&ptr);
return s;
}
void* thread_fun(void* arg)
{
int * p = (int*)arg;
int c = *p;
free(p);
while(1)
{
char buff[1024] = {0};
int n = recv(c,buff,1023,0);
if(n<=0)
{
break;
}
printf("buff = \n%s\n",buff);
char* filename = get_filename(buff);
if( filename == NULL)
{
break;
}
char path[256] = {PATH}; //"/home/dz/study/0517"
if( strcmp(filename,"/") == 0 )
{
filename = "/index.html";
}
strcat(path,filename);
printf("path:%s\n",path);
int fd = open(path,O_RDONLY);
if( fd == -1)
{
break;
}
int filesize = lseek(fd,0,SEEK_END);
lseek(fd,0,SEEK_SET);
char head[256] = {"HTTP/1.1 200 OK\r\n"};
strcat(head,"server: myhttp\r\n");
sprintf(head+strlen(head),"Content-Length: %d\r\n",filesize);
strcat(head,"\r\n");//分割头部和数据部分
send(c,head,strlen(head),0);
char data[1024] = {0};
int num = 0;
while((num = read(fd,data,1024))>0)
{
send(c,data,num,0);
}
close(fd);
//打开文件
//发送文件 报头问题
}
close(c);
pthread_exit(NULL);
}
int main()
{
int sockfd = socket_init();
if(sockfd == -1)
{
exit(1);
}
while( 1 )
{
int c = accept(sockfd,NULL,NULL);
if(c<0)
{
continue;
}
pthread_t id;
int *p = (int*)malloc(sizeof(int));
*p = c;
pthread_create(&id,NULL,thread_fun,(void*)p);
}
}
int socket_init()
{
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd == -1)
{
return -1;
}
struct sockaddr_in saddr;
memset(&saddr,0,sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(80);
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
if(res == -1)
{
printf("bind err\n");
return -1;
}
if(listen(sockfd,5) == -1)
{
return -1;
}
return sockfd;
}
浏览器:
<html>
<head>
<meta charset = utf-8>
<title>主页</title>
</head>
<body background = "shu.jpg" >
<center>
<h1>这是主页
</center>
<a href = "h.html">古诗一首</a>
</body>
</html>
<html>
<head>
<title>古诗</title>
<meta charset = utf8>
</head>
<body>
<center>
<h3> 《送孟浩然之广陵》<br>
故人西辞黄鹤楼,<br>
烟花三月下扬州。<br>
孤帆远影碧山见,<br>
惟见长江天际流。<br>
</center>
<a href = index.html>返回</a>
</body>
</html>
呈现效果:
十六.io复用
可以同时监听多个文件描述符,看是否有我们关心的事件。
1.select:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/select.h>
#include <time.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#define MAXFD 10
int socket_init();
void fds_init(int fds[])
{
for(int i = 0; i < MAXFD; i++ )
{
fds[i] = -1;
}
}
void fds_add(int fds[], int fd)
{
for(int i = 0; i < MAXFD; i++ )
{
if ( fds[i] == -1 )
{
fds[i] = fd;
break;
}
}
}
void fds_del(int fds[], int fd)
{
for(int i = 0; i < MAXFD; i++ )
{
if( fds[i] == fd )
{
fds[i] = -1;
break;
}
}
}
void accept_client(int sockfd, int fds[])
{
int c = accept(sockfd,NULL,NULL);
if ( c < 0 )
{
return ;
}
printf("accept c=%d\n",c);
fds_add(fds,c);
}
void recv_data(int c, int fds[])
{
char buff[128] = {0};
int n = recv(c,buff,127,0);
if ( n <= 0 )
{
printf("client close\n");
close(c);
fds_del(fds,c);
return;
}
printf("buff(c=%d)=%s\n",c,buff);
send(c,"ok",2,0);
}
int main()
{
int sockfd = socket_init();
if ( sockfd == -1 )
{
exit(1);
}
int fds[MAXFD];
fds_init(fds);//-1
fds_add(fds,sockfd);
fd_set fdset;
while( 1 )
{
FD_ZERO(&fdset);
int maxfd = -1;
for(int i = 0; i < MAXFD; i++ )
{
if ( fds[i] == -1 )
{
continue;
}
FD_SET(fds[i],&fdset);//将描述符添加到集合fdset
if( fds[i] > maxfd )
{
maxfd = fds[i];
}
}
struct timeval tv = {5,0};
int n = select(maxfd+1,&fdset,NULL,NULL,&tv);
if ( n == -1 )
{
printf("select err\n");
}
else if ( n == 0 )
{
printf("time out\n");
}
else
{
for(int i = 0; i < MAXFD; i++ )
{
if ( fds[i] == -1 )
{
continue;
}
if ( FD_ISSET(fds[i],&fdset) )
{
if ( fds[i] == sockfd )
{
accept_client(fds[i],fds);
}
else
{
recv_data(fds[i],fds);
}
}
}
}
}
}
int socket_init()
{
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if( sockfd == -1 )
{
return -1;
}
struct sockaddr_in saddr;
memset(&saddr,0,sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(6000);
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
if ( res == -1 )
{
printf("bind err\n");
return -1;
}
if( listen(sockfd,5) == -1 )
{
return -1;
}
return sockfd;
}
2.poll:
每次循环都需要将描述符拷贝到内核中
内核实现:O(n)轮询方式
内核返回后,用户遍历所有描述符找就绪描述符O(n)
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<poll.h>
#define MAXFD 10
int socket_init();
void fds_init(struct pollfd fds[])
{
for(int i = 0;i < MAXFD;i++)
{
fds[i].fd = -1;
fds[i].events = 0;
fds[i].revents = 0;
}
}
void fds_add(struct pollfd fds[],int fd)
{
for(int i=-0;i < MAXFD;i++)
{
if( fds[i].fd == -1)
{
fds[i].fd = fd;
fds[i].events = POLLIN;
fds[i].revents = 0;
break;
}
}
}
void fds_del(struct pollfd fds[],int fd)
{
for(int i =0;i <MAXFD;i++)
{
if( fds[i].fd == fd )
{
fds[i].fd = -1;
fds[i].events = 0;
fds[i].revents = 0;
break;
}
}
}
void accept_cilent(int sockfd,struct pollfd fds[])
{
int c =accept(sockfd,NULL,NULL);
if( c<0 )
{
return;
}
printf("accept c =%d\n",c);
fds_add(fds,c);
}
void recv_data(int c, struct pollfd fds[])
{
char buff[128] = {0};
int n =recv(c,buff,127,0);
if( n <= 0 )
{
close(c);
fds_del(fds,c);
printf("cilent close\n");
return;
}
printf("buff(%d)=%s\n",c,buff);
send(c,"ok",2,0);
}
int main()
{
int sockfd = socket_init();
if( sockfd == -1 )
{
exit(1);
}
struct pollfd fds[MAXFD];
fds_init(fds);
fds_add(fds,sockfd);
while(1)
{
int n =poll(fds,MAXFD,5000);
if( n == -1 )
{
printf("poll err\n");
}
else if( n == 0 )
{
printf("time out\n");
}
else
{
for(int i =0; i < MAXFD;i++)
{
if( fds[i].fd == -1)
{
continue;
}
if( fds[i].revents & POLLIN )
{
if( fds[i].fd == sockfd)
{
accept_cilent(sockfd,fds);
}
else
{
recv_data(fds[i].fd,fds);
}
}
}
}
}
}
int socket_init()
{
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if( sockfd == -1 )
{
return -1;
}
struct sockaddr_in saddr;
memset(&saddr,0,sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(6000);
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
if( res == -1 )
{
printf("bind err\n");
return -1;
}
if( listen(sockfd,5) == -1 )
{
return -1;
}
return sockfd;
}
3.epoll:
创建内核事件表,红黑树,每个描述符只需要向内核拷贝一次。 epoll_create
注册回调函数,实现 O(1) epoll_ctl
直接返回就绪描述符 O(1) epoll_wait
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#define MAXFD 10
int socket_init();
void epoll_add(int epfd, int fd)
{
struct epoll_event ev;
ev.data.fd = fd;
ev.events = EPOLLIN;
if ( epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&ev) == -1 )
{
printf("epoll ctl add err\n");
}
}
void epoll_del(int epfd, int fd)
{
if ( epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL) == -1 )
{
printf("epoll ctl del err\n");
}
}
void accept_client(int sockfd, int epfd)
{
int c = accept(sockfd,NULL,NULL);
if ( c < 0 )
{
return;
}
printf("accept c=%d\n",c);
epoll_add(epfd, c);
}
void recv_data(int c, int epfd)
{
char buff[128] ={0};
int n = recv(c,buff,127,0);
if ( n <= 0 )
{
epoll_del(epfd,c);
close(c);
printf("client close\n");
return;
}
printf("recv(%d):%s\n",c,buff);
send(c,"ok",2,0);
}
int main()
{
int sockfd = socket_init();
if( sockfd == -1 )
{
exit(1);
}
int epfd = epoll_create(MAXFD);//创建内核事件表,红黑树
if ( epfd == -1 )
{
exit(1);
}
epoll_add(epfd,sockfd);//向内核事件表(红黑树)添加描述符 sockfd
struct epoll_event evs[MAXFD];
while( 1 )
{
int n = epoll_wait(epfd,evs,MAXFD,5000);
if ( n == -1 )
{
printf("epoll err\n");
}
else if ( n == 0 )
{
printf("time out\n");
}
else
{
for(int i = 0; i < n; i++ )
{
if ( evs[i].events & EPOLLIN )
{
if ( evs[i].data.fd == sockfd )
{
//accept
accept_client(sockfd,epfd);
}
else
{
//recv
recv_data(evs[i].data.fd,epfd);
}
}
//if ( evs[i].events & POLLOUT)
}
}
}
}
int socket_init()
{
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if ( sockfd == -1 )
{
return -1;
}
struct sockaddr_in saddr;
memset(&saddr,0,sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(6000);
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
if ( res == -1 )
{
printf("bind err\n");
return -1;
}
if( listen(sockfd,5) == -1 )
{
return -1;
}
return sockfd;
}
4.区别:
select和poll每次循环都需要拷贝描述符和事件到内核
内核实现:轮询O(n)
用户需要遍历所有描述符找到就绪描述符。O(n)
epoll 每个描述符只需要拷贝一次
内核实现:注册回调函数的方式O(1),用户直接可以获取就绪描述符O(1)
epoll_create() 创建内核事件表、红黑树、就绪队列(rdlist)
epoll_ctl() 添加、移除、修改
epoll_wait()获取就绪描述符
4.1LT模式:水平触发只要有数据都会触发。
LT模式在事件发生时会一直通知,直到应用程序将事件处理完毕。它不要求应用程序一直读取或写入数据,可以在每个事件通知中只处理一部分数据。LT模式适用于需要按需处理数据的场景,如批量数据处理、文件传输等。
4.2ET模式:边缘触发只有数据到来才触发,不管缓存区中是否还有数据。
ET是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知。请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once)。
十七.libevent库
1.句柄:文件描述符
2.事件多路分发器:事件循环(调select/poll/epoll)
3.事件处理器和具体事件处理器:回调函数
4.Ractor
十八.shell编程:
解释型:shell、python 需要解释器(通常是可执行)bash my.sh参数
编译型:c 、c++、go 需要编译链接,生产可执行程序 main.exe/main 直接执行
java 编译 二进制 .class 解释型(java虚拟机) java.exe
1.变量:
1.1本地变量:
#!/usr/bin/bash
#echo "hello"
#printf "hello\n"
a="100"
str="hello world"
echo a=$a
echo str=$str
echo 'a=$a'
exit 0
1.2环境变量:
#!/usr/bin/bash
echo "\$0=$0" #当前脚本名称
echo "\$#=$#" #传给脚本参数个数
echo "\$$=$$" #当前脚本pid
exit 0
1.3参数变量:
#!/usr/bin/bash
name=$1
pw=$2
echo "\$0=$0"
echo "\$#=$#"
echo "\$$=$$"
echo "\$1=$1"
echo "\$2=$2"
echo "name=$name"
echo "pw=$pw"
exit 0
2.基本运算符:
字符串类: =等于 !=不等于 -z是否为空 -n非空
算数比较: -gt大于 -ge大于等于 -lt小于 -le小于等于
文件测试: -f普通文件 -d目录文件
2.1if-else循环:
if condition1
then
command1
elif condition2
then
command2
else
commandN
fi
2.2for循环:
for var in item1 item2 ... itemN
do
command1
command2
...
commandN
done
2.3while循环:
while condition
do
command
done
十九.Redis:
端口:6379
启动redis:service redis-server status
重合redis:service redis-server restart
默认16个数据库0-15
select index(选择几号数据库)
flushall 清空所有数据库
flushdb 清空当前数据库
dbsize 查看数据库大小
1.五种基本数据类型:
1.1字符串 string:
set get:
设置值和获取值:
127.0.0.1:6379> set key1 v1 #设置值
OK
127.0.0.1:6379> get key1 #获得值
"v1"
append:
127.0.0.1:6379> APPEND key1 hello #追加字符串,如果当前key不存在,相当于set命名
(integer) 7
127.0.0.1:6379> get key1
"v1hello"
strlen:
127.0.0.1:6379> STRLEN key1 #获得字符串的长度
(integer) 7
127.0.0.1:6379> append key1 wang
(integer) 11
127.0.0.1:6379> strlen key1
(integer) 11
127.0.0.1:6379> get key1
"v1hellowang"
1.2列表 list:
lpush rpush lrange:
127.0.0.1:6379> LPUSH list one # 将一个值或者多个值,插入到列表头部(左)
(integer) 1
127.0.0.1:6379> LPUSH list two
(integer) 2
127.0.0.1:6379> LPUSH list three
(integer) 3
127.0.0.1:6379> LRANGE list 0 -1 # 获取list中的值
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> LRANGE list 0 1 # 通过区间获取具体的值
1) "three"
2) "two"
127.0.0.1:6379> RPUSH list right # 将一个值或者多个值,插入到列表尾部(右)
(integer) 4
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
3) "one"
4) "right"
lpop rpop:
从列表的左边或者右边移除值,格式: lpop key rpop key
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
3) "one"
4) "right"
127.0.0.1:6379> LPOP list #移除list的第一个元素
"three"
127.0.0.1:6379> RPOP list #移除list的最后一个元素
"right"
127.0.0.1:6379> LRANGE list 0 -1
1) "two"
2) "one"
1.3集合 set:
set中的值不能重复
sadd:
127.0.0.1:6379> sadd myset hello #set集合中添加值
(integer) 1
127.0.0.1:6379> sadd myset world
(integer) 1
127.0.0.1:6379> sadd myset wang
(integer) 1
127.0.0.1:6379> sadd myset hello #设置重复值时,失败
(integer) 0
smembers :
获取set中的所有值,格式: smembers key
127.0.0.1:6379> SMEMBERS myset #获取set的所有值
1) "hello"
2) "world"
3) "wang"
1.4哈希 hash:
hset hget:
设置或者获取一个hash的值,格式: hset key field1 value1 hget key field
127.0.0.1:6379> hset myhash field1 wang #设置一个hash的值
(integer) 1
127.0.0.1:6379> hget myhash field1 #获取一个hash的值
"wang"
hmset hmget :
127.0.0.1:6379> hmset myhash field2 chen field3 hello #同时设置多个hash的值
OK
127.0.0.1:6379> hmget myhash field1 field2 field3 #同时获取多个hash值
1) "wang"
2) "chen"
3) "hello"
getall :
127.0.0.1:6379> hgetall myhash #获取hash中的所有的值
1) "field1"
2) "wang"
3) "field2"
4) "chen"
5) "field3"
6) "hello"
1.5有序集合 zest:
zrangebyscore:
将zset中的值按照score从小到大排序输出,格式: zrangebyscore key min max
127.0.0.1:6379> ZRANGEBYSCORE myset -inf +inf #按照从小到大排序
1) "wang"
2) "yang"
3) "zhang"
127.0.0.1:6379> ZRANGEBYSCORE myset -inf +inf withscores #按照从下到大排序并显示scores
1) "wang"
2) "1"
3) "yang"
4) "2"
5) "zhang"
6) "3"
2.事务:
2.1事务使用过程:
127.0.0.1:6379> multi # 开启事务
OK
127.0.0.1:6379> set k1 v1 # 命令入队
QUEUED
127.0.0.1:6379> set k2 v2 # 命令入队
QUEUED
127.0.0.1:6379> get k2 # 命令入队
QUEUED
127.0.0.1:6379> set k3 v3 # 命令入队
QUEUED
127.0.0.1:6379> exec # 执行事务
1) OK
2) OK
3) "v2"
4) OK