Chinaunix首页 | 论坛 | 博客
  • 博客访问: 4455847
  • 博文数量: 1214
  • 博客积分: 13195
  • 博客等级: 上将
  • 技术积分: 9105
  • 用 户 组: 普通用户
  • 注册时间: 2007-01-19 14:41
个人简介

C++,python,热爱算法和机器学习

文章分类

全部博文(1214)

文章存档

2021年(13)

2020年(49)

2019年(14)

2018年(27)

2017年(69)

2016年(100)

2015年(106)

2014年(240)

2013年(5)

2012年(193)

2011年(155)

2010年(93)

2009年(62)

2008年(51)

2007年(37)

分类: 系统运维

2011-02-12 23:46:48

UNIX System V IPC 进程间共享数据的方法有三种:
  • 消息队列
  • 共享内存
  • 信号量

这三种方式如果没有设置 IPC_CREAT 标志并且还未创建 IPC 实例,则 get 调用将返回错误。


消息队列
消息队列的好处是一个进程发送完后就可以结束了,接收进程接收消息并释放消息可以是好几天之后了。接收时还可以指定消息的类型。

msg_server.c msg_client.c
无论谁先启动都会创建消息队列,后启动的get创建的消息队列。之后msg_server 发送消息,msg_client 读取相应消息类型并释放消息队列。
如果msg_server 先启动,则msg_server进程立即结束,之后msg_client进程读取消息并释放消息队列。

C语言Codee#16794
msg_server.c
#include
#include
#include
#include
#include

int main (void)
{
  key_t ipckey;
  int mq_id;
  struct { long typechar text[100]; } mymsg;

  /* Generate the ipc key */
  ipckey = ftok("/tmp", 42);
  printf("My key is %d\n", ipckey);

  /* Set up the message queue */
  mq_id = msgget(ipckey, IPC_CREAT | 0660);
  printf("Message identifier is %d\n", mq_id);

  /* Send a message */
  memset(mymsg.text, 0, 100); /* Clear out the space */
  strcpy(mymsg.text, "Hello, world!");
  mymsg.type = 3;
  printf("%d\n", sizeof(mymsg));
  msgsnd(mq_id, &mymsg, sizeof(mymsg), 0);

  return 0;
}

msg_client.c
#include
#include
#include
#include
#include
#include

int main (void)
{
  key_t ipckey;
  int mq_id;
  struct { long typechar text[100]; } mymsg;
  int received;

  /* Generate the ipc key */
  ipckey = ftok("/tmp", 42);
  printf("My key is %d\n", ipckey);

  /* Set up the message queue */
  mq_id = msgget(ipckey, IPC_CREAT | 0660);
  if (mq_id == -1)
  {
      perror("msgget error");
      exit(-1);
  }
  printf("Message identifier is %d\n", mq_id);

  received = msgrcv(mq_id, &mymsg, sizeof(mymsg), 3, 0);

  printf("%s (%d)\n", mymsg.text, received);
  msgctl(mq_id, IPC_RMID, NULL);
  return 0;
}

msgsnd() 第四个参数指示是否阻塞该调用。
如果该参数标志为 IPC_NOWAIT,则即使队列已满,该调用也会返回。如果该参数标志为 0,则调用将阻塞,直至队列上的空间被释放、队列被删除或应用程序收到某个信号。

msgrcv( ) 系统调用是先由核心检查消息队列标识符和许可权,接着根据msgtyp分三种情况处理。 
(1) msgtyp=0,核心寻找消息队列中的第一个消息,并将它返回给调用进程; 
(2)msgtyp为正整数,核心返回给类型的第一个消息; 
(3)msgtyp为负整数,核心应在其类型值小于或等于msgtyp绝对值的所有消息中,选择类型最低的第一消息返回。 
如果所返回的消息的大小等于或小于用户请求,核心便将消息正文拷贝到用户区,再从队列中删除该消息,并唤醒睡眠的发送进程;如果消息比用户要求的大,则系统返回错误信息。
IPC_NOWAIT 如果没有满足条件的消息,调用立即返回,此时,errno=ENOMSG 
IPC_EXCEPT 与msgtyp>0配合使用,返回队列中第一个类型不为msgtyp的消息 
IPC_NOERROR 如果队列中满足条件的消息内容大于所请求的msgsz字节,则把该消息截断,截断部分将丢失。

消息队列对于短期进程是有用的。


共享内存
创建共享内存、写共享内存均需root权限,创建后内存区域所有数据均为0。如果写后读一次,共享内存区里面的数据全变为0,通常是由于读后释放共享内存,而再次读的是其他的共享内存区域。

ipc_shm_write.c ipc_shm_read.c
创建共享内存,系统会以页为单位进行分配,所以程序里传入4096 当作参数(一页)。无论谁先启动,都会创建共享内存页。但如果是ipc_shm_read.c 先创建,他自身结束时又会释放创建的共享内存。

C语言Codee#16795
ipc_shm_write.c
#include
#include
#include
#include
#include
#include

typedef struct {
    char name;
    int age;
} people;

int main(int argc, char **argv)
{
    int shm_id, i;
    key_t key;
    people *p_map = NULL;
    char temp_char = 'a';
    char *name = "/dev/shm";

    key = ftok(name, 0);
    printf("key=%d\n", key);
    if (key == -1{
        perror("ftok error");
        return -1;
    }

    shm_id = shmget(key, 4096, IPC_CREAT);
    printf("shm_id=%d\n", shm_id);
    //if (shm_id == -1 && errno == EEXIST);
    if (shm_id == -1{
        perror("shmget error");
        return -1;
    }

    p_map = (people *shmat(shm_id, NULL, 0);
    if (p_map == NULL{
        perror("shmat error");
        return -1;
    }
    for (i = 0i < 10i++{
        //(*(p_map + i)).name = temp_char;
        (p_map + i)->name = temp_char;
        (*(p_map + i)).age = 20 + i;
        printf("%c\n", temp_char);
        temp_char += 1;
    }
    if (shmdt(p_map== -1{
        perror("detach error");
        return -1;
    }

    return 0;
}

ipc_shm_read.c
#include
#include
#include
#include
#include

typedef struct {
    char name;
    int age;
} people;

int main(int argc, char **argv)
{
    int shm_id, i;
    key_t key;
    people *p_map = NULL;
    struct shmid_ds shmbuf;
    char *name="/dev/shm";

    key = ftok(name, 0);
    printf("key=%d\n", key);
    if (key == -1{
        perror("ftok error");
        return -1;
    }
    shm_id = shmget(key, 4096, IPC_CREAT);
        printf("shm_id=%d\n",shm_id);

    p_map = (people *shmat(shm_id, NULL, 0);
    if (p_map == NULL{
        perror("shmat error");
        return -1;
    }
    for (i = 0i < 10i++{
        printf("name----------%c\n", (*(p_map + i)).name);
        printf("age------------%d\n", (*(p_map + i)).age);
    }

    if (shmdt(p_map== -1{
        perror("shmdt error");
        return -1;
    }
    if (shmctl(shm_id, IPC_RMID, &shmbuf< 0{
        perror("shmctl error");
        return -1;
    }

    return 0;
}

shmget(IPC_PRIVATE,...)  IPC 密钥使用了 IPC_PRIVATE
当使用了 IPC_PRIVATE 时,将保证创建一个唯一的 IPC ID,并且预期应用程序将自己分发该 ID。

shmat 需要共享内存 ID、一个指针和某些标志。
该指针用于请求特定的内存地址。通过传递 0,内核可以随心所欲地选择任何内存地址。标志大部分是特定于供应商的,不过 SHM_RDONLY 是一个公共标志,用于指示不写入的段。shmat 的常见用法是让内核决定一切。


信号量
信号量+共享内存模型,可以用来使两个进程之间频繁通信,更棒的是不使用锁就可以完成。

sem_server.c sem_client.c
如果sem_client.c 无法get到信号量,就阻塞在那,并以0.1ms的粒度不停检测sem_server.c 是否创建(这个粒度的检测在Intel Pentium 1.7GHz的CPU上耗费4%的使用率)。这个例子的P, V 操作也是经典的实现。
在Linux下需要定义union semun(linux下头文件在linux/sem.h),而FreeBSD下却不需要(在sys/sem.h)。

C语言
sem_server.c
#include
#include
#include
#include
#include
#include
#include
#include

#define SEGSIZE    1024
#define READTIME   1

union semun {
    int              val;    /* Value for SETVAL */
    struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
    unsigned short  *array;  /* Array for GETALL, SETALL */
    struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                (Linux-specific) */
};

int generate_sem(key_t key)
{
    union semun sem;
    int semid;
    sem.val = 0;
    semid = semget(key, 1, IPC_CREAT | 0666);
    if (semid == -1{
        perror("create semaphore error\n");
        exit(-1);
    }
    /* 初始化信号量 */
    //semctl(semid, 0, SETVAL, sem);
    return semid;
}

void del_sem(int semid)
{
    //union semun sem;
    //sem.val = 0;
    semctl(semid, 0, IPC_RMID, 0);
}

int p(int semid)
{
    struct sembuf sops = { 0, 1, IPC_NOWAIT };
    return (semop(semid, &sops, 1));
}
int v(int semid)
{
    struct sembuf sops = { 0, -1, IPC_NOWAIT };
    return (semop(semid, &sops, 1));
}

int main()
{
    key_t key;
    int shmid, semid;
    char *shmaddr;
    char msg[7] = "data ";
    char i;
    struct shmid_ds shmbuf;

    key = ftok("/", 0);
    shmid = shmget(key, SEGSIZE, IPC_CREAT | 0604);
    if (shmid == -1{
        printf("create shared momery error\n");
        return -1;
    }
    shmaddr = (char *shmat(shmid, NULL, 0);
    if (shmaddr == (void *-1{
        printf(" attach shared momery error\n");
        return -1;
    }
    semid = generate_sem(key);
    for (i = 0i <= 3i++{
        p(semid);
        sleep(READTIME);
        msg[5] = 'a' + i;
        memcpy(shmaddr, msg, sizeof(msg));
        sleep(2);
        v(semid);
    }
    shmdt(shmaddr);
    shmctl(shmid, IPC_RMID, &shmbuf);
    del_sem(semid);

    return 0;
}

sem_client.c
#include
#include
#include
#include
#include
#include
#include
#include

#define SEGSIZE    1024

union semun {
    int              val;    /* Value for SETVAL */
    struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
    unsigned short  *array;  /* Array for GETALL, SETALL */
    struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                (Linux-specific) */
};

/* 打印程序执行时间 */
void secondpass()
{
    static long start = 0;
    time_t timer;
    if (start == 0{
        timer = time(NULL);
        start = (longtimer;
    }
    printf("second:%ld\n", (long) (time(NULL)) - start);
}

int get_sem(key_t key)
{
    union semun sem;
    int semid;
    sem.val = 0;
    semid = semget(key, 0, 0);
    if (semid == -1{
        return -1;
    }
    return semid;
}

/* 等待信号量变成   0 */
void waitv(int semid)
{
/* {1, 0, 0} 将会阻塞到这里,但CPU使用率还是那么多 */
    struct sembuf sops = { 0, 0, 0 };
    semop(semid, &sops, 1);
}

int main()
{
    key_t key;
    int shmid, semid;
    char *shmaddr;
    int i;

    key = ftok("/", 0);
    shmid = shmget(key, SEGSIZE, IPC_CREAT | 0604);
    if (shmid == -1{
        printf("get shared momery error\n");
        return -1;
    }
    shmaddr = (char *shmat(shmid, NULL, 0);
    if (shmaddr == (void *)-1{
        printf("client attach shared momery error\n");
        return -1;
    }
    while ( (semid = get_sem(key)) == -1 ) {
        //printf("get semaphore error\n");
        usleep(100);
    }
    for (i = 0i < 3i++{
        sleep(1);
        waitv(semid);
        printf("the msg get is: %s\n", shmaddr);
        secondpass();
    }
    shmdt(shmaddr);
    return 0;
}

semget() 第二个参数指定信号量集(Semaphore Set) 的大小。
信号量集是一组共享一个公共 IPC 实例的信号量。该集合中的信号量数量无法更改。如果已经创建了信号量集,则 semget 的第二个参数实际上被忽略。

sem_op in struct sembuf
(1)如果 sem_op 为 0,则测试 sem_num 以确定它是否为 0。如果 sem_num 为 0,则运行下一个测试。如果 sem_num 不为 0,则在未设置 IPC_NOWAIT 时,操作将阻塞直至信号量变为 0,而在设置了 IPC_NOWAIT 时,则跳过其他测试。
(2)如果 sem_op 是某个正数,则将信号量的值加上 sem_op 的值。
(3)如果 sem_op 是一个负整数,并且信号量的值大于或等于 sem_op 的绝对值,则从信号量的值减去该绝对值。
(4)如果 sem_op 是一个负整数,并且信号量的值小于 sem_op 的绝对值,则在 IPC_NOWAIT 为 true 时立即停止测试的执行,而在该值为 false 时则阻塞,直至信号量的值变得大于 sem_op 的绝对值。

信号量被称为建议锁(Advisory Lock)。这意味着信号量本身并不阻止两个进程同时使用同一个资源;相反,它们旨在建议任何进程自愿询问该资源是否正在使用。

参考链接:
  (信号量代码)
阅读(1269) | 评论(0) | 转发(0) |
0

上一篇:捕捉SIGPIPE

下一篇:飞翔的方法(一)

给主人留下些什么吧!~~