应用开发
2023-01-07 10:54:45 0 举报
AI智能生成
c语言线程,进程,网络通信总结
作者其他创作
大纲/内容
文件管理
IO文件操作
7种文件类型
普通,目录,管道,链接,套接字,块,字符设备文件
打开文件 6种方式(r ,r+, w,w+,a,a+)
读写文件 fread fwrite
关闭文件fclose
操作对象
FILE * fp 文件流指针
IO 函数操作,不能操作内核,设备
文件描述符 大于0的正整数
函数
access
int access(const char*pathname,int mode)
① pathname -- 文件名,目录名 -- F_OK(存在与否)
② mode --- F_OK W_OK X_OK R_OK
open
int open(const char*pathname,int flags,int mode)
mode
权限 八进制数表示 0644 0755
int open(const char*pathname,int flags)
flags 打开方式
O_RDWR 可读可写
O_RDONLY 只读
O_WRONLY 只写
O_APPEND 追加
O_CREAT 创建
read
ssize_t read(int fd,char*buf,size_t count)
① fd -- open的返回值
② buf -- 缓冲区大小
③ count -- 读取的字节数
④ 返回值:读取的个数
write
ssize_t write(int fd,char*buf,size_t count)
lseek (重新设置读写的位置)
off_t lseek(int fd,off_t offset,int whence)
① offset -- 相对于whence的偏移量(+,-,0)
② whence --- SEEK_SET 开头 SEEK_CUR 当前 SEEK_END 末尾
③ 返回值:当前位置距离开头的偏移量
stat (文件的状态信息)
int stat(const char *path,struct stat*buf)
① path -- 文件名,路径名
② buf -- 存放,结构体内容
③ 返回值 -- 0 -- success -1 -- erro
目录操作
mkdir (创建目录)
int mkdir(const char*pathname,mode_t mode)
① mode -- 权限
② 返回值: 0 -- success -1-- error
rmdir
rmdir(const char*pathname)
opendir (打开目录)
DIR*dir = opendir(const char*pathname)
pathname -- 目录流
dir --- 目录流指针(存放目录的信息)
readdir (读取目录中的文件信息)
struct dirent *sp = readdir(DIR*dir)
dir --- 目录流指针
sp --- 指向dir目录的结构体指针
d_name 文件名字
子主题
rename (修改名字)
rename(const char *oldname,const char*newname)
remove(删除文件,或者空的目录)
int remove(const char*pathname)
任务(进程)
头文件
<stdio.h>,<unistd.h>,<stdlib.h>,<sys/types.h>
process(进程,动态的过程)
进程的状态
新建 就绪 运行 阻塞 结束 (三个回路)
ps aux--- linux中的进程
进程的特性
动态性 --- pid不连续,动态产生,动态消亡
并发性 --- 同时执行 -- 宏观
异步性 --- 不同时执行 -- 微观角度,时间差
独立性 --- 父子进程运行在不同的空间中
结构特性 --- 空间不同,所以具有不同的空间体系(栈,堆,数据,代码)
进程的概念
1、独立可调度的活动
2、抽象的实体,申请和释放资源
3、并行执行,多进程提供基础
4、进程是程序的一次执行过程,动态的,程序执行和资源管理的最小单元。
进程的标识符
进程的标识符
父进程 -- 创建子进行
fork() -- 函数
fork() -- 函数
pid_t pid = 0;
pid = fork();
pid = fork();
if(pid == -1)
{
perror("fork");
exit(1);
}
if(pid == -1)
{
perror("fork");
exit(1);
}
else if(pid == 0)
{
//子进程
printf("the child process![%d-%d]\n",getpid(),getppid());
while(1);
}
{
//子进程
printf("the child process![%d-%d]\n",getpid(),getppid());
while(1);
}
else
{
//父进程
printf("the parent process![%d-%d]\n",pid,getpid());
while(1);
}
{
//父进程
printf("the parent process![%d-%d]\n",pid,getpid());
while(1);
}
getpid() --- 返回当前进程的pid
getppid() -- 子进程中返回父进程的编号
进程间通信
头文件
<stdio.h> <sys/ipc.h> <sys/shm.h> <stdlib.h> <sys/types.h>
pipe --- 创建无名管道
int pipe(int pfd[2]);
pfd[2] -- 接收管道的描述符
返回值:0 -- success -1 --- error
mkfifo() --- 创建有名管道
int mkfifo(const char*pathname,mode_t mode);
pathname -- 文件名,路径名
mode -- 权限
返回值:0--- success -1 --- error
创建共享内存
查看共享内存:ipcs -m
编程步骤:
1、创建共享内存
2、映射共享内存
3、使用共享内存
4、断开共享内存
5、回收共享内存
int shmid = shmget(key_t key,size_t size,int shmflg);
key -->ftok() -- 返回值获得
key_t key = ftok(const char*pathname,int pro_id);
pathname -- 存在
返回值:键值
size --->共享内存大小
shmflg --> IPC_CREAT|权限
返回值:shmid --- >共享内存的标识符
线程
概念
线程
(1) 什么是线程
① 线程是进程的一个实体,轻量级进程
② 程序执行流的最小单元
③ 被系统独立调度和分派基本单元
(2) 线程和进程关系
① 进程和线程的概念
② 如果只有一个进程,那么这个进程既是进程也叫做线程
(3) 线程A --创建--- 线程B (线程B只拥有自己需要一点儿栈段空间,其他的共享资源)
(4) 运行图
线程的创建
头文件
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
void *(*start_routine) (void *), void *arg);
1、pthread_t*thread --- 新线程的编号
2、attr -- 线程的属性 NULL(默认属性)
3、void *(*start_routine) (void *) --》start_routine 指向返回值和参数都是void*的函数,---》新的线程
4、void*arg -- 作为新的线程的参数
5、返回值:success -- 0 error --- 错误编码 strerror打印
pthread_self() -- 返回线程的编号
pthread_join(pthread_t pid,void**arg) -- 等待新的线程的退出
pthread_exit(void*arg) -- 线程的结束函数(进程结束exit())
互斥锁:锁的机制,上锁和解锁。
互斥问题 -- 只允许一个线程访问
同步问题 -- 在互斥保护下对于线程的有序访问
共享资源的访问 --- 全局变量(两个进程都可以访问到)
通过加锁和解锁的方式实现线程的通信,解决了线程的互斥的问题
1、申请互斥锁
(1) 静态初始化 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 快速互斥锁
(2) 动态初始化 pthread_mutex_init(&mutex,NULL); NULL -- 默认快速互斥锁
2、上锁
pthread_mutex_lock(&mutex);
3、解锁
pthread_mutex_unlock(&mutex);
4、回收资源
pthread_mutex_destroy(&mutex);
条件变量:条件,变量的改变 -- 线程的执行
在互斥锁的保护下
读文件 -- 线程 写文件 -- 线程
执行 条件为真阻塞 -- 不执行了-- 释放锁
改变条件-阻塞线程发送唤醒信号 接受到对方信号--不阻塞执行
执行完成,改变条件让自己继续阻塞
读文件 -- 线程 写文件 -- 线程
执行 条件为真阻塞 -- 不执行了-- 释放锁
改变条件-阻塞线程发送唤醒信号 接受到对方信号--不阻塞执行
执行完成,改变条件让自己继续阻塞
总结:读线程先执行 -- 写的线程在执行(重复上一次过程)
条件变量的操作流程
1、初始化
(1) 静态初始化 -- pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
(2) 动态初始化 -- pthread_cond_init(&cond,NULL);
2、阻塞线程(条件为真)
pthread_cond_wait(&cond,&mutex); 等待被唤醒,阻塞一个线程
3、唤醒一个阻塞的线程
pthread_cond_signal(&cond); -- 唤醒线程执行
4、回收资源
pthread_cond_destroy(&cond) -- 回收条件变量
前提,2个线程必须在互斥锁的保护下进行
网络 (网络应用)
UDP编程
客户端
创建socket通信
int sockfd = socket(AF_INET,SOCK_DGRAM,0)
AF_INET IPV4的协议族
SOCK_DGRAM(数据报套接字)
protocol:指定应用程序使用的通讯协议,一般取值为0,让系统决定使用协议
成功:返回新建的套接字描述符。失败返回-1,并将错误代码放入erron
设置ip和port
struct sockaddr_in seraddr = {0}
清空:bzero(&seraddr,sizeof(seraddr));
seraddr.sin_family = AF_INET;(协议)
seraddr.sin_port = htons(atoi(端口号));
seraddr.sin_addr.s_addr = inet_addr(IP地址);
socklen_t len = sizeof(seraddr);
发送消息
sendto(sockfd,str,sizeof(str),0,(struct sockaddr*)&seraddr,&len);
str:发送的字符串
seraddr:服务器地址
len:seraddr长度
接收消息
ssize_t rv=recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr*)&seraddr,&len);
rv:接收消息的长度
关闭套接字
close(sockfd);
服务器
创建socket通信
int sockfd=socket(AF_INET,SOCK_DGRAM,0)
绑定IP和port
struct sockaddr_in seraddr={0};
接收消息
struct sockaddr_in cliaddr;
ssize_t rv=recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr*)&cliaddr,&len);
关闭套接字
close(sockfd);
TCP编程
客户端
创建socket通信
int sockfd=socket(AF_INET,SOCK_STREAM,0);
AF_INET ipv4的协议族
流式套接字(SOCK_STREAM)
protocol:指定应用程序使用的通讯协议,一般取值为0,让系统决定使用的协议。
成功:返回新建的套接字描述符;失败:返回-1。并将错误码放入 errno
设置ip和port
struct sockaddr_in seraddr = {0}
清空:bzero(&seraddr,sizeof(seraddr));
seraddr.sin_family = AF_INET;(协议)
seraddr.sin_port = htons(atoi(端口号));
seraddr.sin_addr.s_addr = inet_addr(IP地址);
socklen_t len = sizeof(seraddr);
连接服务器
int flag=connect(sockfd,(struct sockaddr*)&seraddr,len)
返回值:0--success 1--error
发送消息
sd=send(sockfd,buf,sizeof(buf),0);
-1失败
也可以用write发送消息格式同read
接收消息
rd=read(sockfd,buf,sizeof(buf));
-1失败
recv接收消息,格式同send
关闭套接字
服务器
创建socket通信
int sockfd=socket(AF_INET,SOCK_STREAM,0);
AF_INET ipv4的协议族
流式套接字(SOCK_STREAM)
protocol:指定应用程序使用的通讯协议,一般取值为0,让系统决定使用的协议。
成功:返回新建的套接字描述符;失败:返回-1。并将错误码放入 errno
设置ip和port
struct sockaddr_in seraddr = {0}
清空:bzero(&seraddr,sizeof(seraddr));
seraddr.sin_family = AF_INET;(协议)
seraddr.sin_port = htons(atoi(端口号));
seraddr.sin_addr.s_addr = inet_addr(IP地址);
socklen_t len = sizeof(seraddr);
监听
flag=listen(sockfd,20);
sockfd:监听客户端连接请求
20:l连接队列中的最大个数,自定
接收请求
struct sockaddr_in cliaddr;
socklen_t len=sizeof(cliaddr);
accfd=accept(sockfd,(struct sockaddr*)&cliaddr,&len)
接收消息
rd=read(accfd,buf,sizeof(buf))
-1失败
发送消息
sd=send(accfd,buf,sizeof(buf),0);
-1失败
关闭套接字
通信协议设计
客户端
创建socket通信
int sockfd=socket(AF_INET,SOCK_STREAM,0);
AF_INET ipv4的协议族
流式套接字(SOCK_STREAM)
protocol:指定应用程序使用的通讯协议,一般取值为0,让系统决定使用的协议。
成功:返回新建的套接字描述符;失败:返回-1。并将错误码放入 errno
设置ip和port
struct sockaddr_in seraddr = {0}
清空:bzero(&seraddr,sizeof(seraddr));
seraddr.sin_family = AF_INET;(协议)
seraddr.sin_port = htons(atoi(端口号));
seraddr.sin_addr.s_addr = inet_addr(IP地址);
socklen_t len = sizeof(seraddr);
连接服务器
int flag=connect(sockfd,(struct sockaddr*)&seraddr,len)
返回值:0--success 1--error
申请空间
char *comm = (char*)malloc(sizeof(struct head)+sizeof(struct body));
head结构体
int type
int id
body结构体
组报
memcpy(comm,&head,sizeof(head));
head存入
②memcpy(comm+sizeof(HEAD),&body,sizeof(body));
body存入
发送消息
sd = send(sockfd,comm,sizeof(HEAD)+sizeof(BODY),0);
-1失败
接收消息
rd=read(sockfd,buf,sizeof(buf));
-1失败
释放空间
free
关闭套接字
服务端
创建socket通信
int sockfd=socket(AF_INET,SOCK_STREAM,0);
AF_INET ipv4的协议族
流式套接字(SOCK_STREAM)
protocol:指定应用程序使用的通讯协议,一般取值为0,让系统决定使用的协议。
成功:返回新建的套接字描述符;失败:返回-1。并将错误码放入 errno
设置ip和port
struct sockaddr_in seraddr = {0}
清空:bzero(&seraddr,sizeof(seraddr));
seraddr.sin_family = AF_INET;(协议)
seraddr.sin_port = htons(atoi(端口号));
seraddr.sin_addr.s_addr = inet_addr(IP地址);
socklen_t len = sizeof(seraddr);
监听
flag=listen(sockfd,20);
sockfd:监听客户端连接请求
20:l连接队列中的最大个数,自定
接收请求
struct sockaddr_in cliaddr;
socklen_t len=sizeof(cliaddr);
accfd=accept(sockfd,(struct sockaddr*)&cliaddr,&len)
申请空间
char *comm = (char*)malloc(sizeof(struct head)+sizeof(struct body));
head结构体
int type
int id
body结构体
接收消息
rd = read(accfd,comm,sizeof(HEAD)+sizeof(BODY));
-1失败
解析消息内容
HEAD*head = NULL;head = (HEAD*)comm;
BODY*body = NULL;body = (BODY*)(comm+sizeof(HEAD)
发送消息
sd=send(accfd,buf,sizeof(buf),0);
释放空间
free
关闭套接字
select函数(非阻塞链接)
申请文件描述符集合
fd_set readfd;
分支主清空文件描述符的集合题
FD_ZERO(&readfd);
文件描述符顺次添加到文件描述符的集合中
int fd = 0;FD_SET(fd,&readfd);
fd最大5
设置等待时间
struct timeval tm;
int retval = 0;
int retval = 0;
内核监控(select)readfd中文件描述符的变化
select(maxfd+1(6),&readfd,NULL,NULL,&tm);
返回值
-1:error
0:无输入
阻塞的方式等待tm时长
maxfd最大值
判断FD_ISSET()判断readfd中文件描述符状态是否可读,如果可
读--通信,如果对方断开,把文件描述符从文件描述符集合中清
除(FD_CLR())
读--通信,如果对方断开,把文件描述符从文件描述符集合中清
除(FD_CLR())
FD_ISSET(fd,&readfd)>0可读,通信
否则循环判断fd变化
优缺点
最大连接个数的限制 1024
效率问题低,主要对于集合操作,线性扫面(所有集合中数据查看)
内存拷贝问题,select机制,把所有的文件描述符的集合方法内核中。
函数少,编码简单,能够实现IO的多路复用。
epoll函数(非阻塞链接)
创建socket通信
int sockfd=socket(AF_INET,SOCK_STREAM,0);
AF_INET ipv4的协议族
流式套接字(SOCK_STREAM)
protocol:指定应用程序使用的通讯协议,一般取值为0,让系统决定使用的协议。
成功:返回新建的套接字描述符;失败:返回-1。并将错误码放入 errno
设置ip和port
struct sockaddr_in seraddr = {0}
清空:bzero(&seraddr,sizeof(seraddr));
seraddr.sin_family = AF_INET;(协议)
seraddr.sin_port = htons(atoi(端口号));
seraddr.sin_addr.s_addr = inet_addr(IP地址);
socklen_t len = sizeof(seraddr);
监听
flag=listen(sockfd,20);
sockfd:监听客户端连接请求
20:l连接队列中的最大个数,自定
创建epoll节点
int epollfd = epoll_create(EPOLL_FLAG);
EPOLL_CTL_ADD 添加fd到节点上
EPOLL_CTL_MOD 修改fd在节点上
EPOLL_CTL_DEL 从节点删除fd
创建节点的结构体并赋值
struct epoll_event ev;
bzero(&ev,sizeof(ev));
ev.events = EPOLLIN;
添加可读的事件
可读EPOLLIN 可写EPOLLOUT 异常EPOLLERR
ev.data.fd = sockfd;
文件描述符
文件描述符 -- 添加到epoll的节点
int flag = epoll_ctl(epollfd,EPOLL_CTL_ADD,sockfd,&ev);
0:success
-1:error
创建监控事件的结构体数组
struct epoll_event even[EPOLL_FLAG];
EPOLL_FLAG值自定义
epoll内核监控这些节点
maxfd = epoll_wait(epollfd,even,EPOLL_FLAG,-1);
even:存储事件
EPOLL_FLAG:检测个数
时间 0 --- 立即返回 , -1-- 阻塞等待事件到来
返回值
活跃的文件描述符的个数max_fd
-1:error
轮询查看文件描述符变化
int ifd = even[i].data.fd;
if((ifd == sockfd)&&(even[i].events == EPOLLIN))
struct sockaddr_in cliaddr;
socklen_t len=sizeof(cliaddr);
accfd=accept(sockfd,(struct sockaddr*)&cliaddr,&len);
accfd添加到epollfd
bzero(&ev,sizeof(ev));
ev.events = EPOLLIN;
ev.data.fd = accfd;
if(epoll_ctl(epollfd,EPOLL_CTL_ADD,accfd,&ev)==-1)
{
perror("epoll_ctl");
exit(1);
}
ev.events = EPOLLIN;
ev.data.fd = accfd;
if(epoll_ctl(epollfd,EPOLL_CTL_ADD,accfd,&ev)==-1)
{
perror("epoll_ctl");
exit(1);
}
else
接收请求
struct sockaddr_in cliaddr;
socklen_t len=sizeof(cliaddr);
accfd=accept(sockfd,(struct sockaddr*)&cliaddr,&len)
接收消息
rd=read(accfd,buf,sizeof(buf));
-1失败
0:断开连接
回收资源 ,fd -- epollfd中删除
epoll_ctl(epollfd,EPOLL_CTL_DEL,accfd,NULL);
成功
发送消息
sd=send(accfd,buf,sizeof(buf),0);
sd=send(accfd,buf,sizeof(buf),0);
关闭套接字
相对于select的改进
没有最大并发量的限制,打开文件的个数(6位数)
效率问题:只管活跃的文件描述符
内存拷贝问题:epoll机制,会和内存实现“内存共享”方式,减少拷贝,提高效率
0 条评论
下一页