本周学习了第八章的主要内容主要包括两大部分分别为:
一、有名管道FIFO
1.有名管道说明
- 定义:有名管道的出现突破了只能用于具有亲缘关系的进程之间,这就大大地限制了管道的使用限制,它可以使互不相关的两个进程实现彼此通信。该管道可以通过路径名来指出, 并且在文件系统中是可见的。在建立了管道之后,两个进程就可以把它当作普通文件一样进行读写操作, 使用非常方便。
- 注意:FIFO 是严格地遵循先进先出规则的,对管道及 FIFO 的读总是从开始 处返回数据,对它们的写则把数据添加到末尾,它们不支持如 lseek()等文件定位操作。 有名管道的创建可以使用函数 mkfifo(),该函数类似文件中的 open()操作,可以指定管道的路径和打开的模式。
- 对阻塞打开和非阻 塞打开的读写进行讨论:
(1)对于读进程。
若该管道是阻塞打开,且当前 FIFO 内没有数据,则对读进程而言将一直阻塞到有数据写入。
若该管道是非阻塞打开,则不论 FIFO 内是否有数据,读进程都会立即执行读操作。即如果 FIFO 内没有数据,则读函数将立刻返回 0。
(2)对于写进程。
若该管道是阻塞打开,则写操作将一直阻塞到数据可以被写入。
若该管道是非阻塞打开而不能写入全部数据,则读操作进行部分写入或者调用失败。
2.mkfifo()函数格式
- mkfifo()函数语法要点
- FIFO 相关的出错信息
3.使用实例
- 以下是写管道的程序:
/* fifo_write.c */#include#include #include #include #include #include #include #define MYFIFO "/tmp/myfifo" /* 有名管道文件名*/#define MAX_BUFFER_SIZE PIPE_BUF /*定义在于 limits.h 中*/int main(int argc, char * argv[]) /*参数为即将写入的字符串*/{ int fd; char buff[MAX_BUFFER_SIZE]; int nwrite; if(argc <= 1) { printf("Usage: ./fifo_write string\n"); exit(1); } sscanf(argv[1], "%s", buff); /* 以只写阻塞方式打开 FIFO 管道 */ fd = open(MYFIFO, O_WRONLY); if (fd == -1) { printf("Open fifo file error\n"); exit(1); } /*向管道中写入字符串*/ if ((nwrite = write(fd, buff, MAX_BUFFER_SIZE)) > 0) { printf("Write '%s' to FIFO\n", buff); } close(fd); exit(0);}
- 以下是读管道程序:
#include#include #include #include #include #include #include #include #define MYFIFO "/tmp/myfifo" /* 有名管道文件名*/#define MAX_BUFFER_SIZE PIPE_BUF /*定义在于 limits.h 中*/ int main(){ char buff[MAX_BUFFER_SIZE]; int fd; int nread; /* 判断有名管道是否已存在,若尚未创建,则以相应的权限创建*/ if (access(MYFIFO, F_OK) == -1) { if ((mkfifo(MYFIFO, 0666) < 0) && (errno != EEXIST)) { printf("Cannot create fifo file\n"); exit(1); } } /* 以只读阻塞方式打开有名管道 */ fd = open(MYFIFO, O_RDONLY); if (fd == -1) { printf("Open fifo file error\n"); exit(1); } while (1) { memset(buff, 0, sizeof(buff)); if ((nread = read(fd, buff, MAX_BUFFER_SIZE)) > 0) { printf("Read '%s' from FIFO\n", buff); } } close(fd); exit(0);}
二、消息队列
1. 消息队列概述
- 顾名思义,消息队列就是一些消息的列表。用户可以从消息队列中添加消息和读取消息等。从这点上看, 消息队列具有一定的 FIFO 特性,但是它可以实现消息的随机查询,比 FIFO 具有更大的优势。同时,这些消息又是存在于内核中的,由“队列 ID”来标识。
2. 消息队列的应用
(1)函数说明
消息队列的实现包括创建或打开消息队列、添加消息、读取消息和控制消息队列这 4 种操作。其中创建 或打开消息队列使用的函数是 msgget(),这里创建的消息队列的数量会受到系统消息队列数量的限制; 添加消息使用的函数是 msgsnd()函数,它把消息添加到已打开的消息队列末尾;读取消息使用的函数是 msgrcv(),它把消息从消息队列中取走,与 FIFO 不同的是,这里可以指定取走某一条消息;最后控制消息队列使用的函数是 msgctl(),它可以完成多项功能。
(2)函数格式
- msgget()函数的语法要点。
- msgsnd()函数的语法要点。
3.使用实例
- 消息队列发送端的代码:
#include#include #include #include #include #include #include #define BUFFER_SIZE 512struct message{ long msg_type; char msg_text[BUFFER_SIZE];};int main(){ int qid; key_t key; struct message msg; /*根据不同的路径和关键字产生标准的 key*/ if ((key = ftok(".", 'a')) == -1) { perror("ftok"); exit(1); } /*创建消息队列*/ if ((qid = msgget(key, IPC_CREAT|0666)) == -1) { perror("msgget"); exit(1); } printf("Open queue %d\n",qid); while(1) { printf("Enter some message to the queue:"); if ((fgets(msg.msg_text, BUFFER_SIZE, stdin)) == NULL) { puts("no message"); exit(1); } msg.msg_type = getpid(); /*添加消息到消息队列*/ if ((msgsnd(qid, &msg, strlen(msg.msg_text), 0)) < 0) { perror("message posted"); exit(1); } if (strncmp(msg.msg_text, "quit", 4) == 0) { break; } } exit(0);}
- 以下是消息队列接收端
#include#include #include #include #include #include #include #define BUFFER_SIZE 512 struct message { long msg_type; char msg_text[BUFFER_SIZE]; }; int main() { int qid; key_t key; struct message msg; /*根据不同的路径和关键字产生标准的 key*/ if ((key = ftok(".", 'a')) == -1) { perror("ftok"); exit(1); } /*创建消息队列*/ if ((qid = msgget(key, IPC_CREAT|0666)) == -1) { perror("msgget"); exit(1); } printf("Open queue %d\n", qid); do { /*读取消息队列*/ memset(msg.msg_text, 0, BUFFER_SIZE); if (msgrcv(qid, (void*)&msg, BUFFER_SIZE, 0, 0) < 0) { perror("msgrcv"); exit(1); } printf("The message from process %d : %s", msg.msg_type, msg.msg_text); } while(strncmp(msg.msg_text, "quit", 4)); /*从系统内核中移走消息队列 */ if ((msgctl(qid, IPC_RMID, NULL)) < 0) { perror("msgctl"); exit(1); } exit(0);
server.c
#include#include #include #include #include #include #include #include #define PORT 4321#define BUFFER_SIZE 1024#define MAX_QUE_CONN_NM 5int main(){ struct sockaddr_in server_sockaddr,client_sockaddr; int sin_size,recvbytes; int sockfd, client_fd; char buf[BUFFER_SIZE];/*创建socket套接字接口*/ sockfd = socket(AF_INET,SOCK_STREAM,0);/*设置本地地址的相关参数*/ server_sockaddr.sin_family = AF_INET; server_sockaddr.sin_port = htons(PORT); server_sockaddr.sin_addr.s_addr = INADDR_ANY; bzero(&(server_sockaddr.sin_zero),8);/*允许重复使用本地地址与套接字进行绑定*/ int i = 1; setsockopt(sockfd, SOL_SOCKET,SO_REUSEADDR,&i,sizeof(i));/*绑定套接字和本地计算机*/ bind(sockfd,(struct sockaddr *)&server_sockaddr, sizeof(struct sockaddr));/*监听客户端连接*/ listen(sockfd, MAX_QUE_CONN_NM);/*接收客户端的连接请求*/ client_fd = accept(sockfd,(struct sockaddr *)&client_sockaddr,&sin_size);/*接收客户端的发送*/ recvbytes = recv(client_fd, buf, BUFFER_SIZE, 0);/*关闭窗口*/ close(sockfd); exit(0);}