(Bruce Molay)Unix/Linux编程实践教程读书笔记(二)----进程间通信(IPC)

进程间通信

一、通信方式:

1.同一台机器的进程间可以通过文件、命名管道、共享内存来进行通信。

2.不同机器间的进程可以通过套接字、文件来进行通信。

二、各种通信方式介绍:

1、文件:两个不同的进程可以通过对同一文件的读写来进行通信。

     竞态条件:如果一个进程刚好在另一个进程对文件进行清空和重写之间读取文件,那么它得到的将是一个空的,或者只有部分的内容。

     避免竞态的条件:可以使用某种类型的互斥量来避免竞态条件。如:文件锁、信号量等。。。

2、命名管道(FIFO):可以连接不相关的进程,并且可以独立于进程存在。

             管道:只能连接相关的进程,由进程创建,并由最后一个进程关闭。

      创建FIFO:mkfifo(char *name,mode_t mode)

      删除FIFO:unlink(char *fifoname);

      监听FIFO:open(fifoname,O_RDONLY);

      写入FIFO:open(fifoname,O_WRONLY);

     对命名管道的操作和对文件的操作很相像。

     竞态条件:因为read和write都是原子操作,完全不存在竞态条件。

3.共享内存:同一系统里的两个进程通过使用共享的内存段来交换数据,每一个进程都有一个指向此内存段的指针。

    基本概念:1.共享内存段在内存中不依赖于进程的存在而存在。

                       2.共享内存段有自己的名字,称为关键字(KEY)。

                       3.关键字是个整数型,,大于0的32位整数。

                       4.共享内存段有自己的拥有者以及权限位。

                       5.进程可以连接到某共享内存段,并且获得指向此段的指针。

    如何获得共享内存段:int segid = shmget(key,size-of-segment,flags)

                             参数:key  共享内存段名称

                                       size-of-segment  共享内存段大小,单位:字节

                                       flags   IPC_CREAT 共享内存不存在则创建一个/IPC_EXCL 只有在共享内存段不存在的时候,新的共享内存段才建立。

                            返回值:  -1 发生错误。

    如何将进程连接到某个共享内存段: void *ptr = shmat(seg_id,shmaddr,flags)

                             参数:flags 用来指定此内存段是否为只读模式SHM_RDONLY,一般为0,其它为读写模式。

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

                             返回值: -1发生错误。

   如何与共享内存段进行读写交互:strcpy(ptr,"hello")/memcpy()/ptr[i]以及其他一些通用的指针操作。

   如何断开共享内存连接:int shmdt(shmaddr)

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

                              返回值:0成功,-1失败。

   如何管理共享内存:int shmctl(seg_id,cmd,struct shmid_ds  *buf)

                              参数:cmd  IPC_STAT 得到共享内存的状态

                                                  IPC_SET  改变共享内存的状态

                                                  IPC_RMID 删除这片共享内存

                                         struct shmid_ds{
                                                  struct ipc_perm shm_perm;/* 操作权限*/
                                                  int shm_segsz;                    /*段的大小(以字节为单位)*/
                                                  time_t shm_atime;          /*最后一个进程附加到该段的时间*/
                                                  time_t shm_dtime;          /*最后一个进程离开该段的时间*/
                                                  time_t shm_ctime;          /*最后一个进程修改该段的时间*/
                                                  unsigned short shm_cpid;   /*创建该段进程的pid*/
                                                  unsigned short shm_lpid;   /*在该段上操作的最后1个进程的pid*/
                                                  short shm_nattch;          /*当前附加到该段的进程的个数*/
                                                  /*下面是私有的*/
                                                  unsigned short shm_npages;  /*段的大小(以页为单位)*/
                                                  unsigned long *shm_pages;   /*指向frames->SHMMAX的指针数组*/
                                                  struct vm_area_struct *attaches; /*对共享段的描述*/
                                                  };

   竞态条件:如果一个进程正好在另一进程更改共享内存段时读取其中的数据,那么它可能读到新数据也读到老数据。

   避免竞态条件:可以使用信号量机制来避免竞态。

三、进程在访问共享资源时所使用的技术:文件锁和信号量

1.文件锁:含有读数据锁和写数据锁。

     如何给已经打开的文件加读/写数据锁:fcntl(fd,F_SETLKW,&lockinfo)

                                                    参数:fd 文件描述符

                                                               F_SETLKW  设置文件锁,可以等待其他的进程释放锁

                                                               lockinfo  是一个指向struct flock 类型的变量

                                                               struct  flock {

                                                                          short  int  l_type;//F_RDLCK(读数据锁)、F_WRLCK(写数据锁)、F_UNLCK(解锁)

                                                                          short  int  l_whence;//SEEK_SET、SEEK_CUR、SEEK_END

                                                                          off_t l_start;

                                                                          off_t l_len;

                                                                          pid_t l_pid;

                                                               }; 

2.信号量:信号量是一个内核变量,它可以被系统中的任何进程所访问。

                 条件变量的对象是进程中的全局变量,而信号量则是系统中的全局变量。

   如何创建信号量集:int semset_id = semget(key_t key,int numsems,int flags)

                        参数:key  信号量名称,关键字

                                   numsems 信号量的数量

                                   flags  IPC_CREAT、IPC_EXCL、IPC_PRIVATE

                       返回值:-1 发生错误

                                     semset_id 信号量集ID

  如何管理信号量集:int semctl(int semid,int semnum,int cmd,union semun arg)

                       参数:semid 信号量集ID

                                  semnum 信号量集中特定信号量编号

                                  cmd  IPC_STAT 从信号量集上检索semid_ds结构,并存到semun联合体参数的成员buf的地址中               union semun{

                                           IPC_SET 设置一个信号量集合的semid_ds结构中ipc_perm域的值,并从semun的buf中取出值                    int val;

                                           IPC_RMID 从内核中删除信号量集合                                                                                                               struct semid_ds *buf;

                                           GETALL 从信号量集合中获得所有信号量的值,并把其整数值存到semun联合体成员的一个指针数组中      ushort  *array;

                                           GETNCNT 返回当前等待资源的进程个数                                                                                                       };

                                           GETPID 返回最后一个执行系统调用semop()进程ID

                                           GETVAL 返回信号量集合内单个信号量的值

                                           GETZCNT 返回当前等待100%资源利用的进程个数

                                           SETALL 设置信号集合中所有信号量的值

                                           SETVAL 用联合体重val成员的值设置信号量集合中单个信号量的值

  如何操作信号量  semop(int semid,struct sembuf *actions,size_t numactions)

                    参数:semid 用来指定信号量集;

                               actions  一组活动的数组;

                               numactions 改数组的大小。

                               struct sembuf {

                                         short sem_num;//指定信号量数目

                                         short sem_op;//信号量的变化值,通常情况下使用两个值,“-1”是P操作,用来等待一个信号量变得可用,“+1”是V操作,用来通知一个信号量可用

                                         short sem_flg;//通常设置为SEM_UNDO,这会使得操作系统跟踪当前进程对信号量所做的改变,使得系统可以自动释放这个信号量

                                         };

四,程序实例:

1.进程间通过使用共享文件来进行通信,通过使用文件锁来避免竞态。

1.1  时间服务器,通过写数据锁来锁定文件:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<time.h>
#include<string.h>
#include<sys/file.h>

#define oops(m,x){perror(m);exit(x);}

void lock_operation(int ,int);


int main(int ac,char *av[])
{
    int fd;
    time_t now;
    char *message;

    if(ac != 2)
    {
        fprintf(stderr,"Usage:%s filename\n",*av);
        exit(EXIT_FAILURE);
    }

    if((fd = open(av[1],O_CREAT|O_EXCL|O_WRONLY,0644)) == -1)
        oops("file open",1);

    while(1)
    {
        time(&now);
        message = ctime(&now);
        lock_operation(fd,F_WRLCK);
        if(lseek(fd,0L,SEEK_SET) == -1)
            oops("lseek",2);
        if(write(fd,message,strlen(message)) == -1)
            oops("write",4);

        lock_operation(fd,F_UNLCK);
        sleep(1);
    }
}

void lock_operation(int fd, int op)
{
    struct flock lock;

    lock.l_whence = SEEK_SET;
    lock.l_start = lock.l_len = 0;
    lock.l_pid = getpid();
    lock.l_type = op;

    if(fcntl(fd,F_SETLKW,&lock) == -1)
        oops("lock operation",6);
}

1.2 客户端,通过读文件锁来锁定文件

#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>

#define oops(m,x){perror(m);exit(x);}
void lock_operation(int,int);
int main(int ac,char *av[])
{
    int fd;
    char buf[BUFSIZ];
    int n;

    if(ac != 2)
    {
        fprintf(stderr,"Usage: %s filename\n",*av);
        exit(EXIT_FAILURE);
    }

    if((fd = open(av[1],O_RDONLY)) == -1)
        oops("file open",1);

    lock_operation(fd,F_RDLCK);

    while((n = read(fd,buf,BUFSIZ)) > 0)
        write(1,buf,n);

    lock_operation(fd,F_UNLCK);

    close(fd);
    exit(EXIT_SUCCESS);
}

void lock_operation(int fd, int op)
{
    struct flock lock;

    lock.l_whence = SEEK_SET;
    lock.l_start = lock.l_len = 0;
    lock.l_pid = getpid();
    lock.l_type = op;

    if(fcntl(fd,F_SETLKW,&lock) == -1)
        oops("file operation",2);
}

2 进程间通过共享内存来进行通信,通过信号量机制来避免竞态。

2.1 时间服务器通过共享内存写入数据 :

#include<stdio.h>
#include<stdlib.h>
#include<sys/shm.h>
#include<string.h>
#include<sys/types.h>
#include<time.h>
#include<sys/sem.h>
#include<signal.h>
#include<unistd.h>

#define TIME_MEM_KEY 100
#define TIME_SEM_KEY 9990
#define SEG_SIZE ((size_t)100)
#define oops(m,x){perror(m);exit(x);}


union semun {int val;struct semid_ds *buf; ushort *array;};

int shm_id,sem_id;

void set_sem_value(int , int , int );
void cleanup(int );
void wait_and_lock(int );
void release_lock(int );

int main(int ac,char *av[])
{
    char *mem_ptr;
    int n;
    time_t now;

    shm_id = shmget(TIME_MEM_KEY,SEG_SIZE,IPC_CREAT|IPC_EXCL|0777);
    if(shm_id == -1)
        oops("shmget",1);

    mem_ptr = shmat(shm_id,NULL,0);
    if(mem_ptr == (void *) -1)
        oops("shmat",2);

    sem_id = semget(TIME_SEM_KEY,2,(066|IPC_CREAT|IPC_EXCL));
    if(sem_id == -1)
        oops("semget",3);

    set_sem_value(sem_id,0,0);
    set_sem_value(sem_id,1,0);

    signal(SIGINT,cleanup);

    for(n = 0; n < 60; n++)
    {
        time(&now);
            printf("\t shm waiting for lock\n");
        wait_and_lock(sem_id);
            printf("\t shm updating memory\n");
        strcpy(mem_ptr,ctime(&now));
            sleep(5);
        release_lock(sem_id);
            printf("\t shm released lock\n");
        sleep(1);
    }
    cleanup(0);
    exit(EXIT_SUCCESS);
}

void set_sem_value(int sem_id, int semnum, int value)
{
    union semun initval;

    initval.val = value;

    if(semctl(sem_id,semnum,SETVAL,initval) == -1)
        oops("semctl",4);
}

void cleanup(int n)
{
    shmctl(shm_id,IPC_RMID,NULL);
    semctl(sem_id,0,IPC_RMID,NULL);
}

void wait_and_lock(int sem_id)
{
    struct sembuf actions[2];

    actions[0].sem_num = 0;
    actions[0].sem_flg = SEM_UNDO;
    actions[0].sem_op = 0;

    actions[1].sem_num = 1;
    actions[1].sem_flg = SEM_UNDO;
    actions[1].sem_op = +1;

    if(semop(sem_id,actions,2) == -1)
        oops("semop:locking",5);
}

void release_lock(int sem_id)
{
    struct sembuf actions[1];

    actions[0].sem_num = 1;
    actions[0].sem_flg = SEM_UNDO;
    actions[0].sem_op = -1;

    if(semop(sem_id,actions,1) == -1)
        oops("semop:unlocking",6);
}


2.2  客户端通过共享内存来读取数据

#include<stdio.h>
#include<stdlib.h>
#include<sys/shm.h>
#include<string.h>
#include<sys/types.h>
#include<time.h>
#include<sys/sem.h>
#include<signal.h>
#include<unistd.h>

#define TIME_MEM_KEY 100
#define TIME_SEM_KEY 9990
#define SEG_SIZE ((size_t)100)
#define oops(m,x){perror(m);exit(x);}

union semun {int val;struct semid_ds *buf; ushort *array;};
void release_lock(int );
void wait_and_lock(int);

int main()
{
   int shm_id,sem_id;
   char *mem_ptr;

   shm_id = shmget(TIME_MEM_KEY,SEG_SIZE,0777);
   if(shm_id == -1)
       oops("shemget",1);

   mem_ptr = shmat(shm_id,NULL,0);
   if(mem_ptr == (void *)-1)
       oops("shmat",2);

   sem_id = semget(TIME_SEM_KEY,2,0);
   wait_and_lock(sem_id);

   printf("The time,direct from memory:...%s",mem_ptr);

   release_lock(sem_id);
   shmdt(mem_ptr);
   exit(EXIT_SUCCESS);
}

void wait_and_lock(int sem_id)
{
    struct sembuf actions[2];

    actions[0].sem_num = 1;
    actions[0].sem_flg = SEM_UNDO;
    actions[0].sem_op = 0;

    actions[1].sem_num = 0;
    actions[1].sem_flg = SEM_UNDO;
    actions[1].sem_op = +1;

    if(semop(sem_id,actions,2) == -1)
        oops("semop:locking",3);
}

void release_lock(int sem_id)
{
    struct sembuf actions[1];

    actions[0].sem_num = 0;
    actions[0].sem_flg = SEM_UNDO;
    actions[0].sem_op = -1;

    if(semop(sem_id,actions,1) == -1)
        oops("semop:unlocking",4);
}


 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值