您的位置:首页 > 运维架构 > Linux

Linux--进程间通信(信号量,共享内存)(转)

2014-11-01 14:59 381 查看
一. 信号量  

l信号量: 解决进程之间的同步与互斥的IPC机制

多个进程同时运行,之间存在关联
  •同步关系
  •互斥关系
互斥与同步关系存在的根源在于临界资源
  •临界资源是在同一个时刻只允许有限个(通常只有一个)进程可以访问(读)或修改(写)的资源
    –硬件资源(处理器、内存、存储器以及其他外围设备等)
    –软件资源(共享代码段,共享结构和变量等)
  •临界区,临界区本身也会成为临界资源

一个称为信号量的变量
  •信号量对应于某一种资源,取一个非负的整型值
  •信号量值指的是当前可用的该资源的数量,若它等于0则意味着目前没有可用的资源
在该信号量下等待资源的进程等待队列
对信号量进行的两个原子操作(PV操作)
  •P操作
  •V操作

最简单的信号量是只能取0 和1 两种值,叫做二维信号量

编程步骤:

  创建信号量或获得在系统已存在的信号量
    •调用semget()函数
    •不同进程使用同一个信号量键值来获得同一个信号量
  初始化信号量
    •使用semctl()函数的SETVAL操作
    •当使用二维信号量时,通常将信号量初始化为1
  进行信号量的PV操作
    •调用semop()函数
    •实现进程之间的同步和互斥的核心部分
  如果不需要信号量,则从系统中删除它
    •使用semclt()函数的IPC_RMID操作
    •在程序中不应该出现对已被删除的信号量的操作









eg. 通过对信号量PV操作,消除父子进程间的竞争条件,使得其调用顺序可控。

1 union semun {
2     int val;
3     struct semid_ds *buf;
4     unsigned short *array;
5 };
6
7 // 将信号量sem_id设置为init_value
8 int init_sem(int sem_id,int init_value) {
9     union semun sem_union;
10     sem_union.val=init_value;
11     if (semctl(sem_id,0,SETVAL,sem_union)==-1) {
12         perror("Sem init");
13         exit(1);
14     }
15     return 0;
16 }
17 // 删除sem_id信号量
18 int del_sem(int sem_id) {
19     union semun sem_union;
20     if (semctl(sem_id,0,IPC_RMID,sem_union)==-1) {
21         perror("Sem delete");
22         exit(1);
23     }
24     return 0;
25 }
26 // 对sem_id执行p操作
27 int sem_p(int sem_id) {
28     struct sembuf sem_buf;
29     sem_buf.sem_num=0;//信号量编号
30     sem_buf.sem_op=-1;//P操作
31     sem_buf.sem_flg=SEM_UNDO;//系统退出前未释放信号量,系统自动释放
32     if (semop(sem_id,&sem_buf,1)==-1) {
33         perror("Sem P operation");
34         exit(1);
35     }
36     return 0;
37 }
38 // 对sem_id执行V操作
39 int sem_v(int sem_id) {
40     struct sembuf sem_buf;
41     sem_buf.sem_num=0;
42     sem_buf.sem_op=1;//V操作
43     sem_buf.sem_flg=SEM_UNDO;
44     if (semop(sem_id,&sem_buf,1)==-1) {
45         perror("Sem V operation");
46         exit(1);
47     }
48     return 0;
49 }


1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <sys/types.h>
5 #include <unistd.h>
6 #include <sys/sem.h>
7 #include <sys/ipc.h>
8 #include "sem_com.c"
9
10 #define DELAY_TIME 3
11
12 int main() {
13     pid_t pid;
14 //    int sem_id;
15 //    key_t sem_key;
16
17 //    sem_key=ftok(".",'a');
18 //    以0666且create mode创建一个信号量,返回给sem_id
19 //    sem_id=semget(sem_key,1,0666|IPC_CREAT);
20 //    将sem_id设为1
21 //    init_sem(sem_id,1);
22
23     if ((pid=fork())<0) {
24         perror("Fork error!\n");
25         exit(1);
26     } else if (pid==0) {
27 //        sem_p(sem_id); //    P操作
28         printf("Child running...\n");
29         sleep(DELAY_TIME);
30         printf("Child %d,returned value:%d.\n",getpid(),pid);
31 //        sem_v(sem_id); //    V操作
32         exit(0);
33     } else {
34 //        sem_p(sem_id); //    P操作
35         printf("Parent running!\n");
36         sleep(DELAY_TIME);
37         printf("Parent %d,returned value:%d.\n",getpid(),pid);
38 //        sem_v(sem_id); //    V操作
39 //        waitpid(pid,0,0);
40 //        del_sem(sem_id);
41         exit(0);
42     }
43
44 }


在以上程序注释//未去掉时,即没用信号量机制时,其结果为:



显然,此处存在竞争条件。

在以上程序注释//去掉后,即使用信号量机制,其结果为:



由于父子进程采用同一信号量且均执行各自PV操作,故必先等一个进程的V操作后,另一个进程才能工作。

二. 共享内存

最为高效的进程间通信方式

进程直接读写内存,不需要任何数据的拷贝
  •为了在多个进程间交换信息,内核专门留出了一块内存区
  •由需要访问的进程将其映射到自己私有地址空间
  •进程直接读写这一内存区而不需要进行数据的拷贝,提高了效率

多个进程共享一段内存,需要依靠某种同步机制,如互斥锁和信号量等



l共享内存编程步骤:
  1. 创建共享内存
    •函数shmget()
    •从内存中获得一段共享内存区域

  2. 映射共享内存
    •把这段创建的共享内存映射到具体的进程空间中
    •函数shmat()

  3. 使用这段共享内存
    •可以使用不带缓冲的I/O读写命令对其进行操作

  4. 撤销映射操作: 函数shmdt()

  5. 删除共享内存: 函数shctl()











eg. 下面这个例子完成:父进程从stdin读取字符串并保存到共享内存中,子进程从共享内存中读出数据并输出到stdout

1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <sys/types.h>
5 #include <sys/ipc.h>
6 #include <sys/shm.h>
7
8 #define BUFFER_SIZE 2048
9
10 int main() {
11     pid_t pid;
12     int shmid;
13     char *shm_addr;
14     char flag[]="Parent";
15     char buff[BUFFER_SIZE];
16 //    创建当前进程的私有共享内存
17     if ((shmid=shmget(IPC_PRIVATE,BUFFER_SIZE,0666))<0) {
18         perror("shmget");
19         exit(1);
20     } else
21         printf("Create shared memory: %d.\n",shmid);
22
23 //    ipcs 命令往标准输出写入一些关于活动进程间通信设施的信息
24 //    -m 表示共享内存
25     printf("Created shared memory status:\n");
26     system("ipcs -m");
27
28     if((pid=fork())<0) {
29         perror("fork");
30         exit(1);
31     }else if (pid==0) {
32 //    自动分配共享内存映射地址,为可读可写,映射地址返回给shm_addr
33         if ((shm_addr=shmat(shmid,0,0))==(void*)-1) {
34             perror("Child:shmat");
35             exit(1);
36         }else
37             printf("Child: Attach shared-memory: %p.\n",shm_addr);
38
39         printf("Child Attach shared memory status:\n");
40         system("ipcs -m");
41 //    比较shm_addr,flag的长度为strlen(flag)的字符
42 //    当其内容相同时,返回0
43 //    否则返回(str1
-str2
)
44         while (strncmp(shm_addr,flag,strlen(flag))) {
45             printf("Child: Waiting for data...\n");
46             sleep(10);
47         }
48
49         strcpy(buff,shm_addr+strlen(flag));
50         printf("Child: Shared-memory: %s\n",buff);
51 //    删除子进程的共享内存映射地址
52         if (shmdt(shm_addr)<0) {
53             perror("Child:shmdt");
54             exit(1);
55         }else
56             printf("Child: Deattach shared-memory.\n");
57
58         printf("Child Deattach shared memory status:\n");
59         system("ipcs -m");
60
61     }else{
62         sleep(1);
63 //    自动分配共享内存映射地址,为可读可写,映射地址返回给shm_addr
64         if ((shm_addr=shmat(shmid,0,0))==(void*)-1) {
65             perror("Parent:shmat");
66             exit(1);
67         }else
68             printf("Parent: Attach shared-memory: %p.\n",shm_addr);
69
70         printf("Parent Attach shared memory status:\n");
71         system("ipcs -m");
72 //    shm_addr为flag+stdin
73         sleep(1);
74         printf("\nInput string:\n");
75         fgets(buff,BUFFER_SIZE-strlen(flag),stdin);
76         strncpy(shm_addr+strlen(flag),buff,strlen(buff));
77         strncpy(shm_addr,flag,strlen(flag));
78 //    删除父进程的共享内存映射地址
79         if (shmdt(shm_addr)<0) {
80             perror("Parent:shmdt");
81             exit(1);
82         }else
83             printf("Parent: Deattach shared-memory.\n");
84
85         printf("Parent Deattach shared memory status:\n");
86         system("ipcs -m");
87 //    保证父进程在删除共享内存前,子进程能读到共享内存的内容
88         waitpid(pid,NULL,0);
89 //    删除共享内存
90         if (shmctl(shmid,IPC_RMID,NULL)==-1) {
91             perror("shmct:IPC_RMID");
92             exit(1);
93         }else
94             printf("Delete shared-memory.\n");
95
96         printf("Child Delete shared memory status:\n");
97         system("ipcs -m");
98
99         printf("Finished!\n");
100     }
101
102     exit(0);
103 }




内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐