clients(PV操作共享内核内存进行输入输出分屏) - server(进程间通信)模型实现

1、拓扑结构

2、PV操作共享内核内存进行输入输出分屏

(1)

1 int semop(int semid,struct sembuf *sops,size_t nsops);

功能描述

操作一个或一组信号。

semid:

信号集的识别码,可通过semget获取。

sops:

指向存储信号操作结构的数组指针,信号操作结构的原型如下

1 struct sembuf
2 {
3 unsigned short sem_num; /* semaphore number */
4 short sem_op; /* semaphore operation */
5 short sem_flg; /* operation flags */
6 };

这三个字段的意义分别为:

sem_num:

操作信号在信号集中的编号,第一个信号的编号是0。

sem_op:

如果其值为正数,该值会加到现有的信号内含值中。通常用于释放所控资源的使用权;如果sem_op的值为负数,而其绝对值又大于信号的现值,操作将会阻塞,直到信号值大于或等于sem_op的绝对值。通常用于获取资源的使用权;如果sem_op的值为0,则操作将暂时阻塞,直到信号的值变为0。

sem_flg:

信号操作标志,可能的选择有两种

IPC_NOWAIT //对信号的操作不能满足时,semop()不会阻塞,并立即返回,同时设定错误提示。

SEM_UNDO //程序结束时(不论正常或不正常),保证信号值会被重设为semop()调用前的值。这样做的目的在于避免程序在异常情况下结束时未将锁定的资源解锁,造成该资源永远锁定。

nsops:信号操作结构的数量,恒大于或等于1。

 1 void P(int semid)
 2 {
 3     struct sembuf my_buf ;
 4     memset(&my_buf, 0, sizeof(my_buf) );
 5     my_buf.sem_num = 0 ;
 6     my_buf.sem_op = -1 ;
 7     my_buf.sem_flg = SEM_UNDO ;
 8     semop(semid, &my_buf, 1);
 9 }
10 void V(int semid)
11 {
12     struct sembuf my_buf ;
13     memset(&my_buf, 0, sizeof(my_buf) );
14     my_buf.sem_num = 0 ;
15     my_buf.sem_op = 1 ;
16     my_buf.sem_flg = SEM_UNDO ;
17     semop(semid, &my_buf, 1);
18
19 }

(2)


shmget(得到一个共享内存标识符或创建一个共享内存对象)

所需头文件

#include <sys/ipc.h>

#include <sys/shm.h>


函数说明

得到一个共享内存标识符或创建一个共享内存对象并返回共享内存标识符

函数原型

int shmget(key_t key, size_t size, int shmflg)

函数传入值

key

0(IPC_PRIVATE):会建立新共享内存对象

大于0的32位整数:视参数shmflg来确定操作。通常要求此值来源于ftok返回的IPC键值

size

大于0的整数:新建的共享内存大小,以字节为单位

0:只获取共享内存时指定为0

shmflg

0:取共享内存标识符,若不存在则函数会报错

IPC_CREAT:当shmflg&IPC_CREAT为真时,如果内核中不存在键值与key相等的共享内存,则新建一个共享内存;如果存在这样的共享内存,返回此共享内存的标识符

IPC_CREAT|IPC_EXCL:如果内核中不存在键值 与key相等的共享内存,则新建一个共享内存;如果存在这样的共享内存则报错

函数返回值

成功:返回共享内存的标识符

出错:-1,错误原因存于error中

(3)

int semget(key_t key, int nsems, int semflg);

key:

所创建或打开信号量集的键值。

nsems:

创建的信号量集中的信号量的个数,该参数只在创建信号量集时有效。

semflg:

调用函数的操作类型,也可用于设置信号量集的访问权限,两者通过or表示

1     shm_key = (key_t)atoi(argv[1]);
2     sem_key = (key_t)atoi(argv[2]);
3
4     my_shm = shmget(shm_key, sizeof(MBUF), 0666|IPC_CREAT);
5
6     my_sem = semget(sem_key, 1, 0666 | IPC_CREAT);

(4)

int semctl(int semid,int semnum,int cmd, /*union semun arg*/);

SETVAL设置信号量集中的一个单独的信号量的值。

1 semctl(my_sem, 0, SETVAL, 1);

(5)


shmat(把共享内存区对象映射到调用进程的地址空间)

所需头文件

#include <sys/types.h>

#include <sys/shm.h>


函数说明

连接共享内存标识符为shmid的共享内存,连接成功后把共享内存区对象映射到调用进程的地址空间,随后可像本地空间一样访问

函数原型

void *shmat(int shmid, const void *shmaddr, int shmflg)

函数传入值
shmid
共享内存标识符

shmaddr

指定共享内存出现在进程内存地址的什么位置,直接指定为NULL让内核自己决定一个合适的地址位置

shmflg

SHM_RDONLY:为只读模式,其他为读写模式

函数返回值

成功:附加好的共享内存地址

出错:-1,错误原因存于error中
1 p = (pMBUF)shmat(my_shm, NULL, 0);

(6)

client_in PV操作:

1 while( P(my_sem), p -> m_flag == 1)
2         {
3             V(my_sem);
4             sleep(1);
5         }
6         strcpy(p ->m_buf, line);
7         p ->m_flag = 1 ;
8         V(my_sem);

(7)


shmdt(断开共享内存连接)

所需头文件

#include <sys/types.h>

#include <sys/shm.h>


函数说明

与shmat函数相反,是用来断开与共享内存附加点的地址,禁止本进程访问此片共享内存

函数原型

int shmdt(const void *shmaddr)

函数传入值

shmaddr:连接的共享内存的起始地址

函数返回值

成功:0

出错:-1,错误原因存于error中
1 shmdt(p);

(8)

删除内存 删除 信号量


shmctl(共享内存管理)

所需头文件

#include <sys/types.h>

#include <sys/shm.h>


函数说明

完成对共享内存的控制

函数原型

int shmctl(int shmid, int cmd, struct shmid_ds *buf)

函数传入值

shmid

共享内存标识符

cmd

IPC_STAT:得到共享内存的状态,把共享内存的shmid_ds结构复制到buf中

IPC_SET:改变共享内存的状态,把buf所指的shmid_ds结构中的uid、gid、mode复制到共享内存的shmid_ds结构内

IPC_RMID:删除这片共享内存

buf

共享内存管理结构体。具体说明参见共享内存内核结构定义部分

函数返回值

成功:0

出错:-1,错误原因存于error中
1 shmctl(my_shm, IPC_RMID, NULL) ;
2 semctl(my_sem, 0, IPC_RMID);//0为信号量编号

3、clients - server 通信

(1)上下线信息管道

server:

 1 char file_name[128]  = "";
 2     char client_r[128], client_w[128];
 3     char line[32] ;
 4     int fd_read ;
 5     int client_id ;
 6     int fd_cr , fd_cw ;
 7     FILE* fp ;
 8     sprintf(file_name, "%s/%s", PATH, NAME);
 9     mkfifo(file_name, 0666) ;
10     fd_read = open(file_name, O_RDONLY);
11     open(file_name, O_WRONLY);
12
13     fp = fdopen(fd_read, "r");

client_in:

1 char server_name[128]= "" ;
2     char read_file[128], write_file[128] ;
3     char msg[32] ="" ;
4     int fd_r, fd_w ;
5     sprintf(server_name, "%s/%s", PATH, SERVER);
6     int fd_server = open(server_name, O_WRONLY);
7     sprintf(msg, "%d\n", getpid());
8     write(fd_server, msg, strlen(msg));

(2) 读写管道

client_in:

 1 memset(read_file, 0, 128);
 2     memset(write_file, 0, 128);
 3
 4     sprintf(read_file, "%s/%d_r.fifo", PATH, getpid());
 5     sprintf(write_file, "%s/%d_w.fifo", PATH, getpid());
 6
 7     mkfifo(read_file, 0666);
 8     mkfifo(write_file, 0666);
 9
10     fd_r = open(read_file, O_RDONLY);
11     fd_w = open(write_file, O_WRONLY);

server:

while(memset(line, 0 , 32), fgets(line, 32, fp) != NULL)// "pid\n"
    {// cr  cw pid_r.fifo  pid_w.fifo
        sscanf(line, "%d", &client_id);
        printf("client: %d request !\n", client_id) ;
        memset(client_r, 0, 128);
        memset(client_w, 0, 128);

        sprintf(client_r, "%s/%d_r.fifo", PATH, client_id);

        sprintf(client_w, "%s/%d_w.fifo", PATH, client_id);

        fd_cw = open(client_r, O_WRONLY);
        fd_cr = open(client_w, O_RDONLY);
        /*
          ****************
                */
        close(fd_cr);
        close(fd_cw);
    }
        

(3)信息传递

client_in:

 1 while(memset(line, 0, 1024), fgets(line, 1024, stdin) != NULL)
 2 {
 3 write(fd_w, line, strlen(line));
 4
 5 memset(line, 0, 1024);
 6
 7 read(fd_r, line, 1024);
 8 /**
 9 ************
10 */
11
12 }

server:

1 write(fd_wr, buf, strlen(buf));

4、server fork 子进程对客户端传来的信息进行处理

(1)fork

1 if(fork() == 0)
2         {
3             child_main(fd_cr, fd_cw);
4             close(fd_cr);
5             close(fd_cw);
6             exit(1);
7         }

(2)reserve 函数

 1 void reverse(char* str)
 2 {
 3     int bg, end ;
 4     char tmp ;
 5     bg = 0 ;
 6     end = strlen(str) - 1 ;
 7     while(bg < end)
 8     {
 9         tmp = str[bg] ;
10         str[bg] = str[end] ;
11         str[end] = tmp ;
12         bg ++ ;
13         end -- ;
14     }
15
16 }

(3) child_main  read and write

 1 void child_main(int fd_rd, int fd_wr)
 2 {
 3     char buf[1024] ;
 4     while(memset(buf, 0, 1024), read(fd_rd, buf, 1024) != 0)
 5     {
 6             reverse(buf);
 7             write(fd_wr, buf, strlen(buf));
 8     }
 9
10 }

(4)防止僵尸进程

  如果子进程先退出,系统不会自动清理掉子进程的环境,而必须由父进程调用wait或waitpid函数来完成清理工作,如果父进程不做清理工作,则已经退出的子进程将成为僵尸进程(defunct)。

1 signal(17, child_handle);

下次收到17号信号(子进程退出信号) 就 调用 child_handle 函数

1 void child_handle(int sig_num)
2 {
3     printf("a child exit!\n");
4     wait(NULL);
5 }

5、详细代码:

server:

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<string.h>
 4 #include<unistd.h>
 5 #include<sys/stat.h>
 6 #include<sys/types.h>
 7 #include<fcntl.h>
 8 #include<signal.h>
 9 #define PATH "/home/comst/pipe"
10 #define NAME "server.fifo"
11
12 void child_handle(int sig_num)
13 {
14     printf("a child exit!\n");
15     wait(NULL);
16 }
17 void reverse(char* str)
18 {
19     int bg, end ;
20     char tmp ;
21     bg = 0 ;
22     end = strlen(str) - 1 ;
23     while(bg < end)
24     {
25         tmp = str[bg] ;
26         str[bg] = str[end] ;
27         str[end] = tmp ;
28         bg ++ ;
29         end -- ;
30     }
31
32 }
33 void child_main(int fd_rd, int fd_wr)
34 {
35     char buf[1024] ;
36     while(memset(buf, 0, 1024), read(fd_rd, buf, 1024) != 0)
37     {
38             reverse(buf);
39             write(fd_wr, buf, strlen(buf));
40     }
41
42 }
43 int main(int argc, char* argv[])
44 {
45     signal(17, child_handle);
46     char file_name[128]  = "";
47     char client_r[128], client_w[128];
48     char line[32] ;
49     int fd_read ;
50     int client_id ;
51     int fd_cr , fd_cw ;
52     FILE* fp ;
53     sprintf(file_name, "%s/%s", PATH, NAME);
54     mkfifo(file_name, 0666) ;
55     fd_read = open(file_name, O_RDONLY);
56     open(file_name, O_WRONLY);
57
58     fp = fdopen(fd_read, "r");
59
60     while(memset(line, 0 , 32), fgets(line, 32, fp) != NULL)// "pid\n"
61     {// cr  cw pid_r.fifo  pid_w.fifo
62         sscanf(line, "%d", &client_id);
63         printf("client: %d request !\n", client_id) ;
64         memset(client_r, 0, 128);
65         memset(client_w, 0, 128);
66
67         sprintf(client_r, "%s/%d_r.fifo", PATH, client_id);
68
69         sprintf(client_w, "%s/%d_w.fifo", PATH, client_id);
70
71         fd_cw = open(client_r, O_WRONLY);
72         fd_cr = open(client_w, O_RDONLY);
73         if(fork() == 0)
74         {
75             child_main(fd_cr, fd_cw);
76             close(fd_cr);
77             close(fd_cw);
78             exit(1);
79         }
80         close(fd_cr);
81         close(fd_cw);
82     }
83
84
85
86
87     memset(file_name, 0, 128);
88     sprintf(file_name, "%s/%s", PATH, NAME);
89     unlink(file_name);
90     return 0 ;
91 }

client.h:

 1 #ifndef __CLINET_H__
 2 #define __CLINET_H__
 3
 4 #include<stdio.h>
 5 #include<stdlib.h>
 6 #include<string.h>
 7 #include<unistd.h>
 8 #include<sys/types.h>
 9 #include<sys/ipc.h>
10 #include<sys/shm.h>
11 #include<sys/sem.h>
12 typedef struct tag
13 {
14     int m_flag ;
15     char m_buf[1024] ;
16 }MBUF, *pMBUF;
17 void P(int semid) ;
18 void V(int semid);
19 #endif

client_in:

 1 #include "client.h"
 2 #include<sys/stat.h>
 3 #include<sys/types.h>
 4 #include<sys/fcntl.h>
 5 #define PATH "/home/comst/pipe"
 6 #define SERVER "server.fifo"
 7 int main(int argc, char* argv[])//shm_key sem_key
 8 {
 9
10     char server_name[128]= "" ;
11     char read_file[128], write_file[128] ;
12     char msg[32] ="" ;
13     int fd_r, fd_w ;
14     sprintf(server_name, "%s/%s", PATH, SERVER);
15     int fd_server = open(server_name, O_WRONLY);
16     sprintf(msg, "%d\n", getpid());
17     write(fd_server, msg, strlen(msg));
18
19     memset(read_file, 0, 128);
20     memset(write_file, 0, 128);
21
22     sprintf(read_file, "%s/%d_r.fifo", PATH, getpid());
23     sprintf(write_file, "%s/%d_w.fifo", PATH, getpid());
24
25     mkfifo(read_file, 0666);
26     mkfifo(write_file, 0666);
27
28
29
30
31     fd_r = open(read_file, O_RDONLY);
32     fd_w = open(write_file, O_WRONLY);
33
34
35     key_t shm_key, sem_key ;
36     int my_shm, my_sem ;
37
38     char line[1024] ;
39
40     pMBUF p ;
41     shm_key = (key_t)atoi(argv[1]);
42     sem_key = (key_t)atoi(argv[2]);
43
44     my_shm = shmget(shm_key, sizeof(MBUF), 0666|IPC_CREAT);
45
46     my_sem = semget(sem_key, 1, 0666 | IPC_CREAT);
47     semctl(my_sem, 0, SETVAL, 1);
48
49
50     p = (pMBUF)shmat(my_shm, NULL, 0);
51     memset(p, 0, sizeof(MBUF));
52
53     while(memset(line, 0, 1024), fgets(line, 1024, stdin) != NULL)
54     {
55         write(fd_w, line, strlen(line));
56
57         memset(line, 0, 1024);
58
59         read(fd_r, line, 1024);
60         while( P(my_sem), p -> m_flag == 1)
61         {
62             V(my_sem);
63             sleep(1);
64         }
65         strcpy(p ->m_buf, line);
66         p ->m_flag = 1 ;
67         V(my_sem);
68
69     }
70
71     while( P(my_sem), p -> m_flag == 1)
72     {
73         V(my_sem);
74         sleep(1);
75     }
76     strcpy(p ->m_buf, "over");
77     p ->m_flag = 1 ;
78     V(my_sem);
79
80     sleep(3);
81
82
83
84
85     shmdt(p);
86     shmctl(my_shm, IPC_RMID, NULL) ;
87
88     semctl(my_sem, 0, IPC_RMID);
89
90
91
92 }

client_out:

 1 #include "client.h"
 2 int main(int argc, char* argv[])//shm_key sem_key
 3 {
 4     key_t shm_key, sem_key ;
 5     int my_shm, my_sem ;
 6
 7     char line[1024] ;
 8
 9     pMBUF p ;
10     shm_key = atoi(argv[1]);
11     sem_key = atoi(argv[2]);
12
13     my_shm = shmget(shm_key, sizeof(MBUF), 0666);
14
15     my_sem = semget(sem_key, 1, 0666 );
16     semctl(my_sem, 0, SETVAL, 1);
17
18
19     p = (pMBUF)shmat(my_shm, NULL, 0);
20     memset(p, 0, sizeof(MBUF));
21
22     while(1)
23     {
24         while(P(my_sem), p -> m_flag == 0)
25         {
26             V(my_sem);
27             sleep(1);
28         }
29         printf("%d :  %s\n", getpid(), p -> m_buf);
30         if(strcmp(p ->m_buf, "over") == 0)
31         {
32
33             V(my_sem);
34             break ;
35         }
36         p -> m_flag = 0 ;
37         V(my_sem);
38
39     }
40
41
42
43
44
45     shmdt(p);
46
47
48
49
50 }

P_V func

 1 #include "client.h"
 2 void P(int semid)
 3 {
 4     struct sembuf my_buf ;
 5     memset(&my_buf, 0, sizeof(my_buf) );
 6     my_buf.sem_num = 0 ;
 7     my_buf.sem_op = -1 ;
 8     my_buf.sem_flg = SEM_UNDO ;
 9     semop(semid, &my_buf, 1);
10 }
11 void V(int semid)
12 {
13     struct sembuf my_buf ;
14     memset(&my_buf, 0, sizeof(my_buf) );
15     my_buf.sem_num = 0 ;
16     my_buf.sem_op = 1 ;
17     my_buf.sem_flg = SEM_UNDO ;
18     semop(semid, &my_buf, 1);
19
20 }
时间: 2024-10-12 14:09:02

clients(PV操作共享内核内存进行输入输出分屏) - server(进程间通信)模型实现的相关文章

共享内存和操作共享内存几个函数的用法

简介:共享内存是进程间通信中最简单的方式之一.共享内存允许两个或更多进程访问同一块内存,就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针.当一个进程改变了这块地址中的内容的时候,其它进程都会察觉到这个更改. 在创建共享内存和操作共享内存的时候被下面这些函数的参数弄糊涂了, 遂写下各个函数的说明. 所需头文件 #include<sys/ipc.h> #include<sys/shm.h> 函数 (1)shmget(key_t key,int size,int

Linux内核——内存管理

内存管理 页 内核把物理页作为内存管理的基本单位:内存管理单元(MMU,管理内存并把虚拟地址转换为物理地址)通常以页为单位进行处理.MMU以页大小为单位来管理系统中的页表.从虚拟内存的角度看,页就是最小单位. 32位系统:页大小4KB 64位系统:页大小8KB 在支持4KB页大小并有1GB物理内存的机器上,物理内存会被划分为262144个页.内核用 struct page 结构表示系统中的每个物理页. struct page { page_flags_t flags;   /* 表示页的状态,每

P-V操作

1.P-V操作原理 P操作和V操作是不可中断的程序段,称为原语. P原语操作的动作 (1).sem减1: (2).若sem减1后仍大于或等于0,则进程继续执行: (3).若sem减1后小于0,则该进程被阻塞后进入与该信号相对应的队列中,然后转进程调度. V原语操作的动作 (1).sem加1: (2).若相加结果大于0,则进程继续执行: (3).若相加结果小于或等于0,则从该信号的等待队列中唤醒一等待进程,然后转进程调度. 注意:P.V操作对于每一个进程来说,都只能进行一次,而且必须是成对使用,且

LINUX内核内存屏障

================= ================= By: David Howells <[email protected]> Paul E. McKenney <[email protected]> 译: kouu <[email protected]> 出处: Linux内核文档 -- Documentation/memory-barriers.txt 文件夹: (*) 内存訪问抽象模型. - 操作设备. - 保证. (*) 什么是内存屏障? -

Java使用wait() notify()方法操作共享资源详解_java - JAVA

文章来源:嗨学网 敏而好学论坛www.piaodoo.com 欢迎大家相互学习 Java多个线程共享资源: 1)wait().notify()和notifyAll()方法是本地方法,并且为final方法,无法被重写. 2)调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的monitor(即锁,或者叫管程) 3)调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程: 4

软考征程之Pv操作

一.概念 1.PV操作的含义 PV操作由P操作原语和V操作原语组成(原语是不可中断的过程),对信号量进行操作,具体定义如下: P(S):①将信号量S的值减1,即S=S-1: ②如果S30,则该进程继续执行:否则该进程置为等待状态,排入等待队列. V(S):①将信号量S的值加1,即S=S+1: ②如果S>0,则该进程继续执行:否则释放队列中第一个等待信号量的进程. PV操作的意义:我们用信号量及PV操作来实现进程的同步和互斥.PV操作属于进程的低级通信. 2.进程的同步和互斥 同步:指在并发进程之

linux内核内存分配(三、虚拟内存管理)

在分析虚拟内存管理前要先看下linux内核内存的详细分配我开始就是困在这个地方,对内核内存的分类不是很清晰.我摘录其中的一段: 内核内存地址 =========================================================================================================== 在linux的内存管理中,用户使用0-3GB的地址空间,而内核只是用了3GB-4GB区间的地址空间,共1GB:非连 续空间的物理映射就位于3G

linux内核内存分配(一、基本概念)

内存分配是linux比较复杂也是比较重要的部分,这个和ssd驱动很类似:物理地址和虚拟地址的映射关系.下面总结下最近看到的有关内存分配的内容和自己的理解: 1.一致内存访问和非一致内存访问 上图来自<深入linux设备驱动程序内核机制> 简单的说明下,UMA(一致内存访问 uniform memory access)可以很好的看到所有cpu访问内存的距离都是一样的(其实就是通过总线到内存的速度和距离都是一样的)所以就叫一致内存访问: 很显然右边的NUMA就是非一致内存访问,内存节点0是CPU0

PV操作——软考探究(五)

之前总结了和资源有关的银行家算法,随着学习的深入,对于资源的理解也有了更深的理解,此篇文章通过解决同步.异步问题的典型机制--PV操作来加深对资源的理解. P操作:申请一个资源.它是执行操作的前提,只有有了资源才可以执行操作.就和现实生活是一样的,只有有了资源才能够进行生产. V操作:释放一个资源.在执行完毕一个操作以后要将占用的资源释放掉,和银行家算法吻合, 同时发出信号. [例]两个相关联的进程A和B,他们共享一个缓冲器.进程A不断地读入数据,并送入缓冲器:进程B不断地从缓冲器中取出数据并加