进程间通信

进程间通信及同步的方式有:pipe、fifo、共享内存、信号量、消息队列、socket通信等

1:管道
两个或多个进程间通过管道,可以互相传递信息,利用read和write系统调用函数来进行读写操作。

    int pipe(int filedes[2]);
    filedes是一个有两个成员的整形数组。
   (1)filedes[0]将用来从管道读取数据
   (2)filedes[1]用来向管道写入数据。

如下图所示:
这里写图片描述
示例代码:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()  {
    int data_processed;
    int file_pipes[2];
    const char some_data[] = “123”;
    char buffer[512];
    memset(buffer, ‘\0’, sizeof(buffer));
    if (pipe(file_pipes) == 0) 
    {
        data_processed=write(file_pipes[1],some_data,strlen(some_data));                     
        printf(“Wrote %d bytes\n”, data_processed);
        data_processed = read(file_pipes[0], buffer, BUFSIZ);
        printf(“Read %d bytes: %s\n”, data_processed, buffer);
        exit(EXIT_SUCCESS);
    }
    exit(EXIT_FAILURE);
  }

2:命名管道FIFO
(1)提供进程间数据交换的一种机制
(2)与pipe不同的是,命名管道不需要程序由一个共同的祖先进程启动
(3)命名管道是一种特殊类型的文件,因为是文件,具备了和文件相同的特点,有文件名、所有者,访问权限等,但行为和管道相同

2.1:命令行方式创建FIFO管道

$ mknod filename p
$ mkfifo filename 

2.2:程序中创建FIFO管道

#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char *filename, mode_t mode);
int mknod(const char *filename, mode_t mode | S_IFIFO, (dev_t)0);

参数filename为指定管道文件的名字
参数mode给出了FIFO的访问权限

例程:创建命名管道FIFO

#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>

int main()
{
    int res = mkfifo(“/tmp/my_fifo”, 0777);
    if (res == 0) 
    printf(“FIFO created\n”);
    exit(EXIT_SUCCESS);
}

2.3:打开FIFO文件
与通过pipe调用创建管道不同,FIFO是以命名文件的形式存在,而不是打开的文件描述符,所以对它进行读写操作之前必须先打开它。
FIFO也用open和close函数打开和关闭,但多了额外的功能
例:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#define FIFO_NAME “/tmp/my_fifo”
int main()  {
    int res;
    int open_mode = 0;
    open_mode = O_RDONLY | O_NONBLOCK;
    res=open(FIFO_NAME, open_mode);
    if (res == -1)
    {
        perror(“Failed tp open FIFO file”);
        exit(1);
    }
    printf(“FIFO file is opened\n”);
    clsoe(res);
    return 0;
  }

注:注意:打开管道文件,可以选择阻塞与非阻塞两种方式。

例程:FIFO读、写程序

读:

#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#define FIFO_NAME "/tmp/my_fifo“
 int main() 
 {   
    char buf[100];   
    int fd; 
    int nread;   
    fd = open(FIFO_NAME, O_RDONLY ,0);   
    if (fd == -1) 
    {      
         perror("open");      
         exit(1);
    }      
    memset(buf,0,sizeof(buf));      
   if ((nread = read(fd, buf, 100))==-1)
   { 
         perror("read");         
         exit(1);
   }      
   printf("read %s from FIFO\n",buf);   
   return 0;
   }

写:

#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#define FIFO_NAME "/tmp/my_fifo“
int main()  
{   
     int fd;   
     int nwrite;   
     char buf[100];      
     fd = open(FIFO_NAME,O_WRONLY  ,0);   
     if (fd == -1)   
     {     
        perror("Failed to open FIFO file");     
     }    
     printf("FIFO file is opened\n");   
     strcpy(buf,"helloworld");   
     if ((nwrite=write(fd,buf,100))==-1)   
     {      
           perror("write");      
           exit(1);   
     }   
      close(fd);   
      return 0;
  }

3:信号量
(1)确保程序对某个特定的资源具有独占式的访问的机制
(2)信号量是特殊的变量,只能取正整数并且只允许有两种操作:等待P和信号V

3.1:IPC机制关键值

#include <sys/ipc.h>

key_t ftok(const char *path, int id);

函数的作用是以path相关的信息为基础返回一个关键值,利用此关键值实现IPC同步和通信机制,也可使用IPC_PRIVATE作为参数由系统自动生成。
ftok与IPC_PRIVATE的区别:
ftok借助文件实现功能,常在不同的进程中使用,尤其是两个进程不是父子进程时,但因为是借助文件实现,一旦文件被删除后重新创建,获得的key就会发生变化,影响两个进程同步及通信机制的实现

IPC_PRIVATE由系统自动生成键值,常用于父子进程中,如要运行非父子进程关系的两个进程间,需要传值才能让另外的进程获得IPC_PRIVATE的值

3.2:semget函数:创建信号量集

#include <sys/sem.h>

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

参数key:整数,相关进程可以通过它访问一个信号量集
参数nsems:指定一个信号量集里的信号量数量。它几乎总是取值1
参数sem_flags:是一组标志,与open函数的标志相似

3.3:semop函数:实际执行各种信号量操作

#include <sys/sem.h>

int semop(int semid, struct sembuf * op_array, size_t num_ops);

参数semid:为semget函数返回的信号量集标识符
参数op_array:对应一个或多个信号量的sembuf结构体操作
参数num_ops:同时对几个信号量进行操作
sembuf结构体数据成员说明

struct sembuf
{
    short sem_num;
    short sem_op;
    short sem_flg;
}

sem_num: 对信号量集中的哪个信号量进行操作,从0开始表示
sem_op: 操作数设置,通常是+1或-1操作
sem_flg: 功能设置,IPC_NOWAIT与SEM_UNDO

3.4:semctl 函数:在一组信号量上做各种控制操作,诸如信号量集合的初始化、删除和状态查询等

#include <sys/sem.h>

int semctl(int semid, int sem_num, int command, union semun ctl_arg);

参数semid:为semget的返回值
参数sem_num:指定特定的信号量
参数ctl_arg 是一个联合体,对应三种不同功能的semctl调用
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
}
参数command:有三种功能:
(1)标准的IPC函数:
IPC_STAT : 把状态信息放入ctl_arg.stat中
IPC_SET : 用ctl_arg.stat中的值设置所有权/许可权
IPC_RMID : 从系统中删除信号量集合
(2)单信号量操作:
GETVAL : 返回信号量的值
SETVAL : 把信号量的值写入ctl_arg.val中
GETPID : 返回sempid值
GETNCNT : 返回semncnt
GETZCNT : 返回semzcnt
(3)全信号量操作:
GETALL : 把所有信号量的semvals值写入ctl_arg.array
SETALL : 用ctl_arg.array中的值设置所有信号量的semvals

3.5例子:
(1)创建信号量

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
    int semid;
    int nsems = 1;
    int flags = 0666;
    struct sembuf buf;
    semid = semget(IPC_PRIVATE, nsems, flags);
    if (semid < 0) {
       perror(“semget”);
       exit(EXIT_FAILURE);
    }
    printf(“semphore created: %d\n”, semid);
    buf.sem_num = 0;
    buf.sem_op = 1;
    buf.sem_flg = IPC_NOWAIT;
    if ((semop(semid, &buf, nsems)) < 0) {
       perror(“semop”);
       exit(EXIT_FAILURE);
    }
    system(“ipcs –s”);
    exit(EXIT_SUCCESS);
}

(2)删除信号量

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <stdlib.h>
 int main(int argc, char *argv[])
 {
      int semid;
      if (argc !=2) 
      {
         puts(“USAGE:sctl <semaphore id>”);
         exit(EXIT_FAILURE);
      }
      semid = atoi(argv[1]);
      if((semctl(semid, 0, IPC_RMID)) < 0)
      {
         perror(“semctl IPC_RMID”);
         exit(EXIT_FAILURE);
      } 
      else 
      {
         puts(“semaphore removed”);
         system(“ipcs –s”);
      }
       exit(EXIT_SUCCESS);
 }

(3)进程同步操作

#include <sys/types.h>
#include <linux/sem.h>
#include <unistd.h>
int pid,semid;
struct sembuf P,V;
union semun arg;
int main(void)   
{
   arg.val = 1;
   semid = semget(IPC_PRIVATE,1,0666|IPC_CREAT);
   semctl(semid, 0, SETVAL, arg);
   P.sem_num = 0;
   P.sem_op = -1;
   P.sem_flg = SEM_UNDO;
   V.sem_num = 0;
   V.sem_op = 1;
   V.sem_flg = SEM_UNDO;
   if ((pid = fork()) == -1)
   {
       perror(“fork”);
       exit(1);
    }
    else if (pid == 0)
    {
        semop(semid,&P,1);
        system(“echo –n ‘a’ >> ./printer);
        system(“echo –n ‘b’ >> ./printer);
        system(“echo ‘c’ >> ./printer);
        semop(semid,&V,1);
    }
    else
    { 
        semop(semid,&P,1);
        system(“echo –n ‘1’ >> ./printer);
        system(‘echo –n ‘2’ >> ./printer);
        system(‘echo ‘3’ >> ./printer);
        semop(semid,&V,1);
     }
     semctl(semid, IPC_RMID,0);
     exit(0);
 } 

4:消息队列

4.1:msgget函数:创建和访问消息队列

#include<sys/msg.h>

int msgget(key_t key, int permflags);

参数key:为标识好的消息队列
参数permflags:有两个值可选择:
IPC_CREAT : 创建新的队列
IPC_EXCL : 与IPC_CREAT合用,如要创建的队列已存在,返回值为-1

例:创建消息队列

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char *argv[])
{
    int qid;
    key_t key;
    key = 123;
    if ((qid = msgget(key, IPC_CREAT | 0666)) < 0)   
   {
        perror(“msgget:create”);
        exit(EXIT_FAILURE);
    }
    printf(“created queue id = %d\n”, qid);
    if ((qid == msgget(key, 0)) < 0) {
         perror(“msgget:open”);
         exit(EXIT_FAILURE);
    }
    printf(“opened queue id = %d\n”, qid);
    exit(EXIT_SUCCESS);
   }

4.2:
msgsnd函数:功能为向消息队列添加信息
msgrcv函数:功能为从队列中读取消息

#include <sys/msg.h>

int msgsnd(int mqid, const void * message, size_t size, int flags);
int msgrcv(int mqid, void * message, size_t size, long msg_type,int flags);

参数mqid:由msgget返回获得的
参数message:由用户自定义的结构
参数flags:只有一个有意义的值IPC_NOWAIT,当消息无法返回,调用立即返回,返回值为-1
参数msg_type:决定实际将读到的是哪条消息

4.3:msgctl函数:功能为使进程获取有关消息队列的状态信息、改变与队列有关的限制或从系统删除一个队列

int msgctl(int mqid, int command, struct msqid_ds *msq_stat);

参数mqid:有效的队列标识符
参数command:有三个值
IPC_STAT : 告诉系统把状态信息存入msq_stat中
IPC_SET : 用于通过msq_stat中存放的信息为消息队列设置控制变量的值
IPC_RMID : 从系统中删除消息队列
参数msq_stat:指向msqid_ds结构的指针

例程:发送与接收

#include <sys/types.h>
#include <linux/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main()
{
   int qid, len;
   struct msgbuf sndmsg;
   struct msgbuf rcvmsg;
   if ((qid  = msgget(IPC_PRIVATE, IPC_CREAT|0666))  == -1)
   {
      perror(“msgget”);
      exit(1);
   }
   sprintf(sndmsg.mtext, “hello”);
   sndmsg.mtype = 1;
   len = strlen(sndmsg.mtext);
   if ((msgsnd(qid, &sndmsg, len, 0)) < 0)
   {
       perror(“msgsnd”);
       exit(1);
   }
   puts(“message posted”);
   sleep(1);
   if ((msgrcv(qid, &rcvmsg, len, 0, 0)) < 0)
   {
      perror(“msgrcv”);
      exit(1);
   }
   puts(“message received”);
   rcvmsg.mtext[5]=‘\0\;
   printf(“This message is %s\n”,rcvmsg.mtext);
   msgctl(qid, IPC_RMID, NULL);
   exit(0);
}

5:共享内存
(1)共享存储操作使得两个或两个以上的进程可以共用一段物理内存
(2)一般情况下,两个进程的数据区是完全独立的,通常它在所有的IPC机制中是最有效的

5.1:shmget函数:创建一段内存用于进程共享数据

int shmget(key_t key, size_t size, int permflags);
参数key:内存段的关键值
参数size:内存段所需最小字节数
参数permflags:给出了内存段的访问权限
例程:创建共享内存

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#define BUFSZ 4096

int main(void)
{
   int shmid;
   if((shmid = shget(IPC_PRIVATE, BUFSZ,0666)) < 0) 
  {
      perror(“shmget”);
      exit(EXIT_FAILURE);
   }
   printf(“segment created: %d\n”, shmid);
   system(“ipcs –m”);
   exit(EXIT_SUCCESS);
}

5.2:shmat函数:功能为将标识的内存段挂接到调用进程的有效地址上

void *shmat(int shmid, const void *daddr, int shmflags);
参数shmid:表示的共享内存段
参数daddr:控制调用选择的地址
参数shmflags有2个值:
SHM_RDONLY: 使内存段只读挂接
SHM_RND : 使挂接的地址可以在daddr的一页范围内变动
如不设置,shmat将精确使用daddr的值

5.3:shmdt函数:取消共享内存的挂接
int shmdt(char *shmadr);
参数shmadr:取消挂接的共享内存

5.4:shmctl函数:功能为对共享内存进行控制管理
int shmctl(int shmid, int command, struct shmid_ds * shm_stat);
功能使用与msgctl函数类似,其中command参数可以取以下参数:
IPC_STAT 把shmid_ds结构中的数据设置为共享内存当前关联值
IPC_SET 进程有足够权限,就设置成shmid_ds中给出的值
IPC_RMID 删除共享内存段

struct shmid_ds
 {
         uid_t shm_perm.uid;
        uid_t shm_perm.gid;
        mode_t shm_perm.mode;
}

5.5:例:共享内存

#include <stdio.h>
#include <sys/shm.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
int main()
{  
   int shmid;  
   char *shmbuf;  
   pid_t pid;  
   key_t key;   
   shmid = shmget(IPC_PRIVATE, 100, IPC_CREAT|0777);  
   if (shmid == -1)  
   {    
       perror("shmid");    
       exit(1);
   }  
   shmbuf = shmat(shmid, NULL, 0);  
   if (shmbuf == (void*) -1)  
   {    
        perror("shmat");    
        exit(1);
   }  
   pid=fork();  
   if(pid == -1)  
   {    
      perror("fork");     
      exit(1);
   }  
   if(pid == 0) 
   {  
      strcpy(shmbuf,"this is child");
   }  
   else
   {      
      printf("%s\n",shmbuf);    
      wait(NULL);
      shmdt(shmid);  
      shmctl(shmid, IPC_RMID, NULL);
   } 
   exit(0);
}

_

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员的资料库

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值