当前位置:51VIP源码软件音乐小游戏下载联盟网络学院操作系统Linux → Linux下的C编程实战之三(下)

Linux下的C编程实战之三(下)

减小字体 增大字体 作者:宋宝华  来源:本站整理  发布时间:2008-6-9 1:41:41 我要发布文章
3.进程间通信

  Linux的进程间通信(IPC,InterProcess Communication)通信方法有管道、消息队列、共享内存、信号量、套接口等。

  管道分为有名管道和无名管道,无名管道只能用于亲属进程之间的通信,而有名管道则可用于无亲属关系的进程之间。

#define INPUT 0
#define OUTPUT 1
void main()
{
 int file_descriptors[2];
 /*定义子进程号 */
 pid_t pid;
 char buf[BUFFER_LEN];
 int returned_count;
 /*创建无名管道*/
 pipe(file_descriptors);
 /*创建子进程*/
 if ((pid = fork()) == - 1)
 {
  printf("Error in fork\n");
  exit(1);
 }
 /*执行子进程*/
 if (pid == 0)
 {
  printf("in the spawned (child) process...\n");
  /*子进程向父进程写数据,关闭管道的读端*/
  close(file_descriptors[INPUT]);
  write(file_descriptors[OUTPUT], "test data", strlen("test data"));
  exit(0);
 }
 else
 {
  /*执行父进程*/
  printf("in the spawning (parent) process...\n");
  /*父进程从管道读取子进程写的数据,关闭管道的写端*/
  close(file_descriptors[OUTPUT]);
  returned_count = read(file_descriptors[INPUT], buf, sizeof(buf));
  printf("%d bytes of data received from spawned process: %s\n",
  returned_count, buf);
 }
}

  上述程序中,无名管道以

int pipe(int filedis[2]);

  方式定义,参数filedis返回两个文件描述符filedes[0]为读而打开,filedes[1]为写而打开,filedes[1]的输出是filedes[0]的输入;

  在Linux系统下,有名管道可由两种方式创建(假设创建一个名为“fifoexample”的有名管道):

  (1)mkfifo("fifoexample","rw");

  (2)mknod fifoexample p

  mkfifo是一个函数,mknod是一个系统调用,即我们可以在shell下输出上述命令。

  有名管道创建后,我们可以像读写文件一样读写之:

/* 进程一:读有名管道*/
void main()
{
 FILE *in_file;
 int count = 1;
 char buf[BUFFER_LEN];
 in_file = fopen("pipeexample", "r");
 if (in_file == NULL)
 {
  printf("Error in fdopen.\n");
  exit(1);
 }
 while ((count = fread(buf, 1, BUFFER_LEN, in_file)) > 0)
  printf("received from pipe: %s\n", buf);
  fclose(in_file);
}

/* 进程二:写有名管道*/
void main()
{
 FILE *out_file;
 int count = 1;
 char buf[BUFFER_LEN];
 out_file = fopen("pipeexample", "w");
 if (out_file == NULL)
 {
  printf("Error opening pipe.");
  exit(1);
 }
 sprintf(buf, "this is test data for the named pipe example\n");
 fwrite(buf, 1, BUFFER_LEN, out_file);
 fclose(out_file);
}

  消息队列用于运行于同一台机器上的进程间通信,与管道相似;

  共享内存通常由一个进程创建,其余进程对这块内存区进行读写。得到共享内存有两种方式:映射/dev/mem设备和内存映像文件。前一种方式不给系统带来额外的开销,但在现实中并不常用,因为它控制存取的是实际的物理内存;常用的方式是通过shmXXX函数族来实现共享内存:

int shmget(key_t key, int size, int flag); /* 获得一个共享存储标识符 */

  该函数使得系统分配size大小的内存用作共享内存;

void *shmat(int shmid, void *addr, int flag); /* 将共享内存连接到自身地址空间中*/

  shmid为shmget函数返回的共享存储标识符,addr和flag参数决定了以什么方式来确定连接的地址,函数的返回值即是该进程数据段所连接的实际地址。此后,进程可以对此地址进行读写操作访问共享内存。

  本质上,信号量是一个计数器,它用来记录对某个资源(如共享内存)的存取状况。一般说来,为了获得共享资源,进程需要执行下列操作:

  (1)测试控制该资源的信号量;

  (2)若此信号量的值为正,则允许进行使用该资源,进程将进号量减1;

  (3)若此信号量为0,则该资源目前不可用,进程进入睡眠状态,直至信号量值大于0,进程被唤醒,转入步骤(1);

  (4)当进程不再使用一个信号量控制的资源时,信号量值加1,如果此时有进程正在睡眠等待此信号量,则唤醒此进程。

  下面是一个使用信号量的例子,该程序创建一个特定的IPC结构的关键字和一个信号量,建立此信号量的索引,修改索引指向的信号量的值,最后清除信号量:

#include <stdio.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <sys/ipc.h>
void main()
{
 key_t unique_key; /* 定义一个IPC关键字*/
 int id;
 struct sembuf lock_it;
 union semun options;
 int i;

 unique_key = ftok(".", 'a'); /* 生成关键字,字符'a'是一个随机种子*/
 /* 创建一个新的信号量集合*/
 id = semget(unique_key, 1, IPC_CREAT | IPC_EXCL | 0666);
 printf("semaphore id=%d\n", id);
 options.val = 1; /*设置变量值*/
 semctl(id, 0, SETVAL, options); /*设置索引0的信号量*/

 /*打印出信号量的值*/
 i = semctl(id, 0, GETVAL, 0);
 printf("value of semaphore at index 0 is %d\n", i);

 /*下面重新设置信号量*/
 lock_it.sem_num = 0; /*设置哪个信号量*/
 lock_it.sem_op = - 1; /*定义操作*/
 lock_it.sem_flg = IPC_NOWAIT; /*操作方式*/
 if (semop(id, &lock_it, 1) == - 1)
 {
  printf("can not lock semaphore.\n");
  exit(1);
 }

 i = semctl(id, 0, GETVAL, 0);
 printf("value of semaphore at index 0 is %d\n", i);

 /*清除信号量*/
 semctl(id, 0, IPC_RMID, 0);
}

  套接字通信并不为Linux所专有,在所有提供了TCP/IP协议栈的操作系统中几乎都提供了socket,而所有这样操作系统,对套接字的编程方法几乎是完全一样的。

  4.小节

  本章讲述了Linux进程的概念,并以多个实例讲解了进程控制及进程间通信方法,理解这一章的内容可以说是理解Linux这个操作系统的关键。