1 内存共享
内存的获取
int fd = shmget(key_t key ,size_t size , int shmflg);
内存的关联
void* shmat(int shmid ,const void* shmaddr , int shmflg);
char *p;
p = (char*) shmat(fd , 0 , 0 );
内存的分离
int shmdt(const void* shmaddr);
内存的控制
int shmctl(int shmid , int cmd , struct shmid_ds* buf);
ret = shmctl(fd, IPC_RMID, NULL);
cmd : IPC_STAT 获取当前状态 返回给参数3
IPC_SET 设置共享内存
2.多路复用
select系统调用
int ret = select (int nfds, fd_set* readfds, fd_set* write ,
fd_set* exceptfds, struct timeval* timeout );
nfds = 监听的文件描述符最大值+1;
文件描述符集:fd_set
FD_ZERO :清空集合 RD_ZERO(&read_set);
FD_CLR :把指定的文集描述符从集合删除 FD_SET(fd ,&read_Set);
FD_SET :添加指定的文件描述符到集合
FD_ISSET:监测指定的文件描述符是否在集合中
struct timeval{
long tv_sec; 超时 秒设置
long tv_usec ; 超时 微微秒设置
}
监听到 ret > 0
在用 FD_ISSET(fd , &read_set) 监测 fd 是否在文件集合中
3 .信号量
信号量的获取:
int semget(key_t key , int nsems , int semflg);
返回值为该信号量的标识符
nsmes 需要的信号数目 一般取1;
IPC_CREAT | 权限
信号的初始化
int semctl(int semid , int sem_num , int cmd , ....);
semid 信号的标识符
sem_num 信号量组中的编号 如果只有一个信号 取值为 0
cmd : SETVAL 表示初始化信号量 具体参数由第四个参数确定
信号量只能初始化一次
:IPC_RMID 表示删除;
第四个参数:
union semun{
int val :
struct semid_ds* buf ; 0;
unsigned short* array ; 0;
}
注意 union semun 联合体需要自己定义
信号量的操作 V——P
int semop(int semid , struct sembuf* sops , unsigned nsops);
sops 是 一个数组, 元素类型为 struct sembuf
struct sembuf {
short sem_num ; 只有一个 取0;
short sem_op ;
short sem_flg ; 0;
}
nsops 表示有几个 struct sembuf ;
4 线程
同一个进程内的各个线程,能够共享整个进程的全局变量 ,除了线程的局部变量外, 其他资源都是共享的
线程的创建:
pthread_t pthread_fd;
int ret = pthread_create( pthread_t pthread_fd, pthread_att_t* attr ,
void*(*start_routine)(void*) , void* arg );
attr 设置线程的属性 一般取默认 NULL;
start_routine 该线程的处理函数
该函数的返回类型和参数类型都是void*
arg 线程处理函数 start_routine 的参数;
: 创建一个线程 : 同时指定该线程的属性 执行函数 执行函数的参数
通过参数1返回该线程的标识符
线程的终止
void pthread_exit(void * retval);
在线程函数内部调用该函数
终止该线程, 并通过参数retval 返回一个指针
该指针不能指向该线程的局部变量
等待指定线程的结束
int pthread_join(pthread_t th ,
void** thread_return);
thread_return 指向该线程函数的返回值
OSI ---- ISO
物理层 比特流 RS-232 网卡
数据链路层 帧(frame) 交换机
网络层 选择最佳的路径到达目的地 路由器
传输层 提供可靠的数据传输服务(监测路由器丢弃的包) 提供重传机制 包的排序
会话层 管理主机之间的会话过程 会话的建立 会话的终止 会话过程的管理
表示层 数据压缩 格式转换 加密
应用层 与应用程序之间通讯
链路层 ARP -----RARP 协议
网络层
传输层
应用层
==------------------------------------------------------------------------
对等通信 虚电路
TCP/IP 协议栈
==-----------------------------------------------------------------------
封装: 用户数据 ----> app + 数据 ----> TCP+app+数据 ---> IP+TCP+app+数据 ---->以太网首部+IP+TCP+app---网络
解封:
===-----------------------------------------------------------------------
端口 1-1023 系统
1024 - 49151
49152-65535
端口决定同一台主机IP 分别传送数据给哪个应用程序
===-------------------------------------------------------------------------
链路层 : 最大传输单元 MTU 46-----1500字节 CRC尾部 校对
ICMP 协议 差错的信息的控制 查询信息的控制 ping————》将数据封装成ICMP 协议实现的
地址解析无法完成 使用ICMP 协议 查找MAC硬件地址
ARP 地址解析协议
RARP 反向
IP地址 只是逻辑上的地址 IP--->MAC 地址解析(ARP)
===-----------------------------------------------------------------------
以太网帧的格式 :
目的地址 :
源地址 :
帧类型 : IP数据包(0800) ARP(0806) RARP(8035) 3种
硬件类型 :
协议类型 :
硬件地址长度 :
协议地址长度 :
OP :
发送端以太网地址 :
发送端IP地址 :
目的以太网地址 :
目的IP地址 :
===-----------------------------------------------------------------------------
1. ping ----主机名 -----调用函数 gethostbyname() 将主机名转化为一个32位的IP地址
----iP地址
2. 向目的IP地址发送一个ICMP 的ECHO 包
3. 将主机IP地址转换为48位的硬件地址 在局域网内发送ARP 请求广播 查找主机的硬件地址
4. 主机B ARP 协议层接收到主机A 的ARP 请求后 ,将本机的硬件地址添加到应答包 发送ARP应答到主机A
5. (ARP高速缓存保存 B 地址 B 保存A地址)
发送ICMP 数据包到主机B
6. 主机B收到A 的ICMP 包 发送响应包
7. 主机A收到B 的ICMP 响应包
===-----------------------------------------------------------------------------------
====================================TCP/IP =======================================================
首部长度为 15*4 = 60
TLL 数据报的生存期 每经过一个路由器TLL 减一 为0 时丢弃 并发送ICMP报文通知源主机
协议类型 1 ICMP 2 IGMP 6 TCP 17 UDP
每经过一个路由器 IP报的头部都会发生改变
===----------------------------------------------------------------------------------------
网际校验和
IP首部(不含校验和)反码+ )+0X 1111
===----------------------------------------------------------------------------------------
路由
用源IP与源SubnetMask相与 , 用目的IP 和SubnetMask相与 看看他们是不是相等 在同一局域网中
==------------------------------------------------------------------------------------------
基于字节流的传输服务 magment 没有边界
面向链接
可靠的传输服务 端到端的校验和
缓冲传输
全双工的传输服务
提供流量控制功能
==------------------------------------------------
IP 报文格式 : IP头部 + TCP头部 + TCP 数据
TCP 格式 :TCP头部 + TCP 数据
TCP 头部 : 源端口 + 目的端口 (都为16位)
====----------------------------------------------
远端口 与目的端口 : + IP首部的源IP地址和目的IP地址 唯一确定一个TCP 链接
序号 : 报文段的第一个数据字节序号
确认号 : ACK
===-----------------------------------------------
三次握手 :
A : 发送 序列号为 a TCP段 给 B;
B :发起链接 序列号为 b 确认号为 a+1 (希望A回复的序列号为a+1)
A :序列号为a+1 , 确认号为b+1 (希望B在回复的序类号 为b+1)
===------------------------------------------------
四次挥手 : FIN 位 为1
A : 调用close()函数 序号为 X 表示希望B回复时序列号为 Y
B : 回复A 序列号为Y ACK 为Q希望收到A回复序列号为Q
B : FIN为1 发送断开请求 序列号为 X ACK 为Q (希望A回复序列号为Q)
A : 发送 序列号为Q ACK 为(X+1) 希望B回复序列号为X+1 ;
===-----------------------------------------------------
差错 端到端的校验和
丢包 超时重传
失序 排序号
重复
数据-----分割成TCP认为最适合发送的数据块 ---发送一个段后---启动定时器---等待确认-----超时重发
TCP 校验发生错误 需要重新传送
流量控制 -------- 滑动窗口
累积字节 确认机制
慢启动
拥塞
==-------------------------------------------------
IP 数据 : IP头部 + UDP头部 + UDP数据
=============================================================socket ==========================================
app ---socket----[TCP ----IP ---inet]----->>>>>
|
|
内核已实现
socket ------用户进程与内核网络协议栈的编程接口
sockaddr_in :
uint8_t sin_len;
sa_family_t sin_family ;
in_port_t si_port ;
struct in_addr sin_addr ;
char sin_zero[8];
通用地址结构 :
struct sockaddr {
uint8_t sin_len;
sa_family_t sin_family ;
char sa_dat[14];
};
===------------------------------------------------------------------------------------------------
字节序 :
uint32_t htonl();
uint16_t htons();
#include <netinet/in.h>
#include <arpa/inet.h>
inet_addr(char*) ;
inet_ntoa(struct in_addr in);
===--------------------------socket---------------------------====
回射客户服务器
stdin ------->fgets -------TCP客户--->write----->read--->TCP服务器
stdout<------fputs <-------TCP客户<---read ------<write<--TCP服务器
int socket(int domain , int type , int protcocol);
AF_INET
SOCK_STREAM SOCK_DGRAM
0
#include <sys/types.h>
#include <sys/socket.h>
int bind (int sock , (struct sockaddr*) &serveraddr , sizeof(struct sockaddr_in));
serveraddr.sin_family = AF_INET ;
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
serveraddr.sin_addr.s_addr = inet_addr("127.186.0.8");
inet_aton("127.0.89.0", &serveraddr.sin_addr);
serveraddr.sin_port = htons(8000);
==--------------------------------------------
#include <sys/socket.h>
int listen(int sock , int backlong)
backlong 为监听的数 最大为20 10 5
return 成功为 0 ,失败为 -1 ;
==-----------------------------------------
被动套接字 : 接收链接
主动套接字 :发送数据
int sockfd = accept (int sock , struct sockaddr* addr , socklen_t *addr_len );
失败 返回 -1;
==-----------------------------------------
int connect(int sock , const struct sockaddr* addr , socklen_t addlen);
IP地址和端口 决定一个链接
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int inet_aton(const char *cp, struct in_addr *inp);
in_addr_t inet_addr(const char *cp);
in_addr_t inet_network(const char *cp);
char *inet_ntoa(struct in_addr in);
struct in_addr inet_makeaddr(int net, int host);
in_addr_t inet_lnaof(struct in_addr in);
===----------------------------------------------
REUSEADDR 用法 消除服务器关闭后马上启动时 TIME_WAIT 状态
==-----------------------------------------------
server 进程实现
pid_t pid ;
pid = fork();
if(pid == -1){
perror(pid);
}else if(pid == 0){
while(1){
}
}
ret = read(sock , buff , BUFF_LEN);
if(ret == 0){
cout << "捕捉客户端关闭"<< endl;
}
sock三次握手完成 放到listen队列中
==-------------------------------------------------------
聊天程序
注意僵尸进程的产生
==------------------------------------------------------
write --> 复制消息到内核 BUFF(内核缓存)---->MSS<MTU-40 ----> IP层 分包------>
ssize_t readn(int fd , void* buff , size_t count ){
size_t nleft = count ;
ssize_t nread;
char* bufp = (char*)buff;
while(nleft > 0){
if((nread == read (fd , bufp , nleft)) < 0){
if(errno == EINTR )
continue ;
return -1;
}else if (nread == 0){
return count -nleft ;
}
bufp +=nread ;
nleft -=nread ;
}
return count ;
}
=
=----------------------------------------------------
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t honsshort);
uint32_t ntohl(uint32_t hostlong);
uint16_t ntohs(uint16_t honsshort);
如果发送的不是字符 而是数的时候 必须将 数转换为网络字节序 htonl();
接收时 必须将 网络字节序转换为主机字节序 ntohl();
ret = read(sockfd , &n , 4);
len = ntohl(n);
ret = read(sockfd , buff, len );
==----------------
struct info {
size_t len;
char* buff[1024]={0};
}info;
memset(buff , "hellword");
info.len = htonl(strlen(buff));
ret = write (sockfd , (void*)&info , 4+strlen(buff));
==---------------------------------------------------------
recv send readline getsockname getpeername gethostname gethostbyname gethostbyaddr
recv 只能用于套接口 不能用于文件套接字
ret = read (sockfd , &info.len , 4);
if(ret == -1 && error == EINTR)
continue ;
return ret ;
ftp 格式:
readline 只能用于套接口
int ret ;
int nread;
char * bufp = buff;
int nleft = maxline ;
while(1){
ret = recv_peek (sockfd , bufp , nleft );
if(ret < 0){
return ret ;
}
if(ret == 0){
return 0;
}
nread = ret ;
int i=0;
for(i=0 ; i < nread ; i++){
if(bufp[i] == '
'){
ret = readn(sockfd , bufp , i+1);
if(ret != i+1){
exit(EXIT_FAILURE);
}
return ret ;
}
}
if(nread > nleft )
exit(EXIT_FAILURE);
nleft -= nread ;
ret = readn(sockfd , bufp , nread );
if(ret != nread ){
exit(EXIT_FAILURE);
}
}
==----------------------------------------------------------
getsockname() 获取本地地址以及端口号
getpeername () 获取链接对方的地址和端口号
#include <sys/socket.h>
int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
#include <sys/socket.h>
int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
#include <netdb.h>
extern int h_errno;
struct hostent *gethostbyname(const char *name);
#include <sys/socket.h> /* for AF_INET */
struct hostent *gethostbyaddr(const void *addr,socklen_t len, int type);
void sethostent(int stayopen);
void endhostent(void);
void herror(const char *s);
====----------------------------------------------------------------
僵死进程的管理控制
#include <signal.h>
sighandler_t signal(int signum, sighandler_t handler);
signal(SIGCHLD, SIG_IGN);
signal(SIGCHLD, handle_sigchld);
signal(SIGPIPE, SIG_IGN)
当服务器close一个连接时,若client端接着发数据。
根据TCP 协议的规定,会收到一个RST响应,client再往这个服务器发送数据时,
系统会发出一个SIGPIPE信号给进程,告诉进程这个连接已经断开了,不要再写了。
根据信号的默认处理规则SIGPIPE信号的默认执行动作是terminate(终止、退出),所以client会退出。
若不想客户端退出可以把SIGPIPE设为SIG_IGN
如: signal(SIGPIPE,SIG_IGN);
这时SIGPIPE交给了系统处理。
服务器采用了fork的话,要收集垃圾进程,防止僵尸进程的产生,可以这样处理:
signal(SIGCHLD,SIG_IGN); 交给系统init去回收。
这里子进程就不会产生僵尸进程了。
void handle_sigchld(int sig){
while(waitpid(-1,NULL , WNOHANG(不挂起)) > 0)
没有子进程返回时 返回-1 导致循环退出
}
完美解决僵尸进程
==-----------------------------------------------------------------
NNUIX 套接字:
socketpair(PF_UNIX ,SOCK_STREAM, 0 ,sockfd )
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
====----------------------------------------------------------------------------
POSIX 进程的通信:
System V 进程间通信:
文件:
文件锁:
管道: pipe 命名管道 FIFE 传输的是字节流没有边界的
信号: signal
消息队列:
内存共享:
信号量:
互斥量:
条件变量:
读写锁:
套接字:
==------------------------------------------------------------------------
消息队列:
int msgget(key_t key , int msgflg);
int msgctl(int msgfd , int cmd , struct msgid_ds* buff);
===-----------------------------------------------------------------------
DMA 控制 cpu----不参与
==----------------------------------------------------------------------
用户-------发送信息 -----内核-----应用程序
cup MNU + linux
逻辑地址
网络内嵌内核 主要防止产生大量的终段
close tcp ip 五种服务器模型
段式管理和页式管理
逻辑地址空间------》物理地址
分时操作系统-----时间片段的轮转
==------------------------------进程三态--------------------
VFS 虚拟文件系统
创建 -----------------就绪
运行------------------调度
I/O -----------------等待
--------------------------------------------------
进程的状态 :
运行----TASK_RUNING
可中断 ---TASK_INTERRUPTBLE
不可中断 ---TASK_UNINTERRUPTBL
暂停 ------TASK_STOPPED
僵死 ------TASK_ZOMBIE
ps -ef
0 号进程 -----内核
进程函数终止的五种状态:
_exit();
exit();
0 --- 正常退出
1-----异常退出
fork();
execl();
int atexit(void(* function)(void));
===============================================
emon函数的用法
说明:
让一个程序后台运行。
原型:
[c-sharp] view plaincopy
#include <unistd.h>
int daemon(int nochdir, int noclose);
参数:
当 nochdir为零时,当前目录变为根目录,否则不变;
当 noclose为零时,标准输入、标准输出和错误输出重导向为/dev/null,也就是不输出任何信 息,否则照样输出。
返回值:
deamon()调用了fork(),如果fork成功,那么父进程就调用_exit(2)退出,所以看到的错误信息 全部是子进程产生的。如果成功函数返回0,否则返回-1并设置errno。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <limits.h>
int main(int argc, char *argv[])
{
char strCurPath[PATH_MAX];
if(daemon(1, 1) < 0)
{
perror("error daemon.../n");
exit(1);
}
sleep(10);
if(getcwd(strCurPath, PATH_MAX) == NULL)
{
perror("error getcwd");
exit(1);
}
printf("%s/n", strCurPath);
return 0;
}
==----------------------------------------------------------------------------
守护进程 :setsid(); 创建一个进程 在子进程中调用setsid(); pid = setsid();
:daemon(); daemon() 已近包含了fork() pid= daemon(1,1) ,if pid > 0 创建成功
会话期 组长
=====-------------------------------------------------------------------------------
子进程退出时向父进程发送一个信号 SIGCHLD
==-----------------------------------------------------------------------------
system : fork------>excel------->waitpid();
==--------------------------------------------------------------------------------
中断信号 :
signal()
sigaction();
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
===-------------------------------------------------------------------------------
shell:
find : find -name "*.*";
grep : 按行查找
awk : 按列查找
sed : 编辑 替换 sed '1,3'
sed 's/s1/s2/g'
==-------------------------------------------------------------------------------
共享内存 :ipcs ---
共享内存被占用的时候 ,不会马上删除。