机关门户网站建设意义,浙江网站建设流程,WordPress 代码修改,网站建设丿金手指专业一.管道#xff08;分为命名管道和匿名管道#xff09;
管道的特点#xff1a;
①无论是命名管道还是匿名管道#xff0c;写入管道的数据都存放在内存之中。
②管道是一种半双工的通信方式#xff08;半双工是指终端A能发信号给终端B#xff0c;终端B也能发信号给终端…一.管道分为命名管道和匿名管道
管道的特点
①无论是命名管道还是匿名管道写入管道的数据都存放在内存之中。
②管道是一种半双工的通信方式半双工是指终端A能发信号给终端B终端B也能发信号给终端A但这两个过程不能同时进行
③命名管道和匿名管道的区别命名管道可以在任意进程间使用匿名管道主要在父子进程间使用。
命名管道
int mkfifo( const char *filename, mode_t mode);//filename 是管道名 mode 是创建的文件访问权限 实践
打开两个独立的终端窗口分别运行两个进程。
//终端1 进程a.c 创建管道
#include stdio.h
#include stdlib.h
#include unistd.h
#include sys/types.h
#include sys/stat.h
#include fcntl.hint main(){const char *fifoPath ./myfifo;//./是指在当前目录下创建mode_t fifoMode 0600;//第一个0代表10进制第二个6代表用户有读写权限后面两个0代表组用户和其他用户无权限// 使用 mkfifo 创建命名管道if (mkfifo(fifoPath, fifoMode) -1) {printf(mkfifo error\n);exit(1);}printf(Named pipe created at %s\n, fifoPath);exit(0);
}
先运行终端1其运行结果如图 //终端2 进程b.c 读取数据
#include stdio.h
#includestdlib.h
#include fcntl.h
#include unistd.hint main()
{int fd open(./myfifo, O_RDONLY);char buffer[20];read(fd, buffer, sizeof(buffer));close(fd);printf(Received: %s\n, buffer);exit(0);
}
由于程序是以阻塞模式打开 命名管道并尝试读取数据。这意味着如果没有其他进程写入数据到该管道它会一直等待直到有数据可读为止。所以b.c运行结果如图 再次打开终端1并在其中写入如下程序
//终端1 进程c.c 写入数据
#include stdio.h
#include stdlib.h
#include unistd.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#includestring.hint main()
{int fd open(./myfifo, O_WRONLY);const char *message Hello, Process B!;write(fd, message, strlen(message) 1);close(fd);exit(0);
}
当运行c.c后终端2中b.c的运行结果如图 匿名管道
int pipe( int fds[2]);//pipe()成功返回 0失败返回-1fds[0]是管道读端的描述符fds[1]是管道写端的描述符。
实践
用fork()创建子进程父进程写入数据子进程读取并输出数据。
#include stdio.h
#include stdlib.h
#include unistd.h
#includestring.h
#include sys/types.hint main()
{int fd[2];char buffer[100];int res pipe(fd); // 创建管道pid_t pid fork(); // 创建子进程if (res -1) {printf(pipe failed\n);exit(1);}if (pid -1) {printf(fork failed\n);exit(1);}if (pid 0) // 子进程{//sleep(1);close(fd[1]); // 关闭写入端read(fd[0], buffer, sizeof(buffer));//fflush(stdout);printf(Child received: %s\n, buffer);} else // 父进程{ close(fd[0]); // 关闭读取端const char *message Hello, Child!;write(fd[1], message, strlen(message) 1);}close(fd[0]);close(fd[1]);exit(0);
}
二.信号
kill()
int kill(pid_t pid, int sig);//用于向指定的进程发送信号。
//pid是要发送信号的目标进程的进程编号。
//sig是要发送的信号的编号。
实践
打开两个独立的终端窗口分别运行两个进程
//终端1 send.cpp#include signal.h
#include stdio.h
#include stdlib.h
#include unistd.hint main()
{pid_t receiver_pid; // 需要知道接收方进程的pid// 获取接收方进程的PIDprintf(输入接收进程的pid: );scanf(%d, receiver_pid);// 发送自定义信号到接收方进程if (kill(receiver_pid, SIGUSR1) 0) {printf( 发送给进程号为:%d的进程\n, receiver_pid);} else {perror(信号量发送失败);}return 0;
}//终端2 recv.cpp#include signal.h
#include stdio.h
#include stdlib.h
#include unistd.h// 信号处理函数
void signal_handler(int signum)
{if (signum SIGUSR1) {printf(自定义信号量收到\n);}
}int main()
{// 约定信号signal(SIGUSR1, signal_handler);printf(接收进程的pid为: %d\n, getpid());while (1) {// 等待信号的到来sleep(1);}return 0;
}运行结果 三.信号量
信号量是一个特殊的变量一般取正数值。它的值代表允许访问的资源数目。
p操作--原子减一--代表获取资源--可能阻塞当信号量值为0即无资源可用时会阻塞
v操作--原子加一--代表释放资源--不会阻塞
semget()
int semget(key_t key, int nsems, int semflg);//创建一个新信号量或取得一个已有信号量的键。
//key算是一个标识符拥有相同key值的进程可以进行通讯
//nesms通常取值为1
//semflg通常是IPC_CREAT不过使用IPC_CREAT时要加权限如0666
//成功返回一个正数失败返回-1
semctl()
int semctl(int semid, int semnum, int cmd, ... );//控制信号量信息。
//semid由semget()返回
//semnum一般取0
//cmd用于指定要进行的操作如GETVAL(获取信号量的值) 、SETVAL(设置信号量的值) IPC_RMID(删除信号量
//...也许会用到这个参数它是union semun类型的参数用于传递额外信息
union semun
{int val; //用于设置或获取单个信号量的值struct semid_ds *buf; //用于传递IPC_STAT或IPC_SET命令的参数unsigned short *array;//用于传递GETALL或SETALL命令的参数
};
实践
用信号量和两个进程来模拟打印机进程A输出a代表开始使用再次输出a代表结束使用进程B输出b代表开始使用再次输出b代表结束使用所以预期结果输出为aabbaabb......
/*sem.h文件*///封装P、V操作
#includeiostream
#includeunistd.h
#includestring.h
#includesys/sem.husing namespace std;union semnum
{int val;struct semid_ds *buf;unsigned short *array;
};void sem_init(); //创建信号量
void sem_p(); //p操作
void sem_v(); //v操作
void sem_destroy();//销毁信号量
/*sem.cpp文件*/#includesem.hstatic int semid -1;//初始化信号量void sem_init()
{semid semget((key_t)1234, 1, IPC_CREAT | 0600);union semnum a;a.val 1;//信号量的初始值if(semctl(semid,0,SETVAL,a)-1){cout semctl failed endl;}
}void sem_p()
{struct sembuf buf;// 信号量的标志通常使用 IPC_NOWAIT 或 SEM_UNDObuf.sem_flg SEM_UNDO;// 信号量的编号如果有多个信号量可以使用不同的编号进行区分buf.sem_num 0;buf.sem_op -1;//p操作if(semop(semid,buf,1)-1){cout semop falied endl;}
}void sem_v()
{struct sembuf buf;buf.sem_flg SEM_UNDO;buf.sem_num 0;buf.sem_op 1;//v操作if(semop(semid,buf,1)-1){cout semop falied endl;}
}void sem_destroy()
{if(semctl(semid,0,IPC_RMID)-1){cout semctl move failed endl;}
}
/*a.cpp文件*/#includesem.hint main()
{sem_init();for (int i 0; i 5;i){sem_p();cout a;fflush(stdout);sleep(5);cout a;fflush(stdout);sem_v();sleep(5);}sleep(10);//等待另一个进程完成sem_destroy();return 0;
}
/*b.cpp文件*/#includesem.hint main()
{sem_init();for (int i 0; i 5;i){sem_p();cout b;fflush(stdout);sleep(5);cout b;fflush(stdout);sem_v();sleep(5);}return 0;
} 另外两个重要的概念
临界资源同一时刻只允许被一个进程或线程访问的资源临界区访问临界资源的代码段
四.共享内存
共享内存是先在物理内存上申请一块空间多个进程可以将其映射到自己的虚拟地址空间中。这是是一种用于在不同进程之间共享数据的机制它允许多个进程访问同一块内存区域从而实现高效的进程间通信。
shmget()
int shmget(key_t key, size_t size, int shmflg); //创建或获取共享内存段并返回一个共享内存标识符,通常称为 shmid。
//key共享内存标识键值
//size指定要创建的共享内存段的大小
//shmflg用于指定创建共享内存的权限和行为,通常是IPC_CREAT表示如果指定的 IPC 资源不存在则创建它如果已经存在则忽略或者IPC_EXCL表示只创建 IPC 资源如果资源已经存在创建操作将失败
shmat()
void *shmat(int shmid, const void *shmaddr, int shmflg);//用于将共享内存附加到进程的地址空间以便进程可以访问共享内存中的数据。
//shmid是由 shmget 函数创建的共享内存的标识符
//shmaddr通常设置为 NULL
//shmflg通常设置为0
shmdt()
int shmdt(const void *shmaddr);//用于将共享内存从进程的地址空间中分离使得该进程不能再访问共享内存中的数据。
//shmaddr是共享内存的地址通常是在使用 shmat 函数时获得的指针。
shmctl()
int shmctl(int shmid, int cmd, struct shmid_ds *buf);//用于获取关于共享内存段的信息、修改权限、删除共享内存段等。
//shmid使用 shmget 函数获得。
//cmd指定要执行的操作例如获取信息IPC_STAT、修改权限IPC_SET、删除共享内存段IPC_RMID等。
//buf用于传递或接收有关共享内存段的信息。如果不需要传递或接收信息可以将其设置为 NULL。
实践
write进程和read进程通过共享内存分别来写入数据和读取数据。
/*shm.h文件*///封装P、V操作
#includeiostream
#includeunistd.h
#includestring.h
#includesys/sem.husing namespace std;//创建两个信号量
enum INDEX
{SEM10,SEM2
};union semnum
{int val;struct semid_ds *buf;unsigned short *array;
};void sem_init(); //创建信号量
void sem_p(enum INDEX i); //p操作
void sem_v(enum INDEX i); //v操作
void sem_destroy();//销毁信号量
/*shm.cpp文件*/
#include shm.hstatic int semid -1; // 初始化信号量void sem_init()
{semid semget((key_t)1234, 2, IPC_CREAT | 0600); // 创建2个信号量int arr[2] {1, 0};union semnum a;for (int i 0; i 2; i){a.val arr[i]; // 信号量的初始值if (semctl(semid, i, SETVAL, a) -1){cout semctl failed endl;}}
}void sem_p(enum INDEX i)
{struct sembuf buf;buf.sem_flg SEM_UNDO;buf.sem_num i;buf.sem_op -1; // p操作if (semop(semid, buf, 1) -1){cout semop falied endl;}
}void sem_v(enum INDEX i)
{struct sembuf buf;buf.sem_flg SEM_UNDO;buf.sem_num i;buf.sem_op 1; // v操作if (semop(semid, buf, 1) -1){cout semop falied endl;}
}void sem_destroy()
{if (semctl(semid, 0, IPC_RMID) -1)//销毁所有信号量{cout semctl move failed endl;}
}
/*write.cpp文件*/
#includeshm.h
#include sys/shm.hint main()
{// 创建共享内存int shmid shmget((key_t)1234, 128, IPC_CREAT | 0600);if (shmid -1){cout write shget filed endl;exit(1);}// 将共享内存映射到当前的地址空间char *s (char *)shmat(shmid, NULL, 0);if (s (char *)-1){cout write shmat failed endl;exit(1);}sem_init();// 往共享内存中写入数据while (1){cout 请输入内容 endl;char buff[128] {0};cin buff;sem_p(SEM1);strcpy(s, buff);sem_v(SEM2);if (strncmp(buff, end, 3) 0){break;}}//分离共享内存shmdt(s);return 0;
}
/*read.cpp文件*/
#includeshm.h
#include sys/shm.hint main()
{// 创建共享内存int shmid shmget((key_t)1234, 128, IPC_CREAT | 0600);if (shmid -1){cout read shget filed endl;exit(1);}// 将共享内存映射到当前的地址空间char *s (char *)shmat(shmid, NULL, 0);if (s (char *)-1){cout read shmat failed endl;exit(1);}sem_init();// 从共享内存中读取数据while (1){sem_p(SEM2);if (strncmp(s, end, 3) 0){break;}cout s endl;sem_v(SEM1);}//分离共享内存shmdt(s);//销毁共享内存shmctl(shmid, IPC_RMID, NULL);//销毁信号量sem_destroy();return 0;
} 运行结果 五.消息队列
msgget()
int msgget(key_t key, int msgflg);//创建消息队列
//key键值
//msgflg用于指定消息队列的创建方式和权限
msgctl()
int msgctl(int msqid, int cmd, struct msqid_ds *buf);//控制消息队列 //msqid消息队列的标识符由msgget()返回
//cmd操作命令
//*buf消息队列信息的结构体指针
msgrcv()
int msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);//从消息队列中接收消息
//msgid消息队列的标识符由msgget()返回
//*msgp消息缓冲区的指针数据将被复制到这个缓冲区
//msgsz缓冲区的大小以字节为单位
//msgtyp接收消息的类型如果设为0则不区分类型
//msgflg控制信息接收的行为
msgsnd()
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);//用于向消息队列中发送信息
//msgid消息队列的标识符由msgget()返回
//*msgp消息缓冲区的指针其中包含要发送的数据
//msgsz缓冲区的大小以字节为单位
//msgflg控制信息接收的行为
/*msga.cpp文件*/
#includeiostream
#includeunistd.h
#includestring.h
#includesys/msg.husing namespace std;// 定义消息结构
struct message
{long type;//必须为长整形char buff[32];
};int main()
{int msgid msgget((key_t)1234, IPC_CREAT | 0600);if(msgid-1){cout msgget failed endl;exit(1);}struct message dt;dt.type 1;//消息类型为1strcpy(dt.buff, hello);msgsnd(msgid, dt, 32, 0);return 0;
}
/*msgb.cpp文件*/
#includeiostream
#includeunistd.h
#includestring.h
#includesys/msg.husing namespace std;// 定义消息结构
struct message
{long type;//必须为长整形char buff[32];
};int main()
{int msgid msgget((key_t)1234, IPC_CREAT | 0600);if(msgid-1){cout msgb.cpp msgget failed endl;exit(0);}struct message dt;msgrcv(msgid, dt, 32, 1, 0);cout 接收到内容为 dt.buff 的消息 endl;return 0;
}
运行结果 入上图运行了2此msga.cpp消息队列中有2条消息当运行msgb.cpp之后消息队列剩余消息如下 六.socket
此部分详见文章用c语言编写简单的一对一服务器和多对一服务器。