当前位置:文档之家› linux下的进程间通信

linux下的进程间通信

linux下的进程间通信
linux下的进程间通信

详细的讲述进程间通信在这里绝对是不可能的事情,而且笔者很难有信心说自己对这一部分内容的认识达到了什么样的地步,所以在这一节的开头首先向大家推荐著名作者Richard Stevens的著名作品:《Advanced Programming in the UNIX Environment》,它的中文译本《UNIX环境高级编程》已有机械工业出版社出版,原文精彩,译文同样地道,如果你的确对在Linux下编程有浓厚的兴趣,那么赶紧将这本书摆到你的书桌上或计算机旁边来。说这么多实在是难抑心中的景仰之情,言归正传,在这一节里,我们将介绍进程间通信最最初步和最最简单的一些知识和概念。

首先,进程间通信至少可以通过传送打开文件来实现,不同的进程通过一个或多个文件来传递信息,事实上,在很多应用系统里,都使用了这种方法。但一般说来,进程间通信(IPC:InterProcess Communication)不包括这种似乎比较低级的通信方法。Unix系统中实现进程间通信的方法很多,而且不幸的是,极少方法能在所有的Unix系统中进行移植(唯一一种是半双工的管道,这也是最原始的一种通信方式)。而Linux作为一种新兴的操作系统,几乎支持所有的Unix下常用的进程间通信方法:管道、消息队列、共享内存、信号量、套接口等等。下面我们将逐一介绍。

2.3.1 管道

管道是进程间通信中最古老的方式,它包括无名管道和有名管道两种,前者用于父进程和子进程间的通信,后者用于运行于同一台机器上的任意两个进程间的通信。

无名管道由pipe()函数创建:

#include

int pipe(int filedis[2]);

参数filedis返回两个文件描述符:filedes[0]为读而打开,filedes[1]为写而打开。filedes[1]的输出是filedes[0]的输入。下面的例子示范了如何在父进程和子进程间实现通信。

#define INPUT 0

#define OUTPUT 1

void main() {

int file_descriptors[2];

/*定义子进程号*/

pid_t pid;

char buf[256];

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);

}

}

在Linux系统下,有名管道可由两种方式创建:命令行方式mknod系统调用和函数mkfifo。下面的两种途径都在当前目录下生成了一个名为myfifo的有名管道:

方式一:mkfifo("myfifo","rw");

方式二:mknod myfifo p

生成了有名管道后,就可以使用一般的文件I/O函数如open、close、read、write等来对它进行操作。下面即是一个简单的例子,假设我们已经创建了一个名为myfifo的有名管道。

/* 进程一:读有名管道*/

#include

#include

void main() {

FILE * in_file;

int count = 1;

char buf[80];

in_file = fopen("mypipe", "r");

if (in_file == NULL) {

printf("Error in fdopen./n");

exit(1);

}

while ((count = fread(buf, 1, 80, in_file)) > 0)

printf("received from pipe: %s/n", buf);

fclose(in_file);

}

/* 进程二:写有名管道*/

#include

#include

void main() {

FILE * out_file;

int count = 1;

char buf[80];

out_file = fopen("mypipe", "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, 80, out_file);

fclose(out_file);

}

2.3.2 消息队列

消息队列用于运行于同一台机器上的进程间通信,它和管道很相似,是一个在系统内核中用来保存消息的队列,它在系统内核中是以消息链表的形式出现。消息链表中节点的结构用msg声明。

事实上,它是一种正逐渐被淘汰的通信方式,我们可以用流管道或者套接口的方式来取代它,所以,我们对此方式也不再解释,也建议读者忽略这种方式。

2.3.3 共享内存

共享内存是运行在同一台机器上的进程间通信最快的方式,因为数据不需要在不同的进程间复制。通常由一个进程创建一块共享内存区,其余进程对这块内存区进行读写。得到共享内存有两种方式:映射/dev/mem设备和内存映像文件。前一种方式不给系统带来额外的开销,但在现实中并不常用,因为它控制存取的将是实际的物理内存,在Linux系统下,这只有通过限制Linux系统存取的内存才可以做到,这当然不太实际。常用的方式是通过shmXXX函数族来实现利用共享内存进行存储的。

首先要用的函数是shmget,它获得一个共享存储标识符。

#include

#include

#include

int shmget(key_t key, int size, int flag);

这个函数有点类似大家熟悉的malloc函数,系统按照请求分配size大小的内存用作共享内存。Linux系统内核中每个IPC结构都有的一个非负整数的标识符,这样对一个消息队列发送消息时只要引用标识符就可以了。这个标识符是内核由IPC结构的关键字得到的,这个关键字,就是上面第一个函数的key。数据类型key_t是在头文件sys/types.h中定义的,它是一个长整形的数据。在我们后面的章节中,还会碰到这个关键字。

当共享内存创建后,其余进程可以调用shmat()将其连接到自身的地址空间中。

void *shmat(int shmid, void *addr, int flag);

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

使用共享存储来实现进程间通信的注意点是对数据存取的同步,必须确保当一个进程去读取数据时,它所想要的数据已经写好了。通常,信号量被要来实现对共享存储数据存取的同步,另外,可以通过使用shmctl函数设置共享存储内存的某些标志位如SHM_LOCK、SHM_UNLOCK等来实现。

2.3.4 信号量

信号量又称为信号灯,它是用来协调不同进程间的数据对象的,而最主要的应用是前一节的共享内存方式的进程间通信。本质上,信号量是一个计数器,它用来记录对某个资源(如共享内存)的存取状况。一般说来,为了获得共享资源,进程需要执行下列操作:(1)测试控制该资源的信号量。

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

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

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

维护信号量状态的是Linux内核操作系统而不是用户进程。我们可以从头文件

/usr/src/linux/include/linux/sem.h 中看到内核用来维护信号量状态的各个结构的定义。信号量是一个数据集合,用户可以单独使用这一集合的每个元素。要调用的第一个函数是semget,用以获得一个信号量ID。

struct sem {

short sempid;/* pid of last operaton */

ushort semval;/* current value */

ushort semncnt;/* num procs awaiting increase in semval */

ushort semzcnt;/* num procs awaiting semval = 0 */

}

#include

#include

#include

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

key是前面讲过的IPC结构的关键字,flag将来决定是创建新的信号量集合,还是引用一个现有的信号量集合。nsems是该集合中的信号量数。如果是创建新集合(一般在服务器中),则必须指定nsems;如果是引用一个现有的信号量集合(一般在客户机中)则将nsems指定为0。

semctl函数用来对信号量进行操作。

int semctl(int semid, int semnum, int cmd, union semun arg);

不同的操作是通过cmd参数来实现的,在头文件sem.h中定义了7种不同的操作,实际编程时可以参照使用。

semop函数自动执行信号量集合上的操作数组。

int semop(int semid, struct sembuf semoparray[], size_t nops);

semoparray是一个指针,它指向一个信号量操作数组。nops规定该数组中操作的数量。

下面,我们看一个具体的例子,它创建一个特定的IPC结构的关键字和一个信号量,建立此信号量的索引,修改索引指向的信号量的值,最后我们清除信号量。在下面的代码中,函数ftok生成我们上文所说的唯一的IPC关键字。

#include

#include

#include

#include

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);

}

semget()

可以使用系统调用semget()创建一个新的信号量集,或者存取一个已经存在的信号量集:

系统调用:semget();

原型:intsemget(key_t key,int nsems,int semflg);

返回值:如果成功,则返回信号量集的IPC标识符。如果失败,则返回-1:errno=EACCESS(没有权限)

EEXIST(信号量集已经存在,无法创建)

EIDRM(信号量集已经删除)

ENOENT(信号量集不存在,同时没有使用IPC_CREAT)

ENOMEM(没有足够的内存创建新的信号量集)

ENOSPC(超出限制)

系统调用semget()的第一个参数是关键字值(一般是由系统调用ftok()返回的)。系统内核将此值和系统中存在的其他的信号量集的关键字值进行比较。打开和存取操作与参数semflg中的内容相关。IPC_CREAT如果信号量集在系统内核中不存在,则创建信号量集。IPC_EXCL当和IPC_CREAT一同使用时,如果信号量集已经存在,则调用失败。如果单独使用IPC_CREAT,则semget()要么返回新创建的信号量集的标识符,要么返回系统中已经存在的同样的关键字值的信号量的标识符。如果IPC_EXCL和IPC_CREAT一同使用,则要么返回新创建的信号量集的标识符,要么返回-1。IPC_EXCL单独使用没有意义。参数nsems指出了一个新的信号量集中应该创建的信号量的个数。信号量集中最多的信号量的个数是在linux/sem.h中定义的:

#defineSEMMSL32/*<=512maxnumofsemaphoresperid*/

下面是一个打开和创建信号量集的程序:

intopen_semaphore_set(key_t keyval,int numsems)

{

intsid;

if(!numsems)

return(-1);

if((sid=semget(mykey,numsems,IPC_CREAT|0660))==-1)

{

return(-1);

}

return(sid);

}

};

==============================================================

semop()

系统调用:semop();

调用原型:int semop(int semid,struct sembuf*sops,unsign ednsops);

返回值:0,如果成功。-1,如果失败:errno=E2BIG(nsops大于最大的ops数目) EACCESS(权限不够)

EAGAIN(使用了IPC_NOWAIT,但操作不能继续进行)

EFAULT(sops指向的地址无效)

EIDRM(信号量集已经删除)

EINTR(当睡眠时接收到其他信号)

EINVAL(信号量集不存在,或者semid无效)

ENOMEM(使用了SEM_UNDO,但无足够的内存创建所需的数据结构)

ERANGE(信号量值超出范围)

第一个参数是关键字值。第二个参数是指向将要操作的数组的指针。第三个参数是数组中的操作的个数。参数sops指向由sembuf组成的数组。此数组是在linux/sem.h中定义的:/*semop systemcall takes an array of these*/

structsembuf{

ushortsem_num;/*semaphore index in array*/

shortsem_op;/*semaphore operation*/

shortsem_flg;/*operation flags*/

sem_num将要处理的信号量的个数。

sem_op要执行的操作。

sem_flg操作标志。

如果sem_op是负数,那么信号量将减去它的值。这和信号量控制的资源有关。如果没有使用IPC_NOWAIT,那么调用进程将进入睡眠状态,直到信号量控制的资源可以使用为止。如果sem_op是正数,则信号量加上它的值。这也就是进程释放信号量控制的资源。最后,如果sem_op是0,那么调用进程将调用sleep(),直到信号量的值为0。这在一个进程等待完全空闲的资源时使用。

===============================================================

semctl()

系统调用:semctl();

原型:int semctl(int semid,int semnum,int cmd,union semunarg);

返回值:如果成功,则为一个正数。

如果失败,则为-1:errno=EACCESS(权限不够)

EFAULT(arg指向的地址无效)

EIDRM(信号量集已经删除)

EINVAL(信号量集不存在,或者semid无效)

EPERM(EUID没有cmd的权利)

ERANGE(信号量值超出范围)

系统调用semctl用来执行在信号量集上的控制操作。这和在消息队列中的系统调用msgctl是十分相似的。但这两个系统调用的参数略有不同。因为信号量一般是作为一个信号量集使用的,而不是一个单独的信号量。所以在信号量集的操作中,不但要知道IPC关键字值,也要知道信号量集中的具体的信号量。这两个系统调用都使用了参数cmd,它用来指出要操作的具体命令。两个系统调用中的最后一个参数也不一样。在系统调用msgctl 中,最后一个参数是指向内核中使用的数据结构的指针。我们使用此数据结构来取得有关消息队列的一些信息,以及设置或者改变队列的存取权限和使用者。但在信号量中支持额外的可选的命令,这样就要求有一个更为复杂的数据结构。

系统调用semctl()的第一个参数是关键字值。第二个参数是信号量数目。

参数cmd中可以使用的命令如下:

·IPC_STAT读取一个信号量集的数据结构semid_ds,并将其存储在semun中的buf参数中。

·IPC_SET设置信号量集的数据结构semid_ds中的元素ipc_perm,其值取自semun中的buf参数。

·IPC_RMID将信号量集从内存中删除。

·GETALL用于读取信号量集中的所有信号量的值。

·GETNCNT返回正在等待资源的进程数目。

·GETPID返回最后一个执行semop操作的进程的PID。

·GETVAL返回信号量集中的一个单个的信号量的值。

·GETZCNT返回这在等待完全空闲的资源的进程数目。

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

·SETVAL设置信号量集中的一个单独的信号量的值。

参数arg代表一个semun的实例。semun是在linux/sem.h中定义的:

/*arg for semctl systemcalls.*/

unionsemun{

intval;/*value for SETVAL*/

structsemid_ds*buf;/*buffer for IPC_STAT&IPC_SET*/

ushort*array;/*array for GETALL&SETALL*/

structseminfo*__buf;/*buffer for IPC_INFO*/

void*__pad;

val当执行SETVAL命令时使用。buf在IPC_STAT/IPC_SET命令中使用。代表了内核中使用的信号量的数据结构。array在使用GETALL/SETALL命令时使用的指针。

下面的程序返回信号量的值。当使用GETVAL命令时,调用中的最后一个参数被忽略:intget_sem_val(intsid,intsemnum)

{

return(semctl(sid,semnum,GETVAL,0));

}

下面是一个实际应用的例子:

#defineMAX_PRINTERS5

printer_usage()

{

int x;

for(x=0;x

printf("Printer%d:%d/n/r",x,get_sem_val(sid,x));

}

下面的程序可以用来初始化一个新的信号量值:

void init_semaphore(int sid,int semnum,int initval)

{

union semunsemopts;

semopts.val=initval;

semctl(sid,semnum,SETVAL,semopts);

}

注意系统调用semctl中的最后一个参数是一个联合类型的副本,而不是一个指向联合类型的指针。

2.3.5 套接口

套接口(socket)编程是实现Linux系统和其他大多数操作系统中进程间通信的主要方式之一。我们熟知的WWW服务、FTP服务、TELNET服务等都是基于套接口编程来实现的。除了在异地的计算机进程间以外,套接口同样适用于本地同一台计算机内部的进程间通信。关于套接口的经典教材同样是Richard Stevens编著的《Unix网络编程:联网的API和套接字》,清华大学出版社出版了该书的影印版。它同样是Linux程序员的必备书籍之一。

关于这一部分的内容,可以参照本文作者的另一篇文章《设计自己的网络蚂蚁》,那里由常用的几个套接口函数的介绍和示例程序。这一部分或许是Linux进程间通信编程中最须关注和最吸引人的一部分,毕竟,Internet 正在我们身边以不可思议的速度发展着,如果一个程序员在设计编写他下一个程序的时候,根本没有考虑到网络,考虑到Internet,那么,可以说,他的设计很难成功。

3 Linux的进程和Win32的进程/线程比较

熟悉WIN32编程的人一定知道,WIN32的进程管理方式与Linux上有着很大区别,在UNIX里,只有进程的概念,但在WIN32里却还有一个"线程"的概念,那么Linux和WIN32在这里究竟有着什么区别呢?

WIN32里的进程/线程是继承自OS/2的。在WIN32里,"进程"是指一个程序,而"线程"是一个"进程"里的一个执行"线索"。从核心上讲,WIN32的多进程与Linux并无多大的区别,在WIN32里的线程才相当于Linux的进程,是一个实际正在执行的代码。但是,WIN32里同一个进程里各个线程之间是共享数据段的。这才是与Linux的进程最大的不同。

下面这段程序显示了WIN32下一个进程如何启动一个线程。

int g;

DWORD WINAPI ChildProcess( LPVOID lpParameter ){

int i;

for ( i = 1; i <1000; i ++) {

g ++;

printf( "This is Child Thread: %d/n", g );

}

ExitThread( 0 );

};

void main()

{

int threadID;

int i;

g = 0;

CreateThread( NULL, 0, ChildProcess, NULL, 0, &threadID );

for ( i = 1; i <1000; i ++) {

g ++;

printf( "This is Parent Thread: %d/n", g );

}

}

在WIN32下,使用CreateThread函数创建线程,与Linux下创建进程不同,WIN32线程不是从创建处开始运行的,而是由CreateThread指定一个函数,线程就从那个函数处开始运行。此程序同前面的UNIX程序一样,由两个线程各打印1000条信息。threadID 是子线程的线程号,另外,全局变量g是子线程与父线程共享的,这就是与Linux最大的不同之处。大家可以看出,WIN32的进程/线程要比Linux复杂,在Linux要实现类似WIN32的线程并不难,只要fork以后,让子进程调用ThreadProc函数,并且为全局变量开设共享数据区就行了,但在WIN32下就无法实现类似fork的功能了。所以现在WIN32下的C语言编译器所提供的库函数虽然已经能兼容大多数Linux/UNIX的库函数,但却仍无法实现fork。

对于多任务系统,共享数据区是必要的,但也是一个容易引起混乱的问题,在WIN32下,一个程序员很容易忘记线程之间的数据是共享的这一情况,一个线程修改过一个变量后,另一个线程却又修改了它,结果引起程序出问题。但在Linux下,由于变量本来并不共享,而由程序员来显式地指定要共享的数据,使程序变得更清晰与安全。

至于WIN32的"进程"概念,其含义则是"应用程序",也就是相当于UNIX下的exec了。

Linux也有自己的多线程函数pthread,它既不同于Linux的进程,也不同于WIN32

下的进程,关于pthread的介绍和如何在Linux环境下编写多线程程序我们将在另一篇文章《Linux下的多线程编程》中讲述。

Linux系统编程实验六进程间通信

实验六:进程间通信 实验目的: 学会进程间通信方式:无名管道,有名管道,信号,消息队列, 实验要求: (一)在父进程中创建一无名管道,并创建子进程来读该管道,父进程来写该管道(二)在进程中为SIGBUS注册处理函数,并向该进程发送SIGBUS信号(三)创建一消息队列,实现向队列中存放数据和读取数据 实验器材: 软件:安装了Linux的vmware虚拟机 硬件:PC机一台 实验步骤: (一)无名管道的使用 1、编写实验代码pipe_rw.c #include #include #include #include #include #include int main() { int pipe_fd[2];//管道返回读写文件描述符 pid_t pid; char buf_r[100]; char* p_wbuf; int r_num; memset(buf_r,0,sizeof(buf_r));//将buf_r初始化 char str1[]=”parent write1 “holle””; char str2[]=”parent write2 “pipe”\n”; r_num=30; /*创建管道*/ if(pipe(pipe_fd)<0) { printf("pipe create error\n"); return -1; } /*创建子进程*/ if((pid=fork())==0) //子进程执行代码 {

//1、子进程先关闭了管道的写端 close(pipe_fd[1]); //2、让父进程先运行,这样父进程先写子进程才有内容读sleep(2); //3、读取管道的读端,并输出数据 if(read(pipe_fd[0],buf_r, r_num)<0) { printf(“read error!”); exit(-1); } printf(“%s\n”,buf_r); //4、关闭管道的读端,并退出 close(pipe_fd[1]); } else if(pid>0) //父进程执行代码 { //1、父进程先关闭了管道的读端 close(pipe_fd[0]); //2、向管道写入字符串数据 p_wbuf=&str1; write(pipe_fd[1],p_wbuf,sizof(p_wbuf)); p_wbuf=&str2; write(pipe_fd[1],p_wbuf,sizof(p_wbuf)); //3、关闭写端,并等待子进程结束后退出 close(pipe_fd[1]); } return 0; } /*********************** #include #include #include #include #include #include int main() { int pipe_fd[2];//管道返回读写文件描述符 pid_t pid; char buf_r[100]; char* p_wbuf; int r_num;

Windows进程间各种通信方式浅谈

Windows进程间各种通信方式浅谈 1、Windows进程间通信的各种方法 进程是装入内存并准备执行的程序,每个进程都有私有的虚拟地址空间,由代码、数据以及它可利用的系统资源(如文件、管道等)组成。 多进程/多线程是Windows操作系统的一个基本特征。Microsoft Win32应用编程接口(Application Programming Interface, API) 提供了大量支持应用程序间数据共享和交换的机制,这些机制行使的活动称为进程间通信(InterProcess Communication, IPC),进程通信就是指不同进程间进行数据共享和数据交换。 正因为使用Win32 API进行进程通信方式有多种,如何选择恰当的通信方式就成为应用开发中的一个重要问题, 下面本文将对Win32中进程通信的几种方法加以分析和比较。 2、进程通信方法 2.1 文件映射 文件映射(Memory-Mapped Files)能使进程把文件内容当作进程地址区间一块内存那样来对待。因此,进程不必使用文件I/O操作,只需简单的指针操作就可读取和修改文件的内容。 Win32 API允许多个进程访问同一文件映射对象,各个进程在它自己的地址空间里接收内存的指针。通过使用这些指针,不同进程就可以读或修改文件的内容,实现了对文件中数据的共享。 应用程序有三种方法来使多个进程共享一个文件映射对象。 (1)继承:第一个进程建立文件映射对象,它的子进程继承该对象的句柄。 (2)命名文件映射:第一个进程在建立文件映射对象时可以给该对象指定一个名字(可与文件名不同)。第二个进程可通过这个名字打开此文件映射对象。另外,第一个进程也可以通过一些其它IPC机制(有名管道、邮件槽等)把名字传给第二个进程。 (3)句柄复制:第一个进程建立文件映射对象,然后通过其它IPC机制(有名管道、

Linux进程间通信(2)实验报告

实验六:Linux进程间通信(2)(4课时) 实验目的: 理解进程通信原理;掌握进程中信号量、共享内存、消息队列相关的函数的使用。实验原理: Linux下进程通信相关函数除上次实验所用的几个还有: 信号量 信号量又称为信号灯,它是用来协调不同进程间的数据对象的,而最主要的应用是前一节的共享内存方式的进程间通信。要调用的第一个函数是semget,用以获得一个信号量ID。 int semget(key_t key, int nsems, int flag); key是IPC结构的关键字,flag将来决定是创建新的信号量集合,还是引用一个现有的信号量集合。nsems是该集合中的信号量数。如果是创建新集合(一般在服务器中),则必须指定nsems;如果是引用一个现有的信号量集合(一般在客户机中)则将nsems指定为0。 semctl函数用来对信号量进行操作。 int semctl(int semid, int semnum, int cmd, union semun arg); 不同的操作是通过cmd参数来实现的,在头文件sem.h中定义了7种不同的操作,实际编程时可以参照使用。 semop函数自动执行信号量集合上的操作数组。 int semop(int semid, struct sembuf semoparray[], size_t nops); semoparray是一个指针,它指向一个信号量操作数组。nops规定该数组中操作的数量。 ftok原型如下: key_t ftok( char * fname, int id ) fname就是指定的文件名(该文件必须是存在而且可以访问的),id是子序号,虽然为int,但是只有8个比特被使用(0-255)。 当成功执行的时候,一个key_t值将会被返回,否则-1 被返回。 共享内存 共享内存是运行在同一台机器上的进程间通信最快的方式,因为数据不需要在不同的进程间复制。通常由一个进程创建一块共享内存区,其余进程对这块内存区进行读写。首先要用的函数是shmget,它获得一个共享存储标识符。 #include #include #include int shmget(key_t key, int size, int flag); 当共享内存创建后,其余进程可以调用shmat()将其连接到自身的地址空间中。 void *shmat(int shmid, void *addr, int flag); shmid为shmget函数返回的共享存储标识符,addr和flag参数决定了以什么方式来确定连接的地址,函数的返回值即是该进程数据段所连接的实际地

进程间通信的四种方式

一、剪贴板 1、基础知识 剪贴板实际上是系统维护管理的一块内存区域,当在一个进程中复制数据时,是将这个数据放到该块内存区域中,当在另一个进程中粘贴数据时,是从该内存区域中取出数据。 2、函数说明: (1)、BOOL OpenClipboard( ) CWnd类的OpenClipboard函数用于打开剪贴板。若打开剪贴板成功,则返回非0值。若其他程序或当前窗口已经打开了剪贴板,则该函数返回0值,表示打开失败。若某个程序已经打开了剪贴板,则其他应用程序将不能修改剪贴板,直到前者调用了CloseClipboard函数。 (2)、BOOL EmptyClipboard(void) EmptyClipboard函数将清空剪贴板,并释放剪贴板中数据的句柄,然后将剪贴板的所有权分配给当前打开剪贴板的窗口。 (3)、HANDLE SetClipboardData(UINT uFormat, HANDLE hMem) SetClipboardData函数是以指定的剪贴板格式向剪贴板上放置数据。uFormat指定剪贴板格式,这个格式可以是已注册的格式,或是任一种标准的剪贴板格式。CF_TEXT表示文本格式,表示每行数据以回车换行(0x0a0x0d)终止,空字符作为数据的结尾。hMem指定具有指定格式的数据的句柄。hMem参数可以是NULL,指示采用延迟提交技术,则该程序必须处理WM_RENDERFORMA T和WM_RENDERALLFORMATS消息。应用程序在调用SetClipboardData函数之后,就拥有了hMem参数所标识的数据对象,该应用程序可以读取该数据对象,但在应用程序调用CloseClipboard函数之前,它不能释放该对象的句柄,或者锁定这个句柄。若hMem标识了一个内存对象,那么这个对象必须是利用GMEM_MOVEABLE标志调用GlobalAlloc函数为其分配内存。 注意:调用SetClipboardData函数的程序必须是剪贴板的拥有者,且在这之前已经打开了剪贴板。 延迟提交技术:当一个提供数据的进程创建了剪贴板数据之后,直到其他进程获取剪贴板数据之前,这些数据都要占据内存空间。若在剪贴板上放置的数据过大,就会浪费内存空间,降低对资源的利用率。为了避免这种浪费,就可以采用延迟提交计数,也就是由数据提供进程先提供一个指定格式的空剪贴板数据块,即把SetClipboardData函数的hMem参数设置为NULL。当需要获取数据的进程想要从剪贴板上得到数据时,操作系统会向数据提供进程发送WM_RENDERFORMA T消息,而数据提供进程可以响应这个消息,并在此消息的响应函数中,再一次调用SetClipboardData函数,将实际的数据放到剪贴板上。当再次调用SetClipboardData函数时,就不再需要调用OpenClipboard函数,也不再需要调用EmptyClipboard函数。也就是说,为了提高资源利用率,避免浪费内存空间,可以采用延迟提交技术。第一次调用SetClipboardData函数时,将其hMem参数设置为NULL,在剪贴板上以指定的剪贴板格式放置一个空剪贴板数据块。然后直到有其他进程需要数据或自身进程需要终止运行时再次调用SetClipboardData函数,这时才真正提交数据。 (4)、HGLOBAL GlobalAlloc( UINT uFlags,SIZE_T dwBytes); GlobalAlloc函数从堆上分配指定数目的字节。uFlags是一个标记,用来指定分配内存的方式,uFlags为0,则该标记就是默认的GMEM_FIXED。dwBytes指定分配的字节数。

Linux进程通信实验报告

Linux进程通信实验报告 一、实验目的和要求 1.进一步了解对进程控制的系统调用方法。 2.通过进程通信设计达到了解UNIX或Linux系统中进程通信的基本原理。 二、实验内容和原理 1.实验编程,编写程序实现进程的管道通信(设定程序名为pipe.c)。使 用系统调用pipe()建立一条管道线。而父进程从则从管道中读出来自 于两个子进程的信息,显示在屏幕上。要求父进程先接受子进程P1 发来的消息,然后再接受子进程P2发来的消息。 2.可选实验,编制一段程序,使其实现进程的软中断通信(设定程序名为 softint.c)。使用系统调用fork()创建两个子进程,再用系统调用 signal()让父进程捕捉键盘上来的中断信号(即按Del键),当父进程 接受这两个软中断的其中一个后,父进程用系统调用kill()向两个子 进程分别发送整数值为16和17的软中断信号,子进程获得对应软中 断信号后分别输出相应信息后终止。 三、实验环境 一台安装了Red Hat Linux 9操作系统的计算机。 四、实验操作方法和步骤 进入Linux操作系统,利用vi编辑器将程序源代码输入并保存好,然后 打开终端对程序进行编译运行。 五、实验中遇到的问题及解决 六、实验结果及分析 基本实验 可选实验

七、源代码 Pipe.c #include"stdio.h" #include"unistd.h" main(){ int i,j,fd[2]; char S[100]; pipe(fd); if(i=fork==0){ sprintf(S,"child process 1 is sending a message \n"); write(fd[1],S,50); sleep(3); return; } if(j=fork()==0){ sprintf(S,"child process 2 is sending a message \n"); write(fd[1],S,50); sleep(3); return;

进程间通信方式比较

进程间的通信方式: 1.管道(pipe)及有名管道(named pipe): 管道可用于具有亲缘关系进程间的通信,有名管道除了具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。 2.信号(signal): 信号是在软件层次上对中断机制的一种模拟,它是比较复杂的通信方式,用于通知进程有某事件发生,一个进程收到一个信号与处理器收到一个中断请求效果上可以说是一致得。 3.消息队列(message queue): 消息队列是消息的链接表,它克服了上两种通信方式中信号量有限的缺点,具有写权限得进程可以按照一定得规则向消息队列中添加新信息;对消息队列有读权限得进程则可以从消息队列中读取信息。 消息缓冲通信技术是由Hansen首先提出的,其基本思想是:根据”生产者-消费者”原理,利用内存中公用消息缓冲区实现进程之间的信息交换. 内存中开辟了若干消息缓冲区,用以存放消息.每当一个进程向另一个进程发送消息时,便申请一个消息缓冲区,并把已准备好的消息送到缓冲区,然后把该消息缓冲区插入到接收进程的消息队列中,最后通知接收进程.接收进程收到发送里程发来的通知后,从本进程的消息队列中摘下一消息缓冲区,取出所需的信息,然后把消息缓冲区不定期给系统.系统负责管理公用消息缓冲区以及消息的传递. 一个进程可以给若干个进程发送消息,反之,一个进程可以接收不同进程发来的消息.显然,进程中关于消息队列的操作是临界区.当发送进程正往接收进程的消息队列中添加一条消息时,接收进程不能同时从该消息队列中到出消息:反之也一样. 消息缓冲区通信机制包含以下列内容:

(1) 消息缓冲区,这是一个由以下几项组成的数据结构: 1、消息长度 2、消息正文 3、发送者 4、消息队列指针 (2)消息队列首指针m-q,一般保存在PCB中。 (1)互斥信号量m-mutex,初值为1,用于互斥访问消息队列,在PCB中设置。 (2)同步信号量m-syn,初值为0,用于消息计数,在PCB中设置。(3)发送消息原语send (4)接收消息原语receive(a) 4.共享内存(shared memory): 可以说这是最有用的进程间通信方式。它使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据得更新。这种方式需要依靠某种同步操作,如互斥锁和信号量等。 这种通信模式需要解决两个问题:第一个问题是怎样提供共享内存;第二个是公共内存的互斥关系则是程序开发人员的责任。 5.信号量(semaphore): 主要作为进程之间及同一种进程的不同线程之间得同步和互斥手段。 6.套接字(socket); 这是一种更为一般得进程间通信机制,它可用于网络中不同机器之间的进程间通信,应用非常广泛。 https://www.doczj.com/doc/0613972907.html,/eroswang/archive/2007/09/04/1772350.aspx linux下的进程间通信-详解

linux进程间通讯的几种方式的特点和优缺点

1. # 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。 # 有名管道(named pipe) :有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。 # 信号量( semophore ) :信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。 # 消息队列( message queue ) :消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。 # 信号( sinal ) :信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。#共享内存( shared memory):共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的IPC方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。 # 套接字( socket ) :套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。 管道的主要局限性正体现在它的特点上: 只支持单向数据流; 只能用于具有亲缘关系的进程之间; 没有名字; 管道的缓冲区是有限的(管道制存在于内存中,在管道创建时,为缓冲区分配一个页面大小);管道所传送的是无格式字节流,这就要求管道的读出方和写入方必须事先约定好数据的格式,比如多少字节算作一个消息(或命令、或记录)等等; 2. 用于进程间通讯(IPC)的四种不同技术: 1. 消息传递(管道,FIFO,posix和system v消息队列) 2. 同步(互斥锁,条件变量,读写锁,文件和记录锁,Posix和System V信号灯) 3. 共享内存区(匿名共享内存区,有名Posix共享内存区,有名System V共享内存区) 4. 过程调用(Solaris门,Sun RPC) 消息队列和过程调用往往单独使用,也就是说它们通常提供了自己的同步机制.相反,共享内存区

进程间的通信

# 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。 # 有名管道(named pipe) :有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。 # 信号量( semophore ) :信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。 # 消息队列( message queue ) :消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。 # 信号( sinal ) :信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。# 共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。 # 套接字( socket ) :套接口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。 windows进程通信的几种方式(转) 2008-10-13 16:47 1 文件映射 文件映射(Memory-Mapped Files)能使进程把文件内容当作进程地址区间一块内存那样来对待。因此,进程不必使用文件I/O操作,只需简单的指针操作就可读取和修改文件的内容。 Win32 API允许多个进程访问同一文件映射对象,各个进程在它自己的地址空间里接收内存的指针。通过使用这些指针,不同进程就可以读或修改文件的内容,实现了对文件中数据的共享。 应用程序有三种方法来使多个进程共享一个文件映射对象。 (1)继承:第一个进程建立文件映射对象,它的子进程继承该对象的句柄。 (2)命名文件映射:第一个进程在建立文件映射对象时可以给该对象指定一个名字(可与文件名不同)。第二个进程可通过这个名字打开此文件映射对象。另外,第一个进程也可以通过一些其它IPC机制(有名管道、邮件槽等)把名字传给第二个进程。 (3)句柄复制:第一个进程建立文件映射对象,然后通过其它IPC机制(有名管道、邮件槽等)把对象句柄传递给第二个进程。第二个进程复制该句柄就取得对该文件映射对象的访问权限。 文件映射是在多个进程间共享数据的非常有效方法,有较好的安全性。但文件映射只能用于本地机器的进程之间,不能用于网络中,而开发者还必须控制进程间的同步。 2 共享内存 Win32 API中共享内存(Shared Memory)实际就是文件映射的一种特殊情况。进程在创建文件映射对象时用0xFFFFFFFF来代替文件句柄(HANDLE),就表示了对应的文件映射对象是从操作系统页面文件访问内存,其它进程打开该文件映射

Linux下的进程间通信-详解

Linux下的进程间通信-详解 详细的讲述进程间通信在这里绝对是不可能的事情,而且笔者很难有信心说自己对这一部分内容的认识达到了什么样的地步,所以在这一节的开头首先向大家推荐著 名作者Richard Stevens的著名作品:《Advanced Programming in the UNIX Environment》,它的中文译本《UNIX环境高级编程》已有机械工业出版社出版,原文精彩,译文同样地道,如果你的确对在Linux下编程有浓 厚的兴趣,那么赶紧将这本书摆到你的书桌上或计算机旁边来。说这么多实在是难抑心中的景仰之情,言归正传,在这一节里,我们将介绍进程间通信最最初步和最 最简单的一些知识和概念。 首先,进程间通信至少可以通过传送打开文件来实现,不同的进程通过一个或多个文件来传递信息,事实上,在很多应用系统里,都使用了这种方法。但一般说来, 进程间通信(IPC:InterProcess Communication)不包括这种似乎比较低级的通信方法。Unix系统中实现进程间通信的方法很多,而且不幸的是,极少方法能在所有的Unix系 统中进行移植(唯一一种是半双工的管道,这也是最原始的一种通信方式)。而Linux作为一种新兴的操作系统,几乎支持所有的Unix下常用的进程间通信 方法:管道、消息队列、共享内存、信号量、套接口等等。下面我们将逐一介绍。 2.3.1 管道 管道是进程间通信中最古老的方式,它包括无名管道和有名管道两种,前者用于父进程和子进程间的通信,后者用于运行于同一台机器上的任意两个进程间的通信。 无名管道由pipe()函数创建: #include int pipe(int filedis[2]); 参数filedis返回两个文件描述符:filedes[0]为读而打开,filedes[1]为写而打开。filedes[1]的输出是filedes[0]的输入。下面的例子示范了如何在父进程和子进程间实现通信。 #define INPUT 0 #define OUTPUT 1 void main() { int file_descriptors[2]; /*定义子进程号 */ pid_t pid; char buf[256]; 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"));

04--Linux系统编程-进程间通信

IPC方法 Linux环境下,进程地址空间相互独立,每个进程各自有不同的用户地址空间。任何一个进程的全局变量在另一个进程中都看不到,所以进程和进程之间不能相互访问,要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信(IPC,InterProcess Communication)。 在进程间完成数据传递需要借助操作系统提供特殊的方法,如:文件、管道、信号、共享内存、消息队列、套接字、命名管道等。随着计算机的蓬勃发展,一些方法由于自身设计缺陷被淘汰或者弃用。现今常用的进程间通信方式有: ①管道(使用最简单) ②信号(开销最小) ③共享映射区(无血缘关系) ④本地套接字(最稳定) 管道 管道的概念: 管道是一种最基本的IPC机制,作用于有血缘关系的进程之间,完成数据传递。调用pipe系统函数即可创建一个管道。有如下特质: 1. 其本质是一个伪文件(实为内核缓冲区) 2.由两个文件描述符引用,一个表示读端,一个表示写端。 3. 规定数据从管道的写端流入管道,从读端流出。 管道的原理: 管道实为内核使用环形队列机制,借助内核缓冲区(4k)实现。 管道的局限性: ①数据自己读不能自己写。 ②数据一旦被读走,便不在管道中存在,不可反复读取。 ③由于管道采用半双工通信方式。因此,数据只能在一个方向上流动。 ④只能在有公共祖先的进程间使用管道。

常见的通信方式有,单工通信、半双工通信、全双工通信。 pipe函数 创建管道 int pipe(int pipefd[2]); 成功:0;失败:-1,设置errno 函数调用成功返回r/w两个文件描述符。无需open,但需手动close。规定:fd[0] →r;fd[1] →w,就像0对应标准输入,1对应标准输出一样。向管道文件读写数据其实是在读写内核缓冲区。 管道创建成功以后,创建该管道的进程(父进程)同时掌握着管道的读端和写端。如何实现父子进程间通信呢?通常可以采用如下步骤: 1.父进程调用pipe函数创建管道,得到两个文件描述符fd[0]、fd[1]指向管道的读端和写端。 2.父进程调用fork创建子进程,那么子进程也有两个文件描述符指向同一管道。 3.父进程关闭管道读端,子进程关闭管道写端。父进程可以向管道中写入数据,子进程将管道中的数据读出。由于管道是利用环形队列实现的,数据从写端流入管道,从读端流出,这样就实现了进程间通信。 练习:父子进程使用管道通信,父写入字符串,子进程读出并,打印到屏幕。【pipe.c】 思考:为甚么,程序中没有使用sleep函数,但依然能保证子进程运行时一定会读到数据呢? 管道的读写行为 使用管道需要注意以下4种特殊情况(假设都是阻塞I/O操作,没有设置O_NONBLOCK标志): 1.如果所有指向管道写端的文件描述符都关闭了(管道写端引用计数为0),而仍然有进程从管道的读端读数据,那么管道中剩余的数据都被读取后,再次read会返回0,就像读到文件末尾一样。

Linux进程间通信程序设计

计算机与信息技术学院设计性实验报告 一、实验目的 (1)理解进程概念; (2)理解并掌握多进程开发模式; (3)理解并掌握Linux平台进程间数据的传送方法。 二、总体设计 (1)实验内容:编写程序实现进程的管道通信。用系统调用pipe( )建立一管道,创建两个二个子进程P1和P2分别向管道各写一句话: Message from child P1! Message from child P2! 父进程从管道中读出二个来自子进程的信息并显示。 (2)设计原理: 所谓管道,是指能够连接一个写进程和一个读进程、并允许它们进行通信的一个共享文件,又称为pipe文件。由写进程从管道的写入端(句柄1)将数据写入管道,而读进程则从管道的读出端(句柄0)读出数据。 通过管道的信息流 三、实验步骤: #include

#include #include #include # include #define BUFSIZE 100 int pid1,pid2; int main() { int fd[2]; char buf_out [BUFSIZE], buf_in [BUFSIZE]; if (pipe(fd) < 0) { printf("pipe error\n"); exit(1); } /*创建一个管道*/ if ((pid1 = fork()) < 0) /*创建子进程1*/ { printf("fork1 failure\n"); exit(1); } else if (pid1 == 0) { lockf(fd[1],1,0); strcpy(buf_out," Message from child P1!"); /*把串放入数组buf_out中*/ write(fd[1],buf_out,BUFSIZE); /*向管道写长为BUFSIZE字节的串*/ sleep(5); /*自我阻塞5秒*/ lockf(fd[1],0,0); exit(0); } else { while((pid2=fork( ))==-1); /*创建子进程2*/ if(pid2==0) { lockf(fd[1],1,0); /*互斥*/ sprintf(buf_out,"Message from child %d!",getpid()); write(fd[1], buf_out,BUFSIZE); sleep(5); lockf(fd[1],0,0); exit(0); } else { wait(0); /*同步*/ read(fd[0], buf_in,BUFSIZE); /*从管道中读长为BUFSIZE字节的串*/ printf("%s\n", buf_in); wait(0); read(fd[0], buf_in,BUFSIZE); printf("%s\n", buf_in); exit(0); } }

进程间通信的几种方式

进程间通信的几种方式 2009-06-19 23:28 (1)管道(Pipe):管道可用于具有亲缘关系进程间的通信,允许一个进程和另一个与它有共同祖先的进程之间进行通信。 (2)命名管道(named pipe):命名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。命名管道在文件系统中有对应的文件名。命名管道通过命令mkfifo或系统调用mkfifo来创建。 (3)信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身;linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数)。 (4)消息(Message)队列:消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺 (5)共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。 (6)内存映射(mapped memory):内存映射允许任何多个进程间通信,每一个使用该机制的进程通过把一个共享的文件映射到自己的进程地址空间来实现它。 (7)信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段。 (8)套接口(Socket):更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:Linux和System V的变种都支持套接字

linux管道通信(C语言)

Linux环境中管道通信的实现 ?摘要Linux系统提供了丰富的进程通信手段,如信号、信号灯、管道、共享内存、消息队列等,能有效地完成多个进程间的信息共享和数据交换。本文主要设 计了Linux环境中的管道通信,并给出了利用该技术制作程序运行进程通信的实 例。 ?关键词管道;进程通信;IPC;Motif;进程条 1 引言 Linux系统提供了丰富的进程通信手段,如信号、信号灯、管道、共享内存、消息队列等,能有效地完成多个进程间的信息共享和数据交换。管道作为最早的进程间通信机制之一,可以在进程之间提供简单的数据交换和通信功能。 2 管道技术简介 2.1 管道的概念及特点 管道分为无名管道和有名管道两种。无名管道可用于具有亲缘关系进程间的通信,如父子进程、兄弟进程。有名管道克服了管道没有名字的限制,允许无亲缘关系进程间的通信。本文应用的是无名管道通信机制。 管道具有以下特点: (1)管道是半双工的,数据只能单向流动;需要相互通信时,就要建立两个管道。 (2)只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程,有名管道则突破了这一限制)。 (3)单独构成一种独立的文件系统,并且只存在于内存中。 (4)数据的读出和写入都是单向的:一个进程向管道中写的数据被管道另一端的进程读出。写入的数据每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。 2.2 管道的创建 #include int pipe(int fd[2]) 该函数是Linux的一个系统调用,其创建的管道两端处于一个进程中间。要用其实现父子进程之间的通信则需要在由pipe()创建管道后,再由系统调用fork创建一个新的子进程,然后通过管道在这两个进程间传送数据,实现进程间的通信(同样,不是父子关系和两个进程,只要两个进程中存在亲缘关系

实验三2_Linux进程间通信

实验三2_Linux进程间通信 实验三 Linux进程间通信 一、实验目的 熟悉Linux下进程间通信机制,能够使用系统提供的各种通信机制实现并发进程间的数据交换。 二、实验题目 分别使用Linux下的共享存储区、消息、管道等通信机制,编程实现并发进程之间的相互通信。 三、背景材料 (一)需要用到的系统调用 实验可能需要用到的主要系统调用和库函数在下面列出,详细的使用方法说明通过“man 2 系统调用名”或者“man 3 函数名”命令获取。 fork() 创建一个子进程,通过返回值区分是在父进程还是子进程中执行; wait() 等待子进程执行完成; getpid() 获取当前进程id; shmget() 建立一个共享存储区; shmctl() 操纵一个共享存储区; shmat() 把一个共享存储区附接到进程内存空间; shmdt() 把一个已经附接的共享存储区从进程内存空间断开; msgget() 建立一个消息队列; msgctl() 操纵一个消息队列; msgsnd() 发送消息; msgrcv() 接收消息;

signal() 设置对信号的处理方式或处理过程; pipe() 打开管道; lockf() 锁定一个文件。 (二)使用共享存储区的示例程序 下面程序主要用来演示共享存储区的使用方法:首先要使用shmget得到共享存储区句柄(可以新建或连接已有的共享存储区,以关键字标识),然后使用shmat挂接到进程的存储空间(这样才能够访问),当使用完后,使用shmctl释放(shmctl 还可以完成一些其他功能)。这种使用逻辑也适用于消息和信号量。示例程序代码如下: #include #include #include #include #include int main(void) { int x, shmid; int *shmptr; if((shmid=shmget(IPC_PRIVATE, sizeof(int), IPC_CREAT|0666)) < 0) printf("shmget error"), exit(1); //函数原型int shmget(key_t key,int size,int shmflg); 函数用于创建(或者获取)一 key键值指定的共享内存对象,返回该对象的系统标识符:shmid;size 是创个由

进程间通讯

全国嵌入式人才培训基地
4. 进程间通信 上一页 第 30 章 进程 下一页
4. 进程间通信
请点评
每个进程各自有不同的用户地址空间, 任何一个进程的全局变量在另一个进程中 都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区, 进程 2 再从内核缓冲区把数据读走, 进程 1 把数据从用户空间拷到内核缓冲区, 内核提供的这种机制称为进程间通信(IPC,InterProcess Communication)。 如下图所示。 图 30.6. 进程间通信
4.1. 管道 请点评
管道是一种最基本的 IPC 机制,由 pipe 函数创建:
#include
int pipe(int filedes[2]);
调用 pipe 函数时在内核中开辟一块缓冲区(称为管道)用于通信,它有一个读 端一个写端, 然后通过 filedes 参数传出给用户程序两个文件描述符, filedes[0] 指向管道的读端,filedes[1]指向管道的写端(很好记,就像 0 是标准输入 1 是标准输出一样)。所以管道在用户程序看起来就像一个打开的文件,通过 read(filedes[0]);或者 write(filedes[1]);向这个文件读写数据其实是在读 写内核缓冲区。pipe 函数调用成功返回 0,调用失败返回-1。

开辟了管道之后如何实现两个进程间的通信呢?比如可以按下面的步骤通信。 图 30.7. 管道
得到两个文件描述符指向管道的两端。 1. 父进程调用 pipe 开辟管道, 2. 父进程调用 fork 创建子进程,那么子进程也有两个文件描述符指 向同一管道。 3. 父进程关闭管道读端,子进程关闭管道写端。父进程可以往管道里 写,子进程可以从管道里读,管道是用环形队列实现的,数据从写 端流入从读端流出,这样就实现了进程间通信。 例 30.7. 管道

操作系统课程设计:Linux系统管理实践与进程通信实现

操作系统课程设计:Linux系统管理实践与进程通信实现

操作系统课程设计——Linux系统管理实践与进程通信实现 班级网络10 学号 31006100 姓名 YHD 指导老师詹永照

二零一三年一月八号 一、设计内容 1、Linux系统的熟悉与常用操作命令的掌握。 2、Linux环境下进程通信的实现。(实现父母子女放水果吃水果的同步互斥问题,爸爸放苹果,女儿专等吃苹果,妈妈放橘子,儿子专等吃橘子,盘子即为缓冲区,大小为5。) 二、Linux环境介绍 1、Linux的由来与发展 Linux是一种可以在PC机上执行的类似UNIX的操作系统,是一个完全免费的操作系统。1991年,芬兰学生Linux Torvalds开发了这个操作系统的核心部分,因为是Linux改良的minix系统,故称之为Linux。 2、Linux的优点 (1)Linux具备UNIX系统的全部优点 Linux是一套PC版的UNIX系统,相对于Windows是一个十分稳定的系统,安全性好。 (2)良好的网络环境 Linux与UNIX一样,是以网络环境为基础的操作系统,具备完整的网络功能,提供在Internet或Intranet的邮件,FTP,www等各种服务。 (3)免费的资源 Linux免费的资源和公开的源代码方便了对操作系统的深入了解,给编程爱好者提供更大的发挥空间。 3、Linux的特点 1)全面的多任务,多用户和真正的32位操作系统 2)支持多种硬件,多种硬件平台

3)对应用程序使用的内存进行保护 4)按需取盘 5)共享内存页面 6)使用分页技术的虚拟内存 7)优秀的磁盘缓冲调度功能 8)动态链接共享库 9)支持伪终端设备 10)支持多个虚拟控制台 11)支持多种CPU 12)支持数字协处理器387的软件模拟 13)支持多种文件系统 14)支持POSIX的任务控制 15)软件移植性好 16)与其它UNIX系统的兼容性 17)强大的网络功能 三、常用命令介绍 1、目录操作 和DOS相似,Linux采用树型目录管理结构,由根目录(/)开始一层层将 子目录建下去,各子目录以 / 隔开。用户login后,工作目录的位置称为 home directory,由系统管理员设定。‘~’符号代表自己的home directory,例 如 ~/myfile 是指自己home目录下myfile这个文件。 Linux的通配符有三种:’*’和’?’用法与DOS相同,‘-‘代表区间内 的任一字符,如test[0-5]即代表test0,test1,……,test5的集合。 (1)显示目录文件 ls 执行格式: ls [-atFlgR] [name] (name可为文件或目录名称) 例: ls 显示出当前目录下的文件 ls -a 显示出包含隐藏文件的所有文件 ls -t 按照文件最后修改时间显示文件 ls -F 显示出当前目录下的文件及其类型

linux进程间通讯的几种方式

1.信号:(signal)是一种处理异步事件的方式。信号时比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程外,还可以发送信号给进程本身。 2.信号量:(Semaphore)进程间通信处理同步互斥的机制。是在多线程环境下使用的一种设施, 它负责协调各个线程, 以保证它们能够正确、合理的使用公共资源。 linux进程间通讯的几种方式的特点和优缺点,和适用场合 1. 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。 有名管道(named pipe) :有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。 信号量( semophore ) :信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。消息队列( message queue ) :消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。 信号( sinal ) :信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。 共享内存( shared memory):共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的IPC方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。

相关主题
文本预览
相关文档 最新文档