怎么制作免费建网站网站数据采集怎么做
news/
2025/9/26 7:10:58/
文章来源:
怎么制作免费建网站,网站数据采集怎么做,python 做网站优势,深圳 网页制作一、进程间通信原理#xff1a;
1、通信是有成本的#xff1a;两个或者多个进程#xff0c;实现数据层面的交互#xff0c;因为进程独立性的存在#xff0c;导致进程通信的成本比较高。
2、进程间通信的方式#xff1a; ①基本数据 ②发送命令 ③某种协同 ④通知 .....…一、进程间通信原理
1、通信是有成本的两个或者多个进程实现数据层面的交互因为进程独立性的存在导致进程通信的成本比较高。
2、进程间通信的方式 ①基本数据 ②发送命令 ③某种协同 ④通知 ......
3、进程间通信的本质必须让不同的进程看到同一份资源——特定形式的内存空间。这个“资源”一般是操作系统提供第三方空间为什么不是我们两个进程中的一个呢因为这样会破坏进程独立性。进程访问这个空间进行通信本身就是访问操作系统而进程代表的就是用户。“资源”从创建使用一般到释放都要使用系统调用接口。所以从底层设计从接口设计都要从操作系统独立设计。
4、一般操作系统会有一个独立的通信模块它隶属于文件系统称为IPC通信模块。进程间通信是有标准的——system V possix。
5、基于文件级别的通信方式——管道。
二、匿名管道本质是文件让不同的进程看到同一份资源。
1. 原理 · 父进程fork创建出子进程子进程会拷贝父进程的文件描述符表此时父进程与子进程都会有相应的读写端指向同一个文件。此时根据要求关闭父进程与子进程相应的读写端来形成单向通信的信道。 · 同一个文件是内存级的每个文件都存在自己的缓冲区如果双方想向自己的缓冲区中写入子进程就可以通过缓冲区读取实现进程间通信。正因为其只能进行单向通信故称其为管道。 · 若进行双向通信用多个管道即可。 · 必须有血缘关系的进程才能通信常用于父子关系兄弟关系和爷孙关系也可。 · 管道是有固定大小的在不同内核里大小可能有差别。
2、接口 输出型参数将文件的文件描述符数字带出来让用户使用。 pipefd[0]读下标。 pipefd[1]写下标。
//testPipe.cc#include iostream
#include cstdio
#include string
#include cstring
#include cstdlib // stdlib.h
#include unistd.h
#include sys/types.h
#include sys/wait.h#define N 2
#define NUM 1024using namespace std;//child:用户级缓冲区拷贝到文件级缓冲区
void Writer(int wfd)
{string s hello, I am child;pid_t self getpid();int number 0;char buffer[NUM];while(true){//构建发送字符串buffer[0] 0;//字符串清空只是为了提醒阅读代码的人我把这个数组当做字符串了snprintf(buffer, sizeof(buffer), %s-%d-%d, s.c_str(), self, number);//int snprintf(char* str, size_t size, const char *format, ...);cout buffer endl;//发送/写入给父进程,system callwrite(wfd, buffer, strlen(buffer));//ssize_t write(int fd, const void * buf, size_t count);//向文件写入不需要1sleep(1);}
}//father:内核级缓冲区拷贝到应用层缓冲区
void Reader(int rfd)
{char buffer[NUM];while(true){buffer[0] 0;//system callssize_t n read(rfd, buffer, sizeof(buffer));//ssize_t read(int fd, void *buf, size_t count);//sizeof ! strlen, 代表buffer缓冲区大小if(n 0){buffer[n] 0;// 0 \0cout father get a message[ getpid() ]# buffer endl;}//TODO}
}int main()
{int pipefd[N] {0};int n pipe(pipefd);if(n 0) return 1;//cout pipefd[0]: pipefd[0] , pipefd[1]: pipefd[1] endl;//child - w, father - rpid_t id fork();if(id 0) return 2;if(id 0){//childclose(pipefd[0]);//IPC codeWriter(pipefd[1]);close(pipefd[1]);exit(0);}//fatherclose(pipefd[1]);//IPC codeReader(pipefd[0]);pid_t rid waitpid(id, nullptr, 0);if(rid 0) return 3;close(pipefd[0]);return 0;
}
//MakefiletestPipe:testPipe.ccg $^ -o $ -stdc11
.PHONY:clean
clean:rm -f testPipe 3、编码实现
管道的五大特征 ①具有血缘关系的进程进行进程间通信。 ②管道只能单向通信。 ③多执行流共享的难免出现访问冲突的问题。临界资源竞争的问题。所以父子进程是会进程协同同步与互斥保护管道文件的数据安全。 ④管道是面向字节流的 ⑤那是基于文件的而对象的生命周期是随进程的
管道四种的情况 ①读写端正常管道如果为空读端就要堵塞 ②读写端正常管道如果被写满写端就要阻塞 ③读端正常读写端关闭读端就会读到零表明读到的文件pipe结尾不会被阻塞。 ④写端是正常写入读端关闭了。操作系统就要通过信号杀掉正在写入的进程。转为③所以子进程写入父进程读取操作系统是不会做低效浪费等类似的工作的如果做了就是操作系统的bug。
4、管道的应用场景
①自定义shell指令的判断 a.分析输入的命令行字符串获取有多少个命令打散多个子命令字符串。 b.malloc申请空间pipe先申请多个管道。 c.循环创建多个子进程每一个子进程的重定向情况。最开始输出重定向1-指定的一个管道的写端。中间输入输出重定向0标准输入重定向到上一个管道的读端标准输出重定向到下一个管道的写端。最后一个将输入重定向将标准输入重定向到最后一个管道的读端。 d.分别让不同的子进程执行不同的命令---exec*exec*不会影响该进程曾经打开的文件不会影响预先设置好的管道重定向。
②简易版本的进程池降低系统调用和成本。
ProcessPool.cc#include Task.hpp
#include string
#include vector
#include cstdlib
#include cassert
#include ctime
#include unistd.h
#include sys/wait.h
#include sys/stat.hconst int processnum 10;//void LoadTask(std::vectortask_t *tasks);std::vectortask_t tasks;// 先描述
class channel
{
public:channel(int cmdfd, pid_t slaverid, const std::string processname):_cmdfd(cmdfd), _slaverid(slaverid), _processname(processname){}
public:int _cmdfd; // 发送任务的文件描述符pid_t _slaverid; // 子进程PIDstd::string _processname;// 子进程的名字————方便我们打印日志
};void slaver()
{// read(0)while(true){int cmdcode 0;int n read(0, cmdcode, sizeof(int)); // 如果父进程不给子进程发送数据呢阻塞等待if(n sizeof(int)){//执行cmdcode对应的任务列表std::cout slaver say get a command: getpid() : cmdcode: cmdcode std::endl;if(cmdcode 0 cmdcode tasks.size()) tasks[cmdcode]();}if(n 0) break;}
}//编码规范
//输入 const
//输出 *
//输入输出
void InitProcessPool(std::vectorchannel *channels)
{// version 2 : 确保每一个子进程都只有一个写端std::vectorint oldfds;for(int i 0; i processnum; i){int pipefd[2]; // 临时空间int n pipe(pipefd);assert(!n);(void)n;pid_t id fork();if(id 0) // child{std::cout child: getpid() close history fd: ;for(auto fd : oldfds) {std::cout fd ;close(fd); }std::cout \n;close(pipefd[1]);dup2(pipefd[0], 0);close(pipefd[0]);slaver();std::cout process : getpid() quit std::endl;// slaver(pipefd[0]);exit(0);}// fatherclose(pipefd[0]);// 添加channel字段了std::string name process- std::to_string(i);channels-push_back(channel(pipefd[1], id, name));oldfds.push_back(pipefd[1]);sleep(1);}
}void Debug(const std::vectorchannel channels)
{// testfor(const auto c : channels){std::cout c._cmdfd c._slaverid c._processname std::endl;}
}void Menu()
{std::cout ##################################### std::endl;std::cout ##### 1、刷新日志 2、刷新野怪 ####### std::endl;std::cout ##3、检测软件是否更新 4、更新血量蓝量## std::endl;std::cout ############## 0、退出 ############### std::endl;std::cout ##################################### std::endl;
}void ctrlSlaver(const std::vectorchannel channels)
{int which 0;//int cnt 5;while(true){int select 0;Menu();std::cout Please Enter ;std::cin select;if(select 0 || select 5) break;//1、选择任务//int cmdcode rand()%tasks.size();int cmdcode select - 1;//2、选择进程:负载均衡(随机数轮转)//int processpos rand()%channels.size();std::cout father say: cmdcode: cmdcode already sendto channels[which]._slaverid process name: channels[which]._processname std::endl;//3、发送任务write(channels[which]._cmdfd, cmdcode, sizeof(cmdcode));which;which % channels.size();//cnt--;//sleep(1);}
}void QuitProcess(const std::vectorchannel channels)
{// version1int last channels.size()-1;for(int i last; i 0; i--){close(channels[i]._cmdfd);waitpid(channels[i]._slaverid, nullptr, 0);}// for(const auto c : channels) close(c._cmdfd);// //sleep(5);// for(const auto c : channels) waitpid(c._slaverid, nullptr, 0);// //sleep(5);
}int main()
{LoadTask(tasks);srand(time(nullptr)^getpid()^1023); // 种一颗随机数种子// 再组织std::vectorchannel channels;// 1、初始化InitProcessPool(channels);Debug(channels);// 2、开始控制子进程ctrlSlaver(channels);// 3、清理收尾QuitProcess(channels);return 0;
}
//Task.hpp#pragma once#include iostream
#include vectortypedef void (*task_t)();void task1()
{std::cout lol 刷新日志 std::endl;
}void task2()
{std::cout lol 更新野区刷新出来野怪 std::endl;
}void task3()
{std::cout lol 检测软件是否更新如果需要就提示用户 std::endl;
}void task4()
{std::cout lol 用户释放技能更新血量蓝量 std::endl;
}void LoadTask(std::vectortask_t *tasks)
{tasks-push_back(task1);tasks-push_back(task2);tasks-push_back(task3);tasks-push_back(task4);
}
##MakefileProcessPool:ProcessPool.ccg $^ -o $ -stdc11
.PHONY:clean
clean:rm -f ProcessPool
以上为具有血缘关系的进程进行进程间通信 如果毫不相关的进程进行进程间通信呢
三、命名管道 1、 理解如果两个不同的进程打开同一个文件的时候在内核中操作系统会打开同一个文件。 · 进程间通信的前提先让不同进程看到同一份资源。 · 管道文件不需要刷盘只是内存级文件。 · 管道怎么打开同一个文件为什么要这么做 通过同路径下的同一个文件名路径文件名具有唯一性的方式让不同进程看到同一份资源进而实现不同进程间通信所以叫命名管道。
2、编码 ①模拟实现命名管道的应用场景
//server.cc#include comm.hppusing namespace std;int main()
{Init init;//打开管道int fd open(FIFO_FILE, O_RDONLY);// 等待写入方打开之后自己才会打开文件向后执行open 阻塞了if(fd 0){perror(open);exit(FIFO_OPEN_ERR);}cout server open file done endl;//开始通信while(true){char buffer[1024] {0};int x read(fd, buffer, sizeof(buffer));if(x 0){buffer[x] 0;cout client say# buffer endl;}else if(x 0){cout client quit, me too!\n endl;break;}else break;}close(fd);return 0;
}
//client.cc#include iostream
#include comm.hppusing namespace std;int main()
{int fd open(FIFO_FILE, O_WRONLY);if(fd 0){perror(open);exit(FIFO_OPEN_ERR);}cout client open file done endl;string line;while(true){cout Please Enter ;getline(cin, line);write(fd, line.c_str(), line.size());}close(fd);return 0;
}
//comm.hpp#pragma once#include iostream
#include string
#include cerrno
#include cstring
#include cstdlib
#include sys/types.h
#include sys/stat.h
#include unistd.h
#include fcntl.h#define FIFO_FILE ./myfifo
#define MODE 0664enum{FIFO_CREATE_ERR 1,FIFO_DELETE_ERR,FIFO_OPEN_ERR
};class Init
{
public:Init(){//创建管道int n mkfifo(FIFO_FILE, MODE);if(n -1){perror(mkfifo);exit(FIFO_CREATE_ERR);}}~Init(){int m unlink(FIFO_FILE);if(m -1){perror(unlink);exit(FIFO_DELETE_ERR);}}
};##Makefile.PHONY:all
all:server clientserver:server.ccg -o $ $^ -g -stdc11
client:client.ccg -o $ $^ -g -stdc11.PHONY:clean
clean:rm -f server client
②模拟实现日志输出时间日志的等级日志内容文件的名称和行号。
//server.cc#include comm.hpp
#include log.hppusing namespace std;int main()
{Init init;Log log;log.Enable(Classfile);//打开管道int fd open(FIFO_FILE, O_RDONLY);// 等待写入方打开之后自己才会打开文件向后执行open 阻塞了if(fd 0){// log.logmessage(Fatal, error string: %s, error code: %d,strerror(errno), errno);log(Fatal, error string: %s, error code: %d,strerror(errno), errno);exit(FIFO_OPEN_ERR);}// log.logmessage(Info, server open file done, error string: %s, error code: %d,strerror(errno), errno);// log.logmessage(Warning, server open file done, error string: %s, error code: %d,strerror(errno), errno);// log.logmessage(Fatal, server open file done, error string: %s, error code: %d,strerror(errno), errno);// log.logmessage(Debug, server open file done, error string: %s, error code: %d,strerror(errno), errno);log(Info, server open file done, error string: %s, error code: %d,strerror(errno), errno);log(Warning, server open file done, error string: %s, error code: %d,strerror(errno), errno);log(Fatal, server open file done, error string: %s, error code: %d,strerror(errno), errno);log(Debug, server open file done, error string: %s, error code: %d,strerror(errno), errno);//开始通信while(true){char buffer[1024] {0};int x read(fd, buffer, sizeof(buffer));if(x 0){buffer[x] 0;cout client say# buffer endl;}else if(x 0){// log.logmessage(Debug, client quit, me too, error string: %s, error code: %d,strerror(errno), errno);log(Debug, client quit, me too, error string: %s, error code: %d,strerror(errno), errno);break;}else break;}close(fd);return 0;
}
//client#include iostream
#include comm.hppusing namespace std;int main()
{int fd open(FIFO_FILE, O_WRONLY);if(fd 0){perror(open);exit(FIFO_OPEN_ERR);}cout client open file done endl;string line;while(true){cout Please Enter ;getline(cin, line);write(fd, line.c_str(), line.size());}close(fd);return 0;
}
//comm.hpp#pragma once#include iostream
#include string
#include cerrno
#include cstring
#include cstdlib
#include sys/types.h
#include sys/stat.h
#include unistd.h
#include fcntl.h#define FIFO_FILE ./myfifo
#define MODE 0664enum{FIFO_CREATE_ERR 1,FIFO_DELETE_ERR,FIFO_OPEN_ERR
};class Init
{
public:Init(){//创建管道int n mkfifo(FIFO_FILE, MODE);if(n -1){perror(mkfifo);exit(FIFO_CREATE_ERR);}}~Init(){int m unlink(FIFO_FILE);if(m -1){perror(unlink);exit(FIFO_DELETE_ERR);}}
};
//log.hpp#pragma once#include iostream
#include time.h
#include stdarg.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include unistd.h
#include stdlib.h#define SIZE 1024#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4#define Screen 1
#define Onefile 2
#define Classfile 3#define LogFile log.txtclass Log
{
public:Log(){printMethod Screen;path ./log/;}void Enable(int method){printMethod method;}std::string levelToString(int level){switch(level){case Info: return Info;case Debug: return Debug;case Warning: return Warning;case Error: return Error;case Fatal: return Fatal;default: return None;}}// void logmessage(int level, char *format, ...)// {// time_t t time(nullptr);// struct tm *ctime localtime(t);// char leftbuffer[SIZE];// snprintf(leftbuffer, sizeof(leftbuffer), [%s][%d-%d-%d %d:%d:%d], levelToString(level).c_str(), // ctime-tm_year1900, ctime-tm_mon1, ctime-tm_mday, // ctime-tm_hour, ctime-tm_min, ctime-tm_sec);// // va_list s;// // va_start(s, format);// char rightbuffer[SIZE];// vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);// // va_end(s);// //格式默认部分自定义部分// char logtxt[SIZE*2];// snprintf(logtxt, sizeof(logtxt), %s %s\n, leftbuffer, rightbuffer);// // printf(%s, logtxt);//暂时打印// printLog(level, logtxt);// }void printLog(int level, const std::string logtxt){switch(printMethod){case Screen:std::cout logtxt std::endl;break;case Onefile:printOneFile(LogFile, logtxt);break;case Classfile:printClassFile(level, logtxt);break;default:break;}}void printOneFile(const std::string logname, const std::string logtxt){std::string _logname path logname;int fd open(_logname.c_str(), O_WRONLY|O_CREAT|O_APPEND, 0666); // log.txtif(fd 0) return;write(fd, logtxt.c_str(), logtxt.size());close(fd);}void printClassFile(int level, const std::string logtxt){std::string filename LogFile;filename .;filename levelToString(level); // log.txt.Debug/Warning/FatalprintOneFile(filename, logtxt);}~Log(){}void operator()(int level, const char *format, ...){time_t t time(nullptr);struct tm *ctime localtime(t);char leftbuffer[SIZE];snprintf(leftbuffer, sizeof(leftbuffer), [%s][%d-%d-%d %d:%d:%d], levelToString(level).c_str(), ctime-tm_year1900, ctime-tm_mon1, ctime-tm_mday, ctime-tm_hour, ctime-tm_min, ctime-tm_sec);va_list s;va_start(s, format);char rightbuffer[SIZE];vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);va_end(s);//格式默认部分自定义部分char logtxt[SIZE*2];snprintf(logtxt, sizeof(logtxt), %s %s\n, leftbuffer, rightbuffer);// printf(%s, logtxt);//暂时打印printLog(level, logtxt);}
private:int printMethod;std::string path;
};// 拓展可变参数(可变参数必须要至少一个实参)
// int sum(int n, ...)
// {
// va_list s; // char*
// va_start(s, n);// int sum 0;
// while(n)
// {
// sum va_arg(s, int);
// n--;
// }// va_end(s);
// return sum;
// }
//Makefile.PHONY:all
all:server clientserver:server.ccg -o $ $^ -g -stdc11
client:client.ccg -o $ $^ -g -stdc11.PHONY:clean
clean:rm -f server client
四、System共享内存 1、原理每个进程都要有自己对应的地址空间有自己对应的tast_struct通过页表将自己地址空间中的内容映射到物理内存中。在物理内存中创建一块共享空间共享空间通过页表映射到进程的共享区当中并给应用层返回一个所对应连续内存空间的起始虚拟地址。从此两个进程就可以通过各自页表访问到同一块物理内存了。
2、申请共享内存步骤 ①申请内存 ②挂接到进程地址空间 ③返回首地址
释放共享内存步骤去关联释放共享内存
3、上面的操作不是进程直接做的进程具有独立性而是直接由操作系统来做系统调用。操作系统中内核结构体描述共享内存再组织从而管理所有内存。
4、接口
创建共享内存(返回消息队列标识符 获取key 让当前进程和指定共享内存链接起来和去关联 删除共享内存 问题1共享内存标识符shmflg IPC_CREAT单独如果你申请的共享内存不存在就创建存在就获取并返回。 IPC_CREAT|IPC_EXCL你申请的共享内存不存在就创建存在就出错并返回。确保如果我们申请成功了一个共享内存这个共享内存一定是一个新的。IPC_EXCL不单独使用
问题2不过你怎么保证让不同的进程看到同一个内存资源呢你怎么知道这个内存资源存在还是不存在呢 谈谈key ①key是一个数字这个数字是几不重要关键在于它必须在内核中具有唯一性能够让不同的进程进行唯一性标识。 ②第一个进程可以通过key创建共享进程。第二个之后的进程只要拿着同一个key就可以和第一个进程看到同一个共享内存了。 ③对于一个已经创建好的共享内存key在哪key在共享内存的描述对象中。 ④第一次创建的时候必须有一个key了。怎么有ftok它是一套算法——通过pathname和proj_id进行数值计算即可pathname和proj_id由用户自由指定。 ⑤key和路径都是唯一的。
问题三key和shmid区别 key操作系统的标定唯一型只在创建管理内存时使用。 shmid只在进程内表示资源的唯一性。
问题四 ①共享内存的生命周期是随内核的用户不主动关闭共享内存会一直存在。除非内核重启用户释放。 ②接口 查看所有的共享内存ipcs -m 删除管理内存ipcrm -m shmid
5、共享内存的特性 ①共享内存没有同步互斥之类的保护机制。 ②共享内存是所有的进程间通信中速度最快的由地址空间映射的方式拷贝少速度快。 ③共享内存内部的数据由用户自己维护。 ④共享内存没有同步机制。
6、共享内存的属性
struct shmid_ds {struct ipc_perm shm_perm; /* operation perms */int shm_segsz; /* size of segment (bytes) */__kernel_time_t shm_atime; /* last attach time */__kernel_time_t shm_dtime; /* last detach time */__kernel_time_t shm_ctime; /* last change time */__kernel_ipc_pid_t shm_cpid; /* pid of creator */__kernel_ipc_pid_t shm_lpid; /* pid of last operator */unsigned short shm_nattch; /* no. of current attaches */unsigned short shm_unused; /* compatibility */void *shm_unused2; /* ditto - used by DIPC */void *shm_unused3; /* unused */
};
7、用共享内存实现进程间通信
// comm.hpp#ifndef __COMM_HPP__
#define __COMM_HPP__#include iostream
#include string
#include cstdlib
#include cstring
#include sys/ipc.h
#include sys/shm.h
#include sys/types.h
#include log.hppusing namespace std;Log log;
//共享内存大小一般的大小一般建议是4096的整数倍
//4097,实际上操作系统给你的是4096*2的大小
const int size 4096;
const string pathname /home/zsx;
const int proj_id 0x6666;key_t GetKey()
{key_t k ftok(pathname.c_str(), proj_id);if(k 0){log(Fatal, ftok error: %s, strerror(errno));exit(1);}log(Info, ftok sucess, key is : 0x%x, k);return k;
}int GetShareMemHelper(int flag)
{key_t k GetKey();// int shmid shmget(k, size, IPC_CREAT | IPC_EXCL | 0666);int shmid shmget(k, size, flag);if(shmid 0){log(Fatal, create share memory error: %s, strerror(errno));exit(2);}log(Info, create share memory success, shmid: %d, shmid);return shmid;
}int CreateShm()
{return GetShareMemHelper(IPC_CREAT | IPC_EXCL | 0666);
}int GetShm()
{return GetShareMemHelper(IPC_CREAT);
}#endif
// log.hpp#pragma once#include iostream
#include time.h
#include stdarg.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include unistd.h
#include stdlib.h#define SIZE 1024#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4#define Screen 1
#define Onefile 2
#define Classfile 3#define LogFile log.txtclass Log
{
public:Log(){printMethod Screen;path ./log/;}void Enable(int method){printMethod method;}std::string levelToString(int level){switch(level){case Info: return Info;case Debug: return Debug;case Warning: return Warning;case Error: return Error;case Fatal: return Fatal;default: return None;}}// void logmessage(int level, char *format, ...)// {// time_t t time(nullptr);// struct tm *ctime localtime(t);// char leftbuffer[SIZE];// snprintf(leftbuffer, sizeof(leftbuffer), [%s][%d-%d-%d %d:%d:%d], levelToString(level).c_str(), // ctime-tm_year1900, ctime-tm_mon1, ctime-tm_mday, // ctime-tm_hour, ctime-tm_min, ctime-tm_sec);// // va_list s;// // va_start(s, format);// char rightbuffer[SIZE];// vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);// // va_end(s);// //格式默认部分自定义部分// char logtxt[SIZE*2];// snprintf(logtxt, sizeof(logtxt), %s %s\n, leftbuffer, rightbuffer);// // printf(%s, logtxt);//暂时打印// printLog(level, logtxt);// }void printLog(int level, const std::string logtxt){switch(printMethod){case Screen:std::cout logtxt std::endl;break;case Onefile:printOneFile(LogFile, logtxt);break;case Classfile:printClassFile(level, logtxt);break;default:break;}}void printOneFile(const std::string logname, const std::string logtxt){std::string _logname path logname;int fd open(_logname.c_str(), O_WRONLY|O_CREAT|O_APPEND, 0666); // log.txtif(fd 0) return;write(fd, logtxt.c_str(), logtxt.size());close(fd);}void printClassFile(int level, const std::string logtxt){std::string filename LogFile;filename .;filename levelToString(level); // log.txt.Debug/Warning/FatalprintOneFile(filename, logtxt);}~Log(){}void operator()(int level, const char *format, ...){time_t t time(nullptr);struct tm *ctime localtime(t);char leftbuffer[SIZE];snprintf(leftbuffer, sizeof(leftbuffer), [%s][%d-%d-%d %d:%d:%d], levelToString(level).c_str(), ctime-tm_year1900, ctime-tm_mon1, ctime-tm_mday, ctime-tm_hour, ctime-tm_min, ctime-tm_sec);va_list s;va_start(s, format);char rightbuffer[SIZE];vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);va_end(s);//格式默认部分自定义部分char logtxt[SIZE*2];snprintf(logtxt, sizeof(logtxt), %s %s\n, leftbuffer, rightbuffer);// printf(%s, logtxt);//暂时打印printLog(level, logtxt);}
private:int printMethod;std::string path;
};// 拓展可变参数
// int sum(int n, ...)
// {
// va_list s; // char*
// va_start(s, n);// int sum 0;
// while(n)
// {
// sum va_arg(s, int);
// n--;
// }// va_end(s);
// return sum;
// }
//processa.cc#include comm.hppextern Log log;int main()
{// sleep(3);int shmid CreateShm();// log(Debug, create shm done);// sleep(5);char *shmaddr (char*)shmat(shmid, nullptr, 0);// log(Debug, attach shm done, shmaddr: 0x%x, shmaddr);// sleep(5);struct shmid_ds shmds;// ipc code// 一旦有人把数据写入共享内存不需要经过系统调用直接就能看到数据了。while(true){cout client say shmaddr endl; // 直接访问共享内存sleep(1);shmctl(shmid, IPC_STAT, shmds);cout shm size: shmds.shm_segsz endl;cout shm nattch: shmds.shm_nattch endl;printf(0x%x\n, shmds.shm_perm.__key);cout shm mode: shmds.shm_perm.mode endl;}shmdt(shmaddr);// log(Debug, detach shm done, shmaddr: 0x%x, shmaddr);// sleep(5);shmctl(shmid, IPC_RMID, nullptr);// log(Debug, destory shm done, shmaddr: 0x%x, shmaddr);// sleep(5);return 0;
}
//processb.cc#include comm.hppint main()
{// sleep(3);int shmid GetShm();// log(Debug, create shm done);// sleep(5);char *shmaddr (char*)shmat(shmid, nullptr, 0);// log(Debug, attach shm done, shmaddr: 0x%x, shmaddr);// sleep(5);// ipc code//一旦有了共享内存挂接到自己的地址空间中直接把它当成自己的内存空间来用即可。//不需要调用系统调用while(true){cout Please Enter ;// char buffer[1024]; // 缓冲区此处没必要因为有内存// fgets(buffer, sizeof(buffer), stdin);// memcpy(shmaddr, buffer, strlen(buffer)1); // 当作字符串fgets(shmaddr, 4096, stdin);}shmdt(shmaddr);// log(Debug, detach shm done, shmaddr: 0x%x, shmaddr);// sleep(5);return 0;
}
##Makefile.PHONY:all
all:processa processbprocessa:processa.ccg -o $ $^ -g -stdc11
processb:processb.ccg -o $ $^ -g -stdc11.PHONY:clean
clean:rm -f processa processb
当然我们也可以利用命名管道实现共享内存的同步有兴趣的同学可以尝试一下
8、mmap函数也是共享内存的一种。选学
五、消息队列了解
1、原理 ①必须让不同的进程看到同一个队列。 ②允许不同的进程向内核中发送带类型的数据块。
2、接口
①创建共享内存(返回消息队列标识符 ②释放共享内存 ③发送/接收消息队列 ④查看所有的共享内存ipcs -q 删除管理内存ipcrm -m msgid
六、IPC内核中的数据结构设计 ①在操作系统中所有的IPC资源都是整合进操作系统里的IPC模块中的 ②创建共享内存/消息队列就要创建对应的数据结构。这些数据结构的第一个字段类型都是ipc_perm管理这些数据结构是通过数组struct ipc_perm *array来管理的。创建共享内存/消息队列时操作系统就要创建对应的数据结构并将第一个字段的地址填入数组结构体数据类型可以不一样因为第一个字段的类型都一样。 ③从此往后管理操纵系统中所有的Ipc资源只要先描述然后对所有资源进行的增删查改转化成对该数组的增删查改。 ④当然也可通过用户输入的key找到每一个对应的ipc资源通过比较第一个字段的ipc_perm中的key确认进程是否已经被创建新旧。 ⑤其中该数组的数组下标为xxxidshmid/msgid……是线性递增的 ⑥当用户未来尝试访问某种资源的时候只要将对应的地址强转成指定的类型就可以自由访问整个结构中的任意类型。 ⑦而操作系统为什么能区分指针指向的对象的类型呢 ipc_perm是操作系统在应用层上的结构体而在内核结构中它的数据结构为kern_ipc_perm。它在第一个字段中增加了一种类型标志位来让代码区分它自己是哪种ipc资源。 ⑧这实际上是多态技术的一种体现。其中ipc_perm就是基类shm_perm/msg_perm就是子类。
七、信号量了解
1、原理概念
1、当我们的进程a正在向共享内存写入时写入了一部分就被进程b拿走了就会导致双方发和收的数据不完整——数据不一致问题因为共享内存没有保护机制。 而管道不会因为管道在通信过程中有原子性保证和同步互斥。
2、 ①AB看到的同一份资源共享资源如果不加保护会导致数据不一致问题。 ②加锁--互斥访问--任何时刻只允许一个执行流访问就是执行访问共享资源——互斥。 ③共享的任何时刻只允许一个执行流访问的资源称为临界资源---一般是内存空间。 ④举例100行代码5到10行代码才在访问临界资源。那我们访问临界资源的代码称为临界区。
3、解释一个现象 多进程、多线程、并发打印时显示器上的信息是错乱的、混乱的、和命令行混在一起的。这就是数据不一致问题。
4、理解信号量信号量/信号灯的本质是一把计数器用来描述临界资源中资源数量的多少。 ①申请计数器成功就表示我具有访问资源的权限了。 ②申请了计数器资源。我当前访问我要的资源了吗 没有。申请了计数器资源是对资源的预定机制。 ③计数器可以有效保证进入共享资源的执行流的数量。 ④所以每一个执行刘翔访问共享资源中的一部分的时候不是直接访问而是先申请计数器资源。这个计数器就叫做信号量。 ⑤我们把值只能为01两态的计数器叫做二元信号量本质就是一个锁。 ⑥让计数器为1资源为1的本质其实就是不将临界资源分成很多块了而是当做一个整体整体申请整体释放比如管道。 ⑦要访问临界资源需要申请信号量计数器资源。而信号量计数器也是共享资源。它要先保证自己的安全。 cnt--C语言一条语句变成汇编多条3汇编语句。 a.cnt变量的内容内存-CPU寄存器 b.cpu内进行--操作 c.将计算结果写回cnt变量的内存位置。 进程在运行的时候可以随时被切换,多执行流都访问这个变量时可能会出错。(线程部分详细解释 ⑧申请信号量本质是对计数器--P操作。 释放资源释放信号量本质是对计数器进行操作V操作。 申请和释放PV操作——原子的一件事情要么不做要做就做完两态的没有“正在做”这样的概念。用一条汇编语句即可实现。
总结信号量本质是一把计数器。对计数器匹配的操作叫pv操作---原子的。执行流申请资源必须先申请信号量资源得到信号量之后才能访问临界资源。信息量值10两态的二元信号量就是互斥功能。申请信号量的本质是对临界资源的预定机制。
2、接口
①申请信号量 ②控制信号量 ③设定信号量 注system v的接口是最难的多线程部分来进行操作说明。
3、信号量凭什么是进程间通信的一种 ①通信不仅仅是通信数据也在于互相协同。 ②要协同本质也是通信信号量首先被所有的通信进程看到。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/917934.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!