8.7  实验内容

8.7.1  管道通信实验

1.实验目的

通过编写有名管道多路通信实验,读者可进一步掌握管道的创建、读写等操作,同时,也复习使用select()函数实现管道的通信。

 

2.实验内容

读者还记得在6.3.3小节中,通过mknod命令创建两个管道的实例吗?本实例只是在它的基础上添加有名管道的创建,而不用再输入mknod命令。

 

3.实验步骤

(1)画出流程图。

该实验流程图如图8.9所示。

图8.9  8.6.1实验流程图

 

(2)编写代码。

该实验源代码如下所示。

 

/* pipe_select.c*/

#include <fcntl.h>

#include <stdio.h>

#include <unistd.h>

#include <stdlib.h>

#include <string.h>

#include <time.h>

#include <errno.h>

 

#define FIFO1               "in1"

#define FIFO2               "in2"

#define MAX_BUFFER_SIZE         1024          /* 缓冲区大小*/

#define IN_FILES            3                   /* 多路复用输入文件数目*/

#define TIME_DELAY          60              /* 超时值秒数 */

#define MAX(a, b)           ((a > b)?(a):(b))

 

int main(void)

{

     int fds[IN_FILES];

     char buf[MAX_BUFFER_SIZE];

     int i, res, real_read, maxfd;

     struct timeval tv;

     fd_set inset,tmp_inset;

    

     fds[0] = 0;        

    

     /* 创建两个有名管道 */

     if (access(FIFO1, F_OK) == -1) 

     {

        if ((mkfifo(FIFO1, 0666) < 0) && (errno != EEXIST))

        {

            printf("Cannot create fifo file\n");

            exit(1);

        }

     }

     if (access(FIFO2, F_OK) == -1) 

     {

        if ((mkfifo(FIFO2, 0666) < 0) && (errno != EEXIST))

        {

            printf("Cannot create fifo file\n");

            exit(1);

        }

     }

        

     /* 以只读非阻塞方式打开两个管道文件 */

     if((fds[1] = open (FIFO1, O_RDONLY|O_NONBLOCK)) < 0)

     {

        printf("Open in1 error\n");

        return 1;

     }

    if((fds[2] = open (FIFO2, O_RDONLY|O_NONBLOCK)) < 0)

    {

        printf("Open in2 error\n");

        return 1;

     }

 

     /*取出两个文件描述符中的较大者*/

    maxfd = MAX(MAX(fds[0], fds[1]), fds[2]);

    /*初始化读集合inset,并在读文件描述符集合中加入相应的描述集*/

    FD_ZERO(&inset); 

    for (i = 0; i < IN_FILES; i++)

    {

        FD_SET(fds[i], &inset);

    }

    FD_SET(0, &inset);

 

    tv.tv_sec = TIME_DELAY;

    tv.tv_usec = 0;

    /*循环测试该文件描述符是否准备就绪,并调用select()函数对相关文件描述符做相应操作*/

    while(FD_ISSET(fds[0],&inset) 

             || FD_ISSET(fds[1],&inset) || FD_ISSET(fds[2], &inset))

    { 

     /* 文件描述符集合的备份, 免得每次进行初始化 */

        tmp_inset = inset; 

        res = select(maxfd + 1, &tmp_inset, NULL, NULL, &tv);

        switch(res)

        {

            case -1:

            {

                  printf("Select error\n");

                  return 1;

              }

              break;                  

              case 0: /* Timeout */

              {

                  printf("Time out\n");

                  return 1;

           }

            break;

            default:

            {

                  for (i = 0; i < IN_FILES; i++)

                  {

                      if (FD_ISSET(fds[i], &tmp_inset))

                      {

                          memset(buf, 0, MAX_BUFFER_SIZE);

                          real_read = read(fds[i], buf, MAX_BUFFER_SIZE);

                          if (real_read < 0)

                          {

                              if (errno != EAGAIN)

                              {

                                  return 1;

                              }

                            }

                           else if (!real_read)

                           {

                               close(fds[i]);

                               FD_CLR(fds[i], &inset);

                          }

                          else

                          {

                               if (i == 0)

                               {/* 主程序终端控制 */

                                  if ((buf[0] == 'q') || (buf[0] == 'Q'))

                                   {

                                      return 1;

                               }

                               }

                               else

                               {/* 显示管道输入字符串 */

                                   buf[real_read] = '\0';

                                   printf("%s", buf);

                              }

                       }

                      } /* end of if */

                  } /* end of for */

             }

             break;

          } /* end of switch */

    } /*end of while */

    return 0;

}

 

(3)编译并运行该程序。

 

(4)另外打开两个虚拟终端,分别键入“cat > in1”和“cat > in2”,接着在该管道中键入相关内容,并观察实验结果。

 

4.实验结果

实验运行结果与第6章的例子完全相同。

 

$ ./pipe_select (必须先运行主程序) 

SELECT CALL

select call

TEST PROGRAMME

test programme

END

end

q /* 在终端上输入’q’或’Q’立刻结束程序运行 */

 

$ cat > in1 

SELECT CALL

TEST PROGRAMME

END

 

$ cat > in2

select call

test programme

end

 

8.7.2  共享内存实验

1.实验目的

通过编写共享内存实验,读者可以进一步了解使用共享内存的具体步骤,同时也进一步加深对共享内存的理解。在本实验中,采用信号量作为同步机制完善两个进程(“生产者”和“消费者”)之间的通信。其功能类似于“消息队列”中的实例,详见8.5.2小节。在实例中使用的与信号量相关的函数,详见8.3.3小节。

 

2.实验内容

该实现要求利用共享内存实现文件的打开和读写操作。

 

3.实验步骤

(1)画出流程图。

该实验流程图如图8.10所示。

图8.10  实验8.6.2流程图

 

(2)编写代码。

下面是共享内存缓冲区的数据结构的定义。

 

/* shm_com.h */

#include <unistd.h>

#include <stdlib.h>

#include <stdio.h>

#include <string.h>

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/shm.h>

#define SHM_BUFF_SZ 2048

struct shm_buff

{

    int pid;

    char buffer[SHM_BUFF_SZ];

};

 

以下是“生产者”程序部分。

 

/* sem_com.h 和 sem_com.c 与“信号量”小节示例中的同名程序相同 */

/* producer.c */

#include "shm_com.h"

#include "sem_com.h"

#include <signal.h>

int ignore_signal(void)

{ /* 忽略一些信号,免得非法退出程序 */

    signal(SIGINT, SIG_IGN);

    signal(SIGSTOP, SIG_IGN);

    signal(SIGQUIT, SIG_IGN);

    return 0;

}

 

int main()

{

    void *shared_memory = NULL;

    struct shm_buff *shm_buff_inst;

    char buffer[BUFSIZ];

    int shmid, semid;

    /* 定义信号量,用于实现访问共享内存的进程之间的互斥*/

      ignore_signal(); /* 防止程序非正常退出 */

    semid = semget(ftok(".", 'a'),  1, 0666|IPC_CREAT); /* 创建一个信号量*/

    init_sem(semid);/* 初始值为1 */

 

    /* 创建共享内存 */

    shmid = shmget(ftok(".", 'b'), sizeof(struct shm_buff), 0666|IPC_CREAT);

    if (shmid == -1)

    {

        perror("shmget failed");

        del_sem(semid);

        exit(1);

    }

   

 

    /* 将共享内存地址映射到当前进程地址空间 */

    shared_memory = shmat(shmid, (void*)0, 0);

    if (shared_memory == (void*)-1)

    {

        perror("shmat");

        del_sem(semid);

        exit(1);

    }

    printf("Memory attached at %X\n", (int)shared_memory);

    /* 获得共享内存的映射地址 */

    shm_buff_inst = (struct shared_use_st *)shared_memory;

    do

    {

        sem_p(semid);    

        printf("Enter some text to the shared memory(enter 'quit' to exit):");

        /* 向共享内存写入数据 */

            if (fgets(shm_buff_inst->buffer, SHM_BUFF_SZ, stdin) == NULL)

        {

            perror("fgets");

            sem_v(semid);

            break;

        }

        shm_buff_inst->pid = getpid();        

        sem_v(semid);

    } while(strncmp(shm_buff_inst->buffer, "quit", 4) != 0);

 

    /* 删除信号量 */

    del_sem(semid);

    /* 删除共享内存到当前进程地址空间中的映射 */

    if (shmdt(shared_memory) == 1)

    {

        perror("shmdt");

        exit(1);

    }

    exit(0);

}

 

以下是“消费者”程序部分。

 

/* customer.c */

#include "shm_com.h"

#include "sem_com.h"

 

int main()

{

    void *shared_memory = NULL;

    struct shm_buff *shm_buff_inst;

    int shmid, semid;

    /* 获得信号量 */

    semid = semget(ftok(".", 'a'),  1, 0666);

    if (semid == -1)

    {    

        perror("Producer is'nt exist");

        exit(1);

    }

    /* 获得共享内存 */

    shmid = shmget(ftok(".", 'b'), sizeof(struct shm_buff), 0666|IPC_CREAT);

    if (shmid == -1)

    {

        perror("shmget");

        exit(1);

    }

    /* 将共享内存地址映射到当前进程地址空间 */

    shared_memory = shmat(shmid, (void*)0, 0); 

    if (shared_memory == (void*)-1)

    {

        perror("shmat");

        exit(1);

    }

    printf("Memory attached at %X\n", (int)shared_memory);

    /* 获得共享内存的映射地址 */

    shm_buff_inst = (struct shm_buff *)shared_memory;

    do

    {

        sem_p(semid);

        printf("Shared memory was written by process %d :%s"

,                         shm_buff_inst->pid, shm_buff_inst->buffer);

        if (strncmp(shm_buff_inst->buffer, "quit", 4) == 0) 

        {

            break;

        }

        shm_buff_inst->pid = 0;

        memset(shm_buff_inst->buffer, 0, SHM_BUFF_SZ);

        sem_v(semid);

    } while(1);

 

    /* 删除共享内存到当前进程地址空间中的映射 */

    if (shmdt(shared_memory) == -1)

    {

        perror("shmdt");

        exit(1);

    }

    /* 删除共享内存 */

    if (shmctl(shmid, IPC_RMID, NULL) == -1)

    {

        perror("shmctl(IPC_RMID)");

        exit(1);

    }

    exit(0);

}

 

4.实验结果

$./producer

Memory attached at B7F90000

Enter some text to the shared memory(enter 'quit' to exit):First message

Enter some text to the shared memory(enter 'quit' to exit):Second message

Enter some text to the shared memory(enter 'quit' to exit):quit

$./customer

Memory attached at B7FAF000

Shared memory was written by process 3815 :First message

Shared memory was written by process 3815 :Second message

Shared memory was written by process 3815 :quit