同步,线程,互斥锁,mutex
用来保护某个临界资源
信号量
p sem_wait
v sem_post
条件变量,进程,管道pipe fifo,消息队列
msg,信号signal,信号量 灯,共享内存,shm
套接字socket
用多线程+sem 实现以下任意一种算法:
读者写者
多个任务写数据
多个任务读取数据,读完不删除
生产者消费者
多个任务产生数据
一个任务读数据,数据就删除了 sem 1
哲学家就餐
fork()?fork():fork()
samples\3.ProcessII\2.1-pipe\pipe_rw.c
1:如何测试pipe缓冲区大小?
如果没有写端,读端会先把缓冲区数据读完,然后退出 EOF
如果没有读端,只有写端,写不进去
如果有读写段,read时没有数据会阻塞,
1 测试缓冲区大小
2 只有读端
3 只有写端
各种类型的文件:
-,l,d,b,c,p,s,有名管道,mkfifo
cat open read close
echo open write close
如如何用管道实现文件的传输??
system("rm fifo");
ret = mkfifo();
fd = open("fifo",O_RDWR);
写入FIFO中的内容保存在什么地方?
命名管道是建立在实际的磁盘介质或文件系统(而不是只存在于内存中)上有自己名字的文件,任何进程可以在任何时间通过文件名或路径名与该文件建立联系。
为了实现命名管道,引入了一种新的文件类型——FIFO文件(遵循先进先出的原则)。 实现一个命名管道实际上就是实现一个FIFO文件。命名管道一旦建立,之后它的读、写以及关闭操作都与普通管道完全相同。虽然FIFO文件的inode节点在磁盘上,但是仅是一个节点而已,文件的数据还是存在于内存缓冲页面中,和普通管道相同。
如何用管道发送文件??
www
是什么?怎么用?为什么
what how why
kill -l
信号生命周期
1 信号“诞生”。信号的诞生指的是触发信号的事件发生(如检测到硬件异常、终端中断、定时器超时以及调用信号发送函数kill()或sigqueue()等)。
信号作用:
1> 异常
2>其他进程
3>终端终端 ctrl+c ctrl+\
4>作业控制
5>分配额 cpu超时或者文件大小突破限制
6>通知 内核通知进程某事件发生,I/O就绪 SIGIO
7>报警 计时器到期
2. 信号在进程中注册指的就是信号值加入到进程的未决信号集中(sigpending结构的第二个成员sigset_t signal),并且信号所携带的信息被保留到未决信号信息链的某个sigqueue结构中。 只要信号在进程的未决信号集中,表明进程已经知道这些信号的存在,但还没来得及处理,或者该信号被进程阻塞。
3.信号在进程中的注销。在目标进程执行过程中,会检测是否有信号等待处理(每次从系统空间返回到用户空间时都做这样的检查)。如果存在未决信号等待处理且该信号没有被进程阻塞,则在运行相应的信号处理函数前,进程会把信号在未决信号链中占有的结构卸掉。
4.进程注销信号后,立即执行相应的信号处理函数,执行完毕后,信号的本次发送对进程的影响彻底结束。
注:在信号被注销到相应的信号处理函数执行完毕这段时间内,如果进程又收到同一信号多次,则对实时(可靠)信号来说,每一次都会在进程中注册;而对于非实时信号来说,无论收到多少次信号,都会视为只收到一个信号,只在进程中注册一次。
用户进程对信号的响应方式:
1 忽略信号:
对信号不做任何处理,但是有两个信号不能忽略:即SIGKILL及SIGSTOP。
2 捕捉信号:前32 ,有2个特权,不可以捕捉
定义信号处理函数,当信号发生时,执行相应的处理函数。
3 执行缺省操作:
Linux对每种信号都规定了默认操作
定时:
1 先起一个进程A,进程b向a进程发送信号
alarm();
pause();
kill();
pause() 默认会终止进程
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler)
练习
1.给alarm信号注册一个响应函数,用命令向该进程发送alarm信号??
2.ctrl+c 终止某个线程?
pthread_t pid;
void *func(void *arg)
{
while(1)
{
sleep(1);
}
}
void sigfunc(int signo)
{
pthread_cancel(pid);
pthread_join(pid,NULL);
signal(SIGINT,SIG_DFL);
}
int main()
{
pthread_create( &pid, NULL,func , NULL);
signal(SIGINT,sigfunc);
while(1);
}
1. 父进程用信号方式回收子进程
2. 实验手册5.3
什么是IPC
生成key
ipcs
共享内存
把物理地址映射到多个进程的虚拟地址,
进程访问这个物理地址,可以直接访问,
效率最高的,
一定涉及同步问题。
如何控制,让写进程先写,读进程后读???
信号量:rsem 0 wsem 1
写进程p(wsem?) 读进程p(rsem)
写进程写完了,v(rsem),读进程就可以读了
E:\华清课件汇总\linux进程课程\课件\samples\3.ProcessII\3.2-shm\shm.c
父子进程 利用共享内存通信,
用信号量控制读写的顺序,同时只能有一个进程读或者写,
消息队列
解决什么问题?
syslog vxworks
1. 消息队列可以看作是若干个FIFO的集合,每个FIFO存放不同类型的消息
msgsnd(shmid,buf,len,flage)
struct{
long type; 1 2 3
………………
………………
}
msgrcv(shmid,buf,len,type, flage)
1.发送的消息的类型包含在发送缓冲区里,类型的值通常为正整数。
2. 消息结构除了第一个成员必须为long类型外,其他成员可以根据应用的需求自行定义。
交互的数据结构必须是
{
long msgtype;
data;
}
信号灯
semget();
1. semid维护的是一个信号量的集合,信号量集描述符
1 socket位置
2. 一套api
3 像文件一样操作 read write socket()
物理层
曼彻斯特
传输物理信号、接口、信号形式CDMA【高通】、速率、
zdc
wifi
a
bg
n
ac
网卡芯片
1、Broadcom(博通,美国)
2、Atheros(创锐讯,美国,被高通(Qualcomm)收购)
3、Marvell(美满科技,美国)
4、TI(德州仪器,美国)
5、Ralink (雷凌,台湾,被联发科(MTK)收购)
华强北
6、Realtek(瑞昱,台湾)
7、上海乐鑫
8、北京新岸线
9、北京盛德微
10、深圳南方硅谷
11、上海澜起
12、上海庆科(出货WiFi模组)
链路层 帧?arp
数据组成可发送、接收的帧
ppp
连续6个1 就在第5个1后面补0
帧头 放 01111110
CRC
网络 分组 ip icmp
数据分组、路由选择
不确保数据的正确到达,
路由选择
根据数据包的目的ip,选择一条通路发送出去
路由器
zxr10
根据ip地址唯一的确认一台主机
传输层?报文段
差错处理/恢复,流量控制,提供可靠的数据传输
确认丢失 确认迟到 超时重传
TCP
建立连接
3次握手
数据发送
断开连接
4次握手
UDP
如何知道数据发送给哪个进程?
端口号
应用层
ftp telnet smtp http dns
要先绑定一个端口号
常用的服务器的端口,都一般是固定的
客户端一般是os自动分配
DoS攻击是指故意的攻击网络协议实现的缺陷或直接通过野蛮手段残忍地耗尽被攻击对象的资源,目的是让目标计算机或网络无法提供正常的服务或资源访问,使目标系统服务系统停止响应甚至崩溃,而在此攻击中并不包括侵入目标服务器或目标网络设备。
1,制造大流量无用数据,造成通往被攻击主机的网络拥塞,使被攻击主机无法正常和外界通信。
2,利用被攻击主机提供服务或传输协议上处理重复连接的缺陷,反复高频的发出攻击性的重复服务请求,使被攻击主机无法及时处理其它正常的请求。[1]
3,利用被攻击主机所提供服务程序或传输协议的本身实现缺陷,反复发送畸形的攻击数据引发系统错误的分配大量系统资源,使主机处于挂起状态甚至死机。
[1] 使用僵尸电脑进行DOS攻击 僵尸电脑(Zombie computer),简称“僵尸(zombie)”,有些人称之为“肉鸡”,接入互联网的电脑被病毒感染后,受控于黑客,可以随时按照黑客的指令展开拒绝服务(DoS)攻击或发送垃圾信息。通常,一部被侵占的电脑只是僵尸网络里面众多中的一环,而且会被用来去运行一连串的或远端控制的恶意程序。很多“僵尸电脑的拥有者”都没有察觉到自己的系统已经被“僵尸化”,就仿佛是没有自主意识的僵尸一般。
os 编译 网络 算法
192.168.1.5
0xc0a80105 内存实际表现形式
掩码?
255.255.0.0
&
192.168.1.5
-------------
192.168.0.0 网络号
网络中:不允许有两个相同的网络,路由器都是个根据网络号进行路由查找的。
IP地址分类
struct sockaddr_in struct sockaddr_un
{
u_short sin_family; // 地址族, AF_INET,2 bytes
u_short sin_port; // 端口,2 bytes
struct in_addr sin_addr; // IPV4地址,4 bytes
char sin_zero[8]; // 8 bytes unused,作为填充
};
// internet address struct in_addr
{
in_addr_t s_addr;?// u32 network address
};
端口号
唯一的确定一个主机里的某个进程,
IP地址唯一的确定一个主机的
ip+port 唯一的确定浩瀚的网络中唯一的一台主机上的唯一的一个进程
ip port
h host 主机
to
n network 网络
l long 4个字节
s short 2个字节
htons 主机字节序---》网络字节序 2个字节
htonl
ntohs
ntohl
int socket (int domain, int type, int protocol)
功能
创建套接字
参数
int domain协议类型?AF_INET
type 套接字的类型
SOCK_STREAM // 流式套接字 tcp
SOCK_DGRAM // 数据报套接字 udp
SOCK_RAW // 原始套接字
int protocol 0
返回值
返回一个套接字描述符
我们要用的结构体
struct sockaddr_in struct sockaddr_un
{
u_short sin_family; // 地址族, AF_INET,2 bytes
u_short sin_port; // 端口,2 bytes
struct in_addr sin_addr; // IPV4地址,4 bytes
char sin_zero[8]; // 8 bytes unused,作为填充
};
// internet address struct in_addr
{
in_addr_t s_addr; // u32 network address
};
老的结构体,大小和上面一样
struct sockaddr {
u_short sa_family; // 地址族, AF_xxx
char sa_data[14]; // 14字节协议地址
};
int bind(int sockfd, struct sockaddr *my_addr, int addrlen) ;
功能
pid + ip +port 绑定
参数
int sockfd socket返回的
struct sockaddr *my_addr 我们要填充的sockaddr_in 结构体
int addrlen?sockaddr_in size
返回值
struct sockaddr_in my_addr;
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons (6666);
my_addr.sin_addr.s_addr = inet_addr(“192.168.1.100”);
bind(sockfd,(struct sockaddr *)&my_addr,sizeof(struct?sockaddr_in?)?);
int listen (int sockfd, int backlog);
功能
监听客户端的连接 tcp服务器专用
调用完之后,这个套接字,就变成了监听套接字
参数
int sockfd
int backlog
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) ;
功能
阻塞
接收一个客户端的连接,创建一个新的套接字返回
调用完了之后,客户端和服务器端就 可以通信了
tcp服务器专用的函数
参数
int sockfd
struct sockaddr *addr 客户端的ip+port
socklen_t *addrlen 指针
返回值
新的套接字,这个套接字才是真正和客户端通信用的套接字
int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
功能
客户端使用,
和服务器连接用的【tcp】 指定一个发送对象【udp】
阻塞,一旦返回,说明建立成功了,就可以和服务器端通信了
事实上发起了一次tcp3次握手。。。
参数
int sockfd
struct sockaddr *serv_addr 服务器的ip+port
客户端的端口号由系统自动分配
int addrlen
返回值
ssize_t send(int socket, const void *buffer, size_t length, int flags);
参数
int socket 套接字 服务器的话。应该填充accept返回的套接字
const void *buffer,?发送的内存的地址
size_t length 发送数据的长度
int flags 0
返回值
实际发送的自己个数
ssize_t recv(int socket, const void *buffer, size_t length, int flags);
size_t length 缓冲区大小
int close(int sockfd);
关闭双向通信
int shutdown(int sockfd, int howto);
howto
0 关闭读通道
1 关系写通道
2 关闭双向通道
综合练习:
1 搭建一个tcp框架
2 互相收发结构体
3. 循环收发聊天
4. 如何收发文件?
UDP专用函数
ssize_t sendto(int socket, void *message, size_t length, int flags, struct sockaddr *dest_addr, socklen_t dest_len);
参数
int socket
void *message 发送数据内存
size_t length 数据长度
int flags
struct sockaddr *dest_addr 对方的ip+port
socklen_t dest_len
返回值
实际发送字节个数
ssize_t recvfrom(int socket, void *buffer, size_t length, int flags, struct sockaddr *address, socklen_t *address_len);
综合练习:
1?udp聊天室
C:\Users\pengdan\Desktop\1611\code\第二天-udp聊天室\实验6_4
C:\Users\pengdan\Desktop\1611\code\UDP\chat-复杂
2. 用tcp实现文件服务器
TCP UDP
server client server client
socket
bind
listen
accept
connect
read/write
send/recv
sendto/recvfrom
sendmsg/recvmsg
close/shutdown
不常用
listen不关闭,关闭连接套接字
抓包工具
会针对ip+port进行数据包过滤
还要回分析数据包
要能够根据内存 ,要知道如何找到需要的字段
IO模型
阻塞
非阻塞
多路复用
异步信号IO
多路复用
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
功能
阻塞
当他维护的文件描述符集合中的任意一个描述符有数据发生变化,
就会立刻返回
然后去判断集合中哪个文件描述符发生了变化
参数
int nfds 维护的文件描述符中最大的值+1
fd_set *readfds 读集合
fd_set *writefds 集合
fd_set *exceptfds
struct timeval *timeout
NULL 一直阻塞
=0 非阻塞
>0 超时时间
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
void FD_CLR(int fd, fd_set *set); 从set中删除fd
int FD_ISSET(int fd, fd_set *set); 判断fd是否发生了变化
void FD_SET(int fd, fd_set *set); 将fd加入到set这个集合中
void FD_ZERO(fd_set *set); 清空
演示:
1 : 单线程客户端
一种是可以收发的client.c ./c 起一个
一种是只收数据包的clientrcv.c ./cr 可以起很多个
思考:
如何让客户端实时收发??
多线程:发送函数和接收函数都是阻塞的。
服务器模型
循环服务器
tcp一般不用
udp一般用循环服务器
1. UDP是非面向连接的,没有一个客户端可以一直占用服务器端
2. 只要处理过程不是死循环, 服务器对于每一个客户机的请求总是能够满足
struct online{
int flage;
char name;
struct sockaddr addr;
}
并发服务器
多进程模型
tcp参考代码
C:\Users\pengdan\Desktop\1611\code\多进程模型
多线程模型
多线程tcp聊天室
1. 增加掉线提示的功能。
2. 显示在线用户
【注意】客户端和服务器端交互的所有数据 都是结构体
超时3种方式
1. setsockopt( )
2. select();
3. 阻塞函数在阻塞的时候,收到一个信号,一般是默认操作,是回去继续阻塞还是立刻返回?
如果一个block系统调用被一个signal的处理函数中断,如果是SA_RESTART,则系统调用会重启,否则会返回失败,失败码是EINTR。老的signal,缺省是restart,如果要让recv在SIGALRM到了之后跳出recvfrom,则需要使用sigaction函数。
sigaction可以自己设置是否重启动函数,即上面例子中的alrmact.sa_flags = SA_NOMASK选项,SA_NOMASK为不重启动,中断已阻塞的函数recvfrom,使程序继续往下执行,SA_RESTART为重启动函数,与signal相同,继续阻塞在recvfrom上。。。
广播
路由器
连接不同网络的
在一个网络里面,不允许出现两个相同的网络,
路由器至少连接2个网络
路由器是知道和谁相连的
路由条目:
1 目的网络, 192.168.0.0
2 掩码 255.255.255.0
3 下一跳 该网络要经过的下一个路由器上,和本路由器相连的那个接口的地址
交换机
工作在链路层,
域套接字
不走协议栈
若客户端没有绑定地址(套接字文件),系统不会自动分配
客户端没有绑定地址,只能发送数据,不能接收数据
listen_fd=socket(PF_UNIX,SOCK_STREAM,0);
if (listen_fd < 0) {
perror("cannot create communication socket");
return 1;
}
/* make sure the socket file doesn't exist */
unlink(UNIX_DOMAIN);
srv_addr.sun_family = AF_UNIX;
strncpy(srv_addr.sun_path, UNIX_DOMAIN, sizeof(srv_addr.sun_path)-1);
ret = bind(listen_fd,(struct sockaddr*)&srv_addr,sizeof(srv_addr));
if (ret == -1) {
perror("cannot bind server socket");
close(listen_fd);
unlink(UNIX_DOMAIN);
return 1;
}
client
connect_fd=socket(PF_UNIX,SOCK_STREAM,0);
if (connect_fd < 0) {
perror("cannot create communication socket");
return 1;
}
srv_addr.sun_family = AF_UNIX;
strcpy(srv_addr.sun_path, UNIX_DOMAIN);
ret=connect(connect_fd, (struct sockaddr*)&srv_addr, sizeof(srv_addr));