进程间通信:信号量

时间:2024-05-18 07:23:37 其他范文 收藏本文 下载本文

进程间通信:信号量(精选6篇)由网友“liaofan”投稿提供,下面给大家分享进程间通信:信号量,欢迎阅读!

进程间通信:信号量

篇1:进程间通信:信号量

//main.cpp#include#include#include

#include#include#include#define _SIZE_ 10using namespace std;void EXIT(int arg)//注册信号处理函数,

进程间通信:信号量

。{ cout<<“Come Over!”<SetStr(“this is semaphore message!!”); sleep(1);//让线程写的速度慢于线程读的速度,可以 //看到互斥等待访问并不影响正常的需求, } return (void*) 0;}void* Read(void *arg){ sem *sm = (sem *)arg; while(1) { cout<GetStr.c_str()<

//下面是Makefile

<“ gtltcode=”>

篇2:Linux进程间通信(IPC)编程实践(九)System V信号量

System信号量集主要API

#include

#include

#include

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

int semctl(int semid, int semnum, int cmd, ...);

int semop(int semid, struct sembuf *sops, unsigned nsops);

semget

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

/** 示例1: 封装一个创建一个信号量集函数

该信号量集包含1个信号量;

权限为0666

**/int sem_create(key_t key){int semid = semget(key, 1, IPC_CREAT|IPC_EXCL|0666);if (semid == -1)err_exit(”sem_create error“);return semid;}

/** 示例2: 打开一个信号量集

nsems(信号量数量)可以填0,

semflg(信号量权限)也可以填0, 表示使用默认的权限打开

**/int sem_open(key_t key){int semid = semget(key, 0, 0);if (semid == -1)err_exit(”sem_open error“);return semid;}int semctl(int semid, int semnum, int cmd, ...);

union semun{int val; /* Value for SETVAL */struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */unsigned short *array; /* Array for GETALL, SETALL */struct seminfo *__buf; /* Buffer for IPC_INFO (Linux-specific)*/};

//struct semid_ds : Linux内核为System V信号量维护的数据结构struct semid_ds{struct ipc_perm sem_perm; /* Ownership and permissions */time_t sem_otime; /* Last semop time */time_t sem_ctime; /* Last change time */unsigned long sem_nsems; /* No. of semaphores in set */};

/** 示例1: 将信号量集semid中的第一个信号量的值设置成为value(SETVAL)

注意: semun联合体需要自己给出(从man-page中拷贝出来即可)

**/union semun{int val; /* Value for SETVAL */struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */unsigned short *array; /* Array for GETALL, SETALL */struct seminfo *__buf; /* Buffer for IPC_INFO(Linux-specific) */};int sem_setval(int semid, int value){union semun su;su.val = value;if (semctl(semid, 0, SETVAL, su) == -1)err_exit(”sem_setval error“);return 0;}

/** 示例2: 获取信号量集中第一个信号所关联的值(GETVAL)

注意: 此时第四个参数可以不填, 而信号量所关联的值可以通过semctl的返回值返回(the value of semval.)

**/int sem_getval(int semid){int value = semctl(semid, 0, GETVAL);if (value == -1)err_exit(”sem_getval error“);return value;return 0;}

/** 示例3: 删除一个信号量集(注意是删除整个集合)

IPC_RMID Immediately remove(立刻删除) the semaphore set, awakening all processes blocked in semop(2) calls on the set (with an error return and errno set to EIDRM)[然后唤醒所有阻塞在该信号量上的进程]. The argument semnum is ignored[忽略第二个参数].**/int sem_delete(int semid){if (semctl(semid, 0, IPC_RMID) == -1)err_exit(”sem_delete error“);return 0;}//测试代码int main(int argc,char *argv[]){int semid = sem_create(0x1234); //创建一个信号量集sem_setval(semid, 500); //设置值cout << sem_getval(semid) << endl; //获取值sleep(10);sem_delete(semid); //删除该集合} /**

示例4: 获取/设置信号量的权限

注意:一定要设定struct semid_ds结构体, 以指定使用semun的哪个字段

**/int sem_getmode(int semid){union semun su;// 注意: 下面这两行语句一定要设定.// (告诉内核使用的semun的哪个字段)struct semid_ds sd;su.buf = &sd;//if (semctl(semid, 0, IPC_STAT, su) == -1)err_exit(”sem_getmode error“);printf(”current permissions is: %o\n“, su.buf->sem_perm.mode);return 0;}int sem_setmode(int semid, char *mode){union semun su;// 注意: 下面这两行语句一定要设定.// (告诉内核使用的semun的哪个字段)struct semid_ds sd;su.buf = &sd;//sscanf(mode, ”%o“, (unsigned int *)&su.buf->sem_perm.mode);if (semctl(semid, 0, IPC_SET, su) == -1)err_exit(”sem_setmode error“);return 0;}[cpp] view plaincopy在CODE上查看代码片派生到我的代码片int semop(int semid, struct sembuf *sops, unsigned nsops);[cpp] view plaincopy在CODE上查看代码片派生到我的代码片//sembuf结构体struct sembuf{unsigned short sem_num; /*semaphore number:信号量的编号(从0开始)*/short sem_op; /* semaphore operation(+1, 0, -1) */short sem_flg; /* operation flags: 常用取值为SEM_UNDO(解释见下) */};

/** 示例: P,V操作封装**可以将sembuf的第三个参数设置为IPC_NOWAIT/0, 以查看程序的状态的变化**/int sem_P(int semid){struct sembuf sops = {0, -1, SEM_UNDO};if (semop(semid, &sops, 1) == -1)err_exit(”sem_P error“);return 0;}int sem_V(int semid){struct sembuf sops = {0, +1, SEM_UNDO};if (semop(semid, &sops, 1) == -1)err_exit(”sem_V error“);return 0;}

下面我们封装一个信号量操作函数工具,将主要的操作封装起来,可以像命令一样使用,

Linux进程间通信(IPC)编程实践(九)System V信号量

/** 信号量综合运用示例:

编译完成之后, 直接运行./semtool, 程序将打印该工具的用法;

下面的这些函数调用, 只不过是对上面所封装函数的稍稍改动, 理解起来并不困难;

**///semtool.cpp#include ”Usage.h“int main(int argc,char *argv[]){int pt = getopt(argc, argv, ”cdpvs:gfm:“);if (opt == '?')exit(EXIT_FAILURE);else if (opt == -1){usage();exit(EXIT_FAILURE);}key_t key = ftok(”.“, 's');int semid;switch (opt){case 'c':sem_create(key);break;case 'd':semid = sem_open(key);sem_delete(semid);break;case 'p':semid = sem_open(key);sem_P(semid);sem_getval(semid);break;case 'v':semid = sem_open(key);sem_V(semid);sem_getval(semid);break;case 's':semid = sem_open(key);sem_setval(semid, atoi(optarg));sem_getval(semid);break;case 'g':semid = sem_open(key);sem_getval(semid);break;case 'f':semid = sem_open(key);sem_getmode(semid);break;case 'm':semid = sem_open(key);sem_setmode(semid, argv[2]);sem_getmode(semid);break;default:break;}return 0;}

//Usage.h#ifndef USAGE_H_INCLUDED#define USAGE_H_INCLUDED#include#include #include #include#include #include#include#include #include#include #include#include #include#include #include#include #include#include #include#include #include#include #includeusing namespace std;inline void err_quit(std::string message);inline void err_exit(std::string message);void usage(){cerr << ”Usage:“ << endl;cerr << ”./semtool -c #create“ << endl;cerr << ”./semtool -d #delte“ << endl;cerr << ”./semtool -p #signal“ << endl;cerr << ”./semtool -v #wait“ << endl;cerr << ”./semtool -s #set-value“ << endl;cerr << ”./semtool -g #get-value“ << endl;cerr << ”./semtool -f #print-mode“ << endl;cerr << ”./semtool -m #set-mode“ << endl; }int sem_create(key_t key){int semid = semget(key, 1, IPC_CREAT|IPC_EXCL|0666);if (semid == -1)err_exit(”sem_create error“);return semid;}int sem_open(key_t key){int semid = semget(key, 0, 0);if (semid == -1)err_exit(”sem_open error“);return semid;}union semun{int val; /* Value for SETVAL */struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */unsigned short *array; /* Array for GETALL, SETALL */struct seminfo *__buf; /* Buffer for IPC_INFO (Linux-specific) */};int sem_getmode(int semid){union semun su;// 注意: 下面这两行语句一定要设定.// (告诉内核使用的semun的哪个字段)struct semid_ds sd;su.buf = &sd;//if (semctl(semid, 0, IPC_STAT, su) == -1)err_exit(”sem_getmode error“);printf(”current permissions is: %o\n“, su.buf->sem_perm.mode);return 0;}int sem_setmode(int semid, char *mode){union semun su;// 注意: 下面这两行语句一定要设定.// (告诉内核使用的semun的哪个字段)struct semid_ds sd;su.buf = &sd;//sscanf(mode, ”%o“, (unsigned int *)&su.buf->sem_perm.mode);if (semctl(semid, 0, IPC_SET, su) == -1)err_exit(”sem_setmode error“);return 0;}int sem_getval(int semid){int value = semctl(semid, 0, GETVAL);if (value == -1)err_exit(”sem_getval error“);cout << ”current value: “ << value << endl;return value;}int sem_setval(int semid, int value){union semun su;su.val = value;if (semctl(semid, 0, SETVAL, su) == -1)err_exit(”sem_setval error“);return 0;}int sem_delete(int semid){if (semctl(semid, 0, IPC_RMID) == -1)err_exit(”sem_delete error“);return 0;}// 为了能够打印信号量的持续变化, 因此sem_flg我们并没用SEM_UNDO// 但是我们推荐使用SEM_UNDOint sem_P(int semid){struct sembuf sops = {0, -1, 0};if (semop(semid, &sops, 1) == -1)err_exit(”sem_P error“);return 0;}int sem_V(int semid){struct sembuf sops = {0, +1, 0};if (semop(semid, &sops, 1) == -1)err_exit(”sem_V error“);return 0;}inline void err_quit(std::string message){std::cerr << message << std::endl;exit(EXIT_FAILURE);}inline void err_exit(std::string message){perror(message.c_str());exit(EXIT_FAILURE);}#endif // USAGE_H_INCLUDED

附:ftok函数

系统建立IPC通讯(如消息队列、共享内存时)必须指定一个ID值。通常情况下,该id值通过ftok函数得到。

ftok原型如下:

key_t ftok( char * fname, int id )fname就时你指定的文件名(该文件必须是存在而且可以访问的),id是子序号,虽然为int,但是只有8个比特被使用(0-255)。

返回值:

当成功执行的时候,一个key_t值将会被返回,否则 -1 被返回。

在我们获取到key之后,就可以使用该key作为某种方法的进程间通信的key值。

篇3:Linux进程间通信(IPC)编程实践(十)System V信号量

//P原语

//P(semaphore *S)

wait(semaphore *S)

{

-- S->value;

if (S->value < 0)

{

//将当前进程设置为阻塞状态

//将当前进程的PCB插入相应的阻塞队列S->list末尾

block(S->list);

}

}[

//V原语

//V(semaphore *S)

signal(semaphore *S)

{

++ S->value;

if (S->value <= 0) //表示有进程处于阻塞状态

{

//唤醒阻塞队列S->list中等待的一个进程,将其置为就绪态;

//将其插入就绪队列;

wakeup (S->list);

}

} p操作(wait):申请一个单位资源,进程进入

v操作(signal):释放一个单位资源,进程出来

使用PV操作实现进程互斥时应该注意的是:

(1)每个程序中用户实现互斥的P、V操作必须成对出现,先做P操作,进临界区,后做V操作,出临界区,若有多个分支,要认真检查其成对性。

(2)P、V操作应分别紧靠临界区的头尾部,临界区的代码应尽可能短,不能有死循环。

(3)互斥信号量的初值一般为1。

在接下来的经典题目的分析中,我们可以 将P操作看作是判断型操作,也就是if;将V操作看作是通知型的操作,这样更容易写伪代码。

(一)生产者消费者问题

生产者一消费者问题(producer-consumerproblem)是指若干进程通过有限的共享缓冲区交换数据时的缓冲区资源使用问题。假设“生产者”进程不断向共享缓冲区写人数据(即生产数据),而“消费者”进程不断从共享缓冲区读出数据(即消费数据);共享缓冲区共有n个;任何时刻只能有一个进程可对共享缓冲区进行操作。所有生产者和消费者之间要协调,以完成对共享缓冲区的操作。

生产者进程结构:

do{

wait(empty) ;

wait(mutex) ;add nextp to buffersignal(mutex) ;

signal(full) ;

}while(1) ;消费者进程结构:

do{

wait(full) ;

wait(mutex) ;

remove an item from buffer to nextp

signal(mutex) ;

signal(empty) ;

}while(1) ;

我们可把共享缓冲区中的n个缓冲块视为共享资源,生产者写人数据的缓冲块成为消费者可用资源,而消费者读出数据后的缓冲块成为生产者的可用资源。为此,可设置三个信号量:full、empty和mutex。其中:full表示有数据的缓冲块数目,初值是0;empty表示空的缓冲块数初值是n;mutex用于访问缓冲区时的互斥,初值是1。实际上,full和empty间存在如下关系:full + empty = N注意:这里每个进程中各个P操作的次序是重要的。各进程必须先检查自己对应的资源数在确信有可用资源后再申请对整个缓冲区的互斥操作;否则,先申请对整个缓冲区的互斥操后申请自己对应的缓冲块资源,就可能死锁。出现死锁的条件是,申请到对整个缓冲区的互斥操作后,才发现自己对应的缓冲块资源,这时已不可能放弃对整个缓冲区的占用。如果采用AND信号量集,相应的进入区和退出区都很简单。如生产者的进入区为Swait(empty,mutex),退出区为Ssignal(full,mutex)。

(二)读者写者问题

读者—写者问题(Readers-Writers problem)也是一个经典的并发程序设计问题,是经常出现的一种同步问题。计算机系统中的数据(文件、记录)常被多个进程共享,但其中某些进程可能只要求读数据(称为读者Reader);另一些进程则要求修改数据(称为写者Writer)。就共享数据而言,Reader和Writer是两组并发进程共享一组数据区,要求:

(1)允许多个读者同时执行读操作;

(2)不允许读者、写者同时操作;

(3)不允许多个写者同时操作。

我们使用读者优先策略解决:

int rc=0; //用于记录当前的读者数量

semaphore rc_mutex=1;//用于对共享变量rc 操作的互斥信号量

semaphore write=1; //用于保证读者和写者互斥地访问的信号量

void reader

do{

P(rc_mutex); //开始对rc共享变量进行互斥访问

rc ++; //来了一个读进程,读进程数加1

if (rc==1) P(write);//如是第一个读进程,判断是否有写进程在临界区,

//若有,读进程等待,若无,阻塞写进程

V(rc_mutex); //结束对rc共享变量的互斥访问

读文件;

P(rc_mutex); //开始对rc共享变量的互斥访问

rc--; //一个读进程读完,读进程数减1

if (rc == 0) V(write); //最后一个离开临界区的读进程需要判断是否有写进程//需要进入临界区,若有,唤醒一个写进程进临界区

V(rc_mutex);//结束对rc共享变量的互斥访问

} while(1)

void writer()

do{

P(write); //无读进程,进入写进程;若有读进程,写进程等待

写文件;

V(write);//写进程完成;判断是否有读进程需要进入临界区,

//若有,唤醒一个读进程进临界区

} while(1)

读者优先的设计思想是读进程只要看到有其它读进程正在读,就可以继续进行读;写进程必须等待所有读进程都不读时才能写,即使写进程可能比一些读进程更早提出申请。该算法只要还有一个读者在活动,就允许后续的读者进来,该策略的结果是,如果有一个稳定的读者流存在,那么这些读者将在到达后被允许进入。而写者就始终被挂起,直到没有读者为止。

(三)哲学家就餐问题

假设有五位哲学家围坐在一张圆形餐桌旁,做以下两件事情之一:吃饭,或者思考。吃东西的时候,他们就停止思考,思考的时候也停止吃东西。每两个哲学家之间有一只餐叉。因为用一只餐叉很难吃饭,所以假设哲学家必须用两只餐叉吃东西, 而且他们只能使用自己左右手边的那两只餐叉。

[cpp] view plaincopy

void philosopher(int i) /*i:哲学家编号,从0 到4*/

{

while (TRUE) {

think( ); /*哲学家正在思考*/

take_fork(i); /*取左侧的筷子*/

take_fork((i+1) % N); /*取左侧筷子;%为取模运算*/

eat( ); /*吃饭*/

put_fork(i); /*把左侧筷子放回桌子*/

put_fork((i+1) % N); /*把右侧筷子放回桌子*/

}

}

分析:假如所有的哲学家都同时拿起左侧筷子,看到右侧筷子不可用,又都放下左侧筷子, 等一会儿,又同时拿起左侧筷子,如此这般,永远重复。对于这种情况,即所有的程序都在 无限期地运行,但是都无法取得任何进展,即出现饥饿,所有哲学家都吃不上饭。

问题算法描述:

规定在拿到左侧的筷子后,先检查右面的筷子是否可用。如果不可用,则先放下左侧筷子, 等一段时间再重复整个过程。

分析:当出现以下情形,在某一个瞬间,所有的哲学家都同时启动这个算法,拿起左侧的筷子,而看到右侧筷子不可用,又都放下左侧筷子,等一会儿,又同时拿起左侧筷子……如此这样永远重复下去。对于这种情况,所有的程序都在运行,但却无法取得进展,即出现饥饿,所有的哲学家都吃不上饭。

2) 描述一种没有人饿死(永远拿不到筷子)算法。

考虑了四种实现的方式(A、B、C、D):

A:

原理:至多只允许四个哲学家同时进餐,以保证至少有一个哲学家能够进餐,最终总会释放出他所使用过的两支筷子,从而可使更多的哲学家进餐。以下将room 作为信号量,只允许4 个哲学家同时进入餐厅就餐,这样就能保证至少有一个哲学家可以就餐,而申请进入餐厅的哲学家进入room 的等待队列,根据FIFO 的原则,总会进入到餐厅就餐,因此不会出现饿死和死锁的现象。

伪码:

[cpp] view plaincopy

semaphore chopstick[5]={1,1,1,1,1};

semaphore room=4;

void philosopher(int i)

{

while(true)

{

think();

wait(room); //请求进入房间进餐

wait(chopstick[i]); //请求左手边的筷子

wait(chopstick[(i+1)%5]); //请求右手边的筷子

eat();

signal(chopstick[(i+1)%5]); //释放右手边的筷子

signal(chopstick[i]); //释放左手边的筷子

signal(room); //退出房间释放信号量room

}

}

B:

原理:仅当哲学家的左右两支筷子都可用时,才允许他拿起筷子进餐,

方法1:利用AND 型信号量机制实现:根据课程讲述,在一个原语中,将一段代码同时需要的多个临界资源,要么全部分配给它,要么一个都不分配,因此不会出现死锁的情形。当某些资源不够时阻塞调用进程;由于等待队列的存在,使得对资源的请求满足FIFO 的要求,因此不会出现饥饿的情形。

伪码:

semaphore chopstick[5]={1,1,1,1,1};

void philosopher(int I)

{

while(true)

{

think();

Swait(chopstick[(I+1)]%5,chopstick[I]);

eat();

Ssignal(chopstick[(I+1)]%5,chopstick[I]);

}

}

方法2:利用信号量的保护机制实现。通过信号量mutex对eat之前的取左侧和右侧筷子的操作进行保护,使之成为一个原子操作,这样可以防止死锁的出现。

伪码:

semaphore mutex = 1 ;

semaphore chopstick[5]={1,1,1,1,1};

void philosopher(int I)

{

while(true)

{

think();

wait(mutex);

wait(chopstick[(I+1)]%5);

wait(chopstick[I]);

signal(mutex);

eat();

signal(chopstick[(I+1)]%5);

signal(chopstick[I]);

}

}

C:

原理:规定奇数号的哲学家先拿起他左边的筷子,然后再去拿他右边的筷子;而偶数号的哲学家则相反.按此规定,将是1,2号哲学家竞争1号筷子,3,4号哲学家竞争3号筷子.即五个哲学家都竞争奇数号筷子,获得后,再去竞争偶数号筷子,最后总会有一个哲学家能获得两支筷子而进餐。而申请不到的哲学家进入阻塞等待队列,根FIFO原则,则先申请的哲学家会较先可以吃饭,因此不会出现饿死的哲学家。

伪码:

semaphore chopstick[5]={1,1,1,1,1};

void philosopher(int i)

{

while(true)

{

think();

if(i%2 == 0) //偶数哲学家,先右后左。

{

wait (chopstick[ i + 1 ] mod 5) ;

wait (chopstick[ i]) ;

eat();

signal (chopstick[ i + 1 ] mod 5) ;

signal (chopstick[ i]) ;

}

Else //奇数哲学家,先左后右。

{

wait (chopstick[ i]) ;

wait (chopstick[ i + 1 ] mod 5) ;

eat();

signal (chopstick[ i]) ;

signal (chopstick[ i + 1 ] mod 5) ;

}

}

}

D:

利用管程机制实现(最终该实现是失败的,见以下分析):

原理:不是对每只筷子设置信号量,而是对每个哲学家设置信号量。test()函数有以下作用:

a. 如果当前处理的哲学家处于饥饿状态且两侧哲学家不在吃饭状态,则当前哲学家通过test()函数试图进入吃饭状态。

b. 如果通过test()进入吃饭状态不成功,那么当前哲学家就在该信号量阻塞等待,直到其他的哲学家进程通过test()将该哲学家的状态设置为EATING。

c. 当一个哲学家进程调用put_forks()放下筷子的时候,会通过test()测试它的邻居,如果邻居处于饥饿状态,且该邻居的邻居不在吃饭状态,则该邻居进入吃饭状态。

由上所述,该算法不会出现死锁,因为一个哲学家只有在两个邻座都不在进餐时,才允许转换到进餐状态。

该算法会出现某个哲学家适终无法吃饭的情况,即当该哲学家的左右两个哲学家交替处在吃饭的状态的时候,则该哲学家始终无法进入吃饭的状态,因此不满足题目的要求。

但是该算法能够实现对于任意多位哲学家的情况都能获得最大的并行度,因此具有重要的意义。

伪码:

#define N 5 /* 哲学家人数*/

#define LEFT (i-1+N)%N /* i的左邻号码 */

#define RIGHT (i+1)%N /* i的右邻号码 */

typedef enum { THINKING, HUNGRY, EATING } phil_state; /*哲学家状态*/

monitor dp /*管程*/

{

phil_state state[N];

semaphore mutex =1;

semaphore s[N]; /*每个哲学家一个信号量,初始值为0*/

void test(int i)

{

if ( state[i] == HUNGRY &&state[LEFT(i)] != EATING && state[RIGHT(i)] != EATING )

{

state[i] = EATING;

V(s[i]);

}

}

void get_forks(int i)

{

P(mutex);

state[i] = HUNGRY;

test(i); /*试图得到两支筷子*/

V(mutex);

P(s[i]); /*得不到筷子则阻塞*/

}

void put_forks(int i)

{

P(mutex);

state[i]= THINKING;

test(LEFT(i)); /*看左邻是否进餐*/

test(RIGHT(i)); /*看右邻是否进餐*/

V(mutex);

}

}

哲学家进程如下:

void philosopher(int process)

{

while(true)

{

think();

get_forks(process);

eat();

put_forks(process);

}

}

篇4:进程间通信概述

进程是一个独立的资源分配单元,不同进程(这里所说的进程通常指的是用户进程)之间的资源是独立的,没有关联,不能在一个进程中直接访问另一个进程的资源(例如打开的文件描述符),

但是,进程不是孤立的,不同的进程需要进行信息的交互和状态的传递等,因此需要进程间通信( IPC:Inter Processes Communication )。

进程间通信的目的:

数据传输:一个进程需要将它的数据发送给另一个进程,

通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。

资源共享:多个进程之间共享同样的资源。为了做到这一点,需要内核提供互斥和同步机制。

进程控制:有些进程希望完全控制另一个进程的执行(如 Debug 进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。

Linux 操作系统支持的主要进程间通信的通信机制:

篇5:进程间通信:管道(pipe)

管道的概述

管道也叫无名管道,它是是 UNIX 系统 IPC(进程间通信) 的最古老形式,所有的 UNIX 系统都支持这种通信机制,

无名管道有如下特点:

1、半双工,数据在同一时刻只能在一个方向上流动。

2、数据只能从管道的一端写入,从另一端读出。

3、写入管道中的数据遵循先入先出的规则。

4、管道所传送的数据是无格式的,这要求管道的读出方与写入方必须事先约定好数据的格式,如多少字节算一个消息等。

5、管道不是普通的文件,不属于某个文件系统,其只存在于内存中。

6、管道在内存中对应一个缓冲区。不同的系统其大小不一定相同。

7、从管道读数据是一次性操作,数据一旦被读走,它就从管道中被抛弃,释放空间以便写更多的数据。

8、管道没有名字,只能在具有公共祖先的进程(父进程与子进程,或者两个兄弟进程,具有亲缘关系)之间使用。

对于无名管道特点的理解,我们可以类比现实生活中管子,管子的一端塞东西,管子的另一端取东西。

无名管道是一种特殊类型的文件,在应用层体现为两个打开的文件描述符。

管道的操作

所需头文件:

#include

int pipe(int filedes[2]);

功能:

创建无名管道。

参数:

filedes: 为 int 型数组的首地址,其存放了管道的文件描述符 filedes[0]、filedes[1]。

当一个管道建立时,它会创建两个文件描述符 fd[0] 和 fd[1]。其中 fd[0] 固定用于读管道,而 fd[1] 固定用于写管道。一般文件 I/O 的函数都可以用来操作管道( lseek 除外)。

返回值:

成功:0

失败:-1

下面我们写这个一个例子,子进程通过无名管道给父进程传递一个字符串数据:

#include#include#include#include#include#includeint main(int argc, char *argv[]){ int fd_pipe[2] = {0}; pid_t pid; if( pipe(fd_pipe) < 0 ){// 创建无名管道 perror(”pipe“); } pid = fork(); // 创建进程 if( pid < 0 ){ // 出错 perror(”fork“); exit(-1); } if( pid == 0 ){ // 子进程 char buf[] = ”I am sunplus“; // 往管道写端写数据 write(fd_pipe[1], buf, strlen(buf)); _exit(0); }else if( pid >0){// 父进程 wait(NULL); // 等待子进程结束,回收其资源 char str[50] = {0}; // 从管理里读数据 read(fd_pipe[0], str, sizeof(str)); printf(”str=[%s]\n“, str); // 打印数据 } return 0;}

运行结果:

管道的特点

每个管道只有一个页面作为缓冲区,该页面是按照环形缓冲区的方式来使用的。这种访问方式是典型的“生产者——消费者”模型。当“生产者”进程有大量的数据需要写时,而且每当写满一个页面就需要进行睡眠等待,等待“消费者”从管道中读走一些数据,为其腾出一些空间。相应的,如果管道中没有可读数据,“消费者” 进程就要睡眠等待,具体过程如下图所示:

默认的情况下,从管道中读写数据,最主要的特点就是阻塞问题(这一特点应该记住),当管道里没有数据,另一个进程默认用 read() 函数从管道中读数据是阻塞的。

测试代码如下:

#include#include#include#include#include#includeint main(int argc, char *argv[]){ int fd_pipe[2] = {0}; pid_t pid; if( pipe(fd_pipe) < 0 ){// 创建无名管道 perror(”pipe“); } pid = fork(); // 创建进程 if( pid < 0 ){ // 出错 perror(”fork“); exit(-1); } if( pid == 0 ){ // 子进程 _exit(0); }else if( pid >0){// 父进程 wait(NULL); // 等待子进程结束,回收其资源 char str[50] = {0}; printf(”before read\n“); // 从管道里读数据,如果管道没有数据, read()会阻塞 read(fd_pipe[0], str, sizeof(str)); printf(”after read\n“); printf(”str=[%s]\n“, str); // 打印数据 } return 0;}

运行结果:

当然,我们编程时可通过 fcntl() 函数设置文件的阻塞特性,

设置为阻塞:fcntl(fd, F_SETFL, 0);

设置为非阻塞:fcntl(fd, F_SETFL, O_NONBLOCK);

测试代码如下:

#include#include#include#include#include#include#includeint main(int argc, char *argv[]){ int fd_pipe[2] = {0}; pid_t pid; if( pipe(fd_pipe) < 0 ){// 创建无名管道 perror(”pipe“); } pid = fork(); // 创建进程 if( pid < 0 ){ // 出错 perror(”fork“); exit(-1); } if( pid == 0 ){ // 子进程 sleep(3); char buf[] = ”hello, edu“; write(fd_pipe[1], buf, strlen(buf)); // 写数据 _exit(0); }else if( pid >0){// 父进程 fcntl(fd_pipe[0], F_SETFL, O_NONBLOCK); // 非阻塞 //fcntl(fd_pipe[0], F_SETFL, 0); // 阻塞 while(1){ char str[50] = {0}; read( fd_pipe[0], str, sizeof(str) );//读数据printf(”str=[%s]\n“, str); sleep(1); } } return 0;}

运行结果:

默认的情况下,从管道中读写数据,还有如下特点(知道有这么回事就够了,不用刻意去记这些特点):

1)调用 write() 函数向管道里写数据,当缓冲区已满时 write() 也会阻塞。

测试代码如下:

#include#include#include#include#include#includeint main(int argc, char *argv[]){ int fd_pipe[2] = {0}; pid_t pid; char buf[1024] = {0}; memset(buf, 'a', sizeof(buf)); // 往管道写的内容 int i = 0; if( pipe(fd_pipe) < 0 ){// 创建无名管道 perror(”pipe“); } pid = fork(); // 创建进程 if( pid < 0 ){ // 出错 perror(”fork“); exit(-1); } if( pid == 0 ){ // 子进程 while(1){ write(fd_pipe[1], buf, sizeof(buf)); i++; printf(”i ======== %d\n“, i); } _exit(0); }else if( pid >0){// 父进程 wait(NULL); // 等待子进程结束,回收其资源 } return 0;}

运行结果:

2)通信过程中,别的进程先结束后,当前进程读端口关闭后,向管道内写数据时,write() 所在进程会(收到 SIGPIPE 信号)退出,收到 SIGPIPE 默认动作为中断当前进程。

测试代码如下:

#include#include#include#include#include#includeint main(int argc, char *argv[]){ int fd_pipe[2] = {0}; pid_t pid; if( pipe(fd_pipe) < 0 ){// 创建无名管道 perror(”pipe“); } pid = fork(); // 创建进程 if( pid < 0 ){ // 出错 perror(”fork“); exit(-1); } if( pid == 0 ){ // 子进程 //close(fd_pipe[0]); _exit(0); }else if( pid >0 ){// 父进程 wait(NULL); // 等待子进程结束,回收其资源 close(fd_pipe[0]); // 当前进程读端口关闭 char buf[50] = ”12345"; // 当前进程读端口关闭 // write()会收到 SIGPIPE 信号,默认动作为中断当前进程 write(fd_pipe[1], buf, strlen(buf)); while(1); // 阻塞 } return 0;}

运行结果:

篇6:Python进程间通信用法实例

作者:MaxOmnis 字体:[增加 减小] 类型:转载

这篇文章主要介绍了Python进程间通信用法,涉及Python通过multiprocessing模块操作进程的相关技巧,需要的朋友可以参考下

本文实例讲述了Python进程间通信用法,分享给大家供大家参考。具体如下:

#!/usr/bin/env python# -*- coding=utf-8 -*-import multiprocessingdef counsumer(input_q): while True: item = input_q.get() #处理项目 print item #此处替换为有用的工作 #发出信号通知任务完成 input_q.task_done()def producer(sequence,output_q): for item in sequence: #将项目放入队列 output_q.put(item)#建立进程if __name__ == ‘__main__‘: q = multiprocessing.JoinableQueue()#创建可连接的共享进程队列 cons_q = multiprocessing.Process(target=counsumer,args=(q,)) cons_q.daemon = True cons_q.start() sequence = [1,2,3,4] producer(sequence,q) q.join() print ‘success‘

希望本文所述对大家的Python程序设计有所帮助,

程序员操作系统笔试题

“火柴棍式”程序员笔试题

UCTD系统及其关键技术介绍

网络监听技术概览

无线通信在救援指挥决策系统中的作用论文

唯品会 java 面试

简述光纤通信技术的现状与形势论文

浅析进程“伪隐藏”技术与实现两则

光纤通信技术通信论文

百度面试Android面试题

进程间通信:信号量
《进程间通信:信号量.doc》
将本文的Word文档下载到电脑,方便收藏和打印
推荐度:
点击下载文档

【进程间通信:信号量(精选6篇)】相关文章:

单总线协议转换器在分布式测控系统中的应用2022-11-17

电力技术的现状与前景论文2024-02-06

操作系统实习报告2022-09-25

计算机四级网络工程师试题及答案2022-05-15

唯品会商业计划书2022-06-03

信息技术发展论文2022-08-04

嵌入式系统低功耗软件技术分析论文2022-11-04

无线接入技术的产生2022-06-25

网络即时通信的原理和实现论文2022-10-30

计算机答案2023-08-26

点击下载本文文档