- ·上一篇文章:Linux下的C编程实战之四(上)
- ·下一篇文章:Linux下的C编程实战之三(上)
Linux下的C编程实战之三(下)
3.进程间通信
Linux的进程间通信(IPC,InterProcess Communication)通信方法有管道、消息队列、共享内存、信号量、套接口等。
管道分为有名管道和无名管道,无名管道只能用于亲属进程之间的通信,而有名管道则可用于无亲属关系的进程之间。
上述程序中,无名管道以
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下输出上述命令。
有名管道创建后,我们可以像读写文件一样读写之:
消息队列用于运行于同一台机器上的进程间通信,与管道相似;
共享内存通常由一个进程创建,其余进程对这块内存区进行读写。得到共享内存有两种方式:映射/dev/mem设备和内存映像文件。前一种方式不给系统带来额外的开销,但在现实中并不常用,因为它控制存取的是实际的物理内存;常用的方式是通过shmXXX函数族来实现共享内存:
该函数使得系统分配size大小的内存用作共享内存;
shmid为shmget函数返回的共享存储标识符,addr和flag参数决定了以什么方式来确定连接的地址,函数的返回值即是该进程数据段所连接的实际地址。此后,进程可以对此地址进行读写操作访问共享内存。
本质上,信号量是一个计数器,它用来记录对某个资源(如共享内存)的存取状况。一般说来,为了获得共享资源,进程需要执行下列操作:
(1)测试控制该资源的信号量;
(2)若此信号量的值为正,则允许进行使用该资源,进程将进号量减1;
(3)若此信号量为0,则该资源目前不可用,进程进入睡眠状态,直至信号量值大于0,进程被唤醒,转入步骤(1);
(4)当进程不再使用一个信号量控制的资源时,信号量值加1,如果此时有进程正在睡眠等待此信号量,则唤醒此进程。
下面是一个使用信号量的例子,该程序创建一个特定的IPC结构的关键字和一个信号量,建立此信号量的索引,修改索引指向的信号量的值,最后清除信号量:
套接字通信并不为Linux所专有,在所有提供了TCP/IP协议栈的操作系统中几乎都提供了socket,而所有这样操作系统,对套接字的编程方法几乎是完全一样的。
4.小节
本章讲述了Linux进程的概念,并以多个实例讲解了进程控制及进程间通信方法,理解这一章的内容可以说是理解Linux这个操作系统的关键。
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这个操作系统的关键。

