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


    一. 信号量  

    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[n]-str2[n])
    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 }



     
     




     
  • 相关阅读:
    全局变量与全局静态变量的区别:
    Python模块学习 ---- datetime
    python sys.path用法
    过来人谈《去360还是留在百度?》
    [编码问题] Python错误: SyntaxError: Non-ASCII character
    E513: write error, conversion failed (make 'fenc' empty to override)"解决办法
    巴真的点评
    set之hashset与TreeSet、LinkedHashSet实现原理
    list之linedlist与arraylist实现原理
    统一会话与单点登录
  • 原文地址:https://www.cnblogs.com/forstudy/p/2413724.html
Copyright © 2020-2023  润新知