Linux系统概述

目录

一.目录结构:

二.文件类型:

三.基础命令:

权限修改:

安装软件(命令):

关机与重启:

四.vim的使用

1.vim的三种命令模式:

2.模式之间的切换:

3.文件编译生成:

4.其他操作:

五.文件的操作命令:

1.cp 拷贝文件:

2.mv 移动文件、重命名:

3.cat 打印文件内容:适合小文件

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查看:

4.将前后台运行的进程互换:

七.文件压缩与解压命令:

1.tar:将文件打包或解包:c 创建包文件   f 指定目标为文件而不是设备   v 显示详细过程   t 显示包中的内容而不释放

x 释放包中内容   

2.压缩:gzip 文件名称

八.makefile文件:

九.GDB调试:

十.库文件:预先编译好的方法的集合

1.静态库:libxx.a 

2.共享库(win动态链接库):libxx.so

3.ldd 文件名(文件使用了哪些库(只能看共享库))

4.设置环境变量使先从当前开始寻找库文件:

十一.进程复制与替换:

十五.网络编程:(进程间通信)

1.网络协议三要素: 语法、语义、时序

2.OSI参考模型(开放式系统互联参考模型):下层为上层服务

3.TCP/IP参考模型:

4.TCP 服务器 客户端编程流程:

5.TCP状态转移:

6.UDP 服务器 客户端编程流程:

7.HTTP协议:(端口:80)

十六.io复用

1.select:

2.poll:

3.epoll:

4.区别:

5.LT模式:水平触发只要有数据都会触发。

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.模式之间的切换:

在命令模式下,输入iao进入编辑模式;

编辑模式下,按下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:用于显示与进程间通信有关的信息。

-q : 显示消息队列

-m : 显示共享内存

-s : 显示信号量

-a : 显示所有信息(消息队列,共享内存,信号量)

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:
追加字符串,如果key不存在,相当于set命令,格式: append key value
127.0.0.1:6379> APPEND key1 hello #追加字符串,如果当前key不存在,相当于set命名
(integer) 7
127.0.0.1:6379> get key1
"v1hello"
strlen:
获取字符串的长度,格式: strlen key
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:
在Redis中,我们可以把list完成栈、队列、阻塞队列
所有的list命令都是用l开头的
lpush  rpush  lrange:
从list的左边或者右边插入值,格式: lpush key value rpush key value
lrange获取指定范围的值,格式: lrange key start stop
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:
给set中添加值,格式: sadd key value1 value2 ...
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:
可以将哈希看成是一个Map集合,key-value中的value是一个map集合
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 :
设置或者获取hash的值,格式: hmset key field1 value1 field2 value2 ... hmget key field1
field2...
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 ​​​​​​​:
获取hash中的所有值
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.事务:
事务的本质: 一组命令的集合!一个事务中的所有命令都会被序列化,在事务执行过程中,会按照顺 序执行!
一次性,顺序性,排他性,执行一系列的命令!
MySQL中的事务,要么同时成功,要么同时失败,必须保证原子性!
Redis单条命令是保证原子性的,但是Redis的事务不保证原子性!
Redis事务是没有隔离级别的概念 ​​​​​​​
2.1事务使用过程:
开启事务 -- multi
命令入队 -- ....
执行事务 -- exec
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
3.Redis的乐观锁 Watch
悲观锁:
很悲观,认为什么时候都会出问题,无论做什么都会加锁!但是影响效率!
乐观锁:
很乐观,认为什么时候都不会出问题,所以不会加锁!更新数据时去判断一下,在此期间是否有人
修改过这个数据!MySQL的version的使用:先获取version,更新数据时比较version,看version
是否被修改 ​​​​​​​

4.redis持久化:
Redis是内存数据库,如果不将内存中的数据库状态保存到磁盘中,那么一旦服务器进程退出,服务
器中的数据库状态也会消失。所以Redis提供了持久化的功能。
4.1RDB(默认使用):
4.2AOF:

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值