linux系统编程之进程间的通信-创新互联

  进程间通信(IPC),是指在不同进程间传播或交换信息的。其常用方式:管道(无名管道和命名管道)、消息队列、共享内存、信号、Socket、Streams等。其中 Socket和Streams支持不同主机上的两个进程IPC。

平桥ssl适用于网站、小程序/APP、API接口等需要进行数据传输应用场景,ssl证书未来市场广阔!成为创新互联的ssl证书销售渠道,可以享受市场价格4-6折优惠!如果有意向欢迎电话联系或者加微信:028-86922220(备注:SSL证书合作)期待与您的合作!一、管道(无名管道)

函数原型:int pipe(int pipefd[2]);

包含头文件:#include

成功调用返回0,否则返回-1;

参数pipdfd是数组,存放文件描述符;(pipefd[0]-读,pipefd[1]-写)

特点:1.它是半双工的,具有固定的读端和写端;2.只能用于具有亲缘关系进程之间的通信(父子进程或兄弟进程);3.不是普通文件,但是可以使用read、write等,不属于系统文件(即ls指令看不到该管道),只存在于内存中。

上面代码的中我们在父进程中写内容到子进程中,并且我们让父进程等待子进程。

二、管道(命名管道FIFO)

函数原型:int mkfifo(const char *pathname, mode_t mode);

包含头文件:#include、#include

参数1:文件路径;参数2:与open函数中mode参数一样;

成功返回0,否则返回-1;

特点:1.可以在无关进程中通信;2.以一种特殊文件形式存在于系统文件中(ls看查看到);

这里要注意的是:当 open 一个FIFO时,是否设置非阻塞标志(O_NONBLOCK)的区别:

  • 若没有指定O_NONBLOCK(默认),只读 open 要阻塞到某个其他进程为写而打开此 FIFO。类似的,只写 open 要阻塞到某个其他进程为读而打开它。

  • 若指定了O_NONBLOCK,则只读 open 立即返回。而只写 open 将出错返回 -1 如果没有进程已经为读而打开该 FIFO,其errno置ENXIO。

read.c:

write.c :

read.c执行结果: 

write.c执行结果:

这里我们实现了两个无关进程的通信,fife1就相当于接口。

三、消息队列

消息队列是消息的链接表,存放在内核中,一个消息队列由一个标识符来标识;

函数原型:

1.int msgget(key_t key, int msgflg);        //创建或打开消息队列;

成功返回队列ID,否则返回-1;

2.int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);        //发送消息;

参数1:队列ID;参数2:消息相关的结构体(包括消息类型和消息内容);参数3:消息大小;参数4:取0即可(表示阻塞);

成功返回0,否则返回-1;

3.ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);    //读取消息

和msgsnd函数参数不同的是多了long msgtye,msgtye对应的就是消息类型,一般>0;

成功返回消息数据长度,否则返回-1;

4.int msgctl(int msqid, int cmd, struct msqid_ds *buf);        //控制消息队列

参数1:队列ID;参数2:对消息队列控制命令;参数3:由参数2决定,一般为NULL;

成功返回0,否则返回-1;

特点:1.消息队列是面向记录的,消息具有特定的格式和特定的优先级;2.独立于发送和接收进程,进程终止消息队列和其内容不会被删除;3.可以实现消息的随机查询;

receive.c

send.c

receivc.c运行结果:

send.c运行结果:

上面代码表示:receive进程在一直等待send进程发数据,当receive进程收到数据后给send进程回应; 

四、共享内存

共享内存是指两个或两个以上进程共享一个给定的存储区;

函数原型:

1.int shmget(key_t key, size_t size, int shmflg);        //创建或获取一个共享内存;

(当创建一个共享内存时,参数2-size需指定其大小,并且以M为单位,引用一个存在的共享内存时,size写0即可)

成功返回共享内存ID,否则返回-1;

2.void *shmat(int shmid, const void *shmaddr, int shmflg);        //连接共享内存到当前进程的地址空间;

成功连接后把共享内存区对象映射到调用进程的地址空间,随后可像本地空间一样访问;

参数1:共享内存ID;参数2:指定共享内存的空间地址(为0时由系统默认指定);参数3:与参数2相关,参赛2为0时,参数3也为0;

成功返回指向共享内存的指针(即地址),否则返回-1;

3.int shmdt(const void *shmaddr);        //端口与共享内存的连接;

注意:这并不是从系统中删除该共享内存,只是当前进程不能再访问该共享内存而已;

成功返回0,否则返回-1;

4.int shmctl(int shmid, int cmd, struct shmid_ds *buf);        //控制共享内存的相关信息;

参数1:共享内存ID;参数2:对共享内存控制命令;参数3:与参数2相关,控制一些信息;

成功返回0,否则返回-1;

特点:1.共享内存时最快的一种IPC,因为进程直接对内存进行存取;2.因为多个进程可以同时操作,因此需要进行同步;3.共享内存一般搭配信息量使用(同步信息);

shmwrite.c:

shmread.c: 

shmwrite.c运行结果: 

shmread.c运行结果:

以上代码就是使用了共享内存进行通信,大家可以发我们在shmwrite.c里面发数据时运用了sleep函数,防止共享内存提前销毁了。

五、信号

信号就是软件中断,信号提供了一种异步处理事件的方法,它允许进程和内核中断其他进程,一个信号就是一条消息,它通知进程系统发生了某一种类型的事件;

信号的处理:1.忽略信号;2.捕捉信号;3.默认动作;

忽略信号:大多数信号可以使用这个方式,注意:SIGKILL和SIGSTOP信号不能被忽略;

捕捉信号:当该信号产生时,由内核来调用用户自定义的函数来实现某一种信号的处理;

默认动作:当发生该信号时,系统会自动执行;

函数原型:

信号处理函数的注册:

1.void ( *signal(int signum, void (*handler)(int)) ) (int);或者

typedef void (*sighandler_t)(int);

1.sighandler_t signal(int signum, sighandler_t handler);        //设置某一信号对应动作(入门版)
再讲解这个函数之前我们先来补充一点知识——typedef用法之一:定义函数指针类型;

就拿上面这个函数为例:sighandler_t是一个函数指针,其有一个整形类型的形参,并且返回值为空,使用typedef之后,sighandler_t就可以来定义一个函数,其就相当于是一个类型了(和int这种一样),所有signal函数形参中handler就是一个形参为int,返回值为空的函数指针。再看signal函数,通过其原型(即void ( *signal(int signum, void (*handler)(int)) ) (int))可知signal是一个指针函数,即返回值为 指针的函数,那么这样sighandler_t signal(int signum, sighandler_t handler)就好理解了,就是signal的返回值是指针(这里是函数指针),其类型是sighandler_t(即形参为int,返回值为空的函数指针),这里要注意的是sighandler_t是signal返回值的类型,而不是signal的类型,但是是handler的类型。

参数1:信号名;参数2:信号处理函数,当信号发生时调用此函数;

成功返回处理函数的指针,否则返回SIG_ERR;

2.int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);        //检测或修改以及指定信号相关联的处理动作(高级版,可携带消息)

参数1:信号名;参数2:结构体指针(包含信号处理函数相关内容);参数3:结构体指针(用来备份);

成功返回0,否则返回-1;

信号处理发送函数:

3.int kill(pid_t pid, int sig);        //将信号发送给进程或者进程组(入门版)

参数1:进程PID;参数2:信号名;

成功返回0,否则返回-1;

4.int sigqueue(pid_t pid, int sig, const union sigval value);        //将信号发送给进程或者进组(高级版,可携带信息)

参数1:进程PID;参数2:信号名;参数3:要发送的消息内容;

成功返回0,否则返回-1;

一般signal函数搭配kill函数来使用:

signal.c:

kill.c:

一般sigaction函数搭配sigqueue函数使用:

sigaction.c:

sigqueue.c: 

sigqueue.c运行结果:

sigaction.c运行结果:

六、信号量

信号量本质上来说不属于进程间通信的方式之一,它是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据;

最简单的信号量只能取0和1变量,称为二值变量;可以取多个正整数的信号量称为通用信号量;

函数原型:

1.int semget(key_t key, int nsems, int semflg);        //创建或获取一个信号量组;

其中nsems为信号量的个数,必须指定;若为现有的信号量集则为0;

成功返回信号量集ID,否则返回-1;

2.int semop(int semid, struct sembuf *sops, unsigned nsops);        //对信号量组进行操作,改变信号量的值;

参数1:信号量集ID;参数2:结构体指针,包含信号量相关信息;参数3:信号量数组;

成功返回0,否则返回-1;

3.int semctl(int semid, int semnum, int cmd, ...);        //控制信号两个的相关信息;

参数1:信号量集ID;参数2:信号量编号;参数3;信号量的信息命令;参数4:与参数3有关;

成功返回的值与cmd有关,否则返回-1;

特点:1.信号量用于进程间同步,一般配合共享内存使用;2.信号量基于操作系统的PV操作,程序对信号量的操作都是原子操作;3.每次对信号量的PV操作不仅限于信号量值+1或-1,而且可以加减任意数;4.支持信号量组;

这个代码就通过信号量来让子进程先运行,子进程先放信号量,父进程等待信号量,一般不这样用,因为要父进程等待子进程用wait函数即可,信号量一般是搭配共享内存使用的。 

总结

1.无名管道:速度慢,容量有限,只有亲缘进程之间才能通信;

2.有名管道:速度慢,但是任何进程间都能通信;

3.消息队列:容量受系统限制,第一次读时,要考虑上次没有读完的数据问题;

4.共享内存:能够容易控制容量,速度快,但要考虑同步问题;

5.信号量:用来同步消息;

以上就是进程间通信相关内容,有不对的地方欢迎大家批评指正。

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


文章名称:linux系统编程之进程间的通信-创新互联
URL分享:http://ybzwz.com/article/dopgog.html