第一篇只是简单的介绍下一些相关知识,如果想详细了解可以看博客中的相关内容!!!


A与B虽然无法在用户空间通信,但是可以通过访问内核进行通信,访问内核中同一个对象(通过内核中的对象)


内核对象:  进程间的通信方式


2.有哪几种通信方式?


管道通信:无名管道、有名管道(文件系统中有名)


信号通信:信号(通知)通信包括:信号的发送、信号的接收和信号的处理。


IPC(Inter-Process Communication)通信:共享内存、消息队列和信号灯。


以上是单机模式下的进程通信(只有一个Linux内核)


Socket通信: 存在于一个网络中两个进程之间的通信(两个Linux内核)。


线程间通信:可以在用户空间就可以实现,可以通过全局变量通信。


一:进程通信概述


1.什么是进程间通信?


    什么是线程间通信?


    进程通信:在用户空间实现进程通信是不可能的,通过Linux内核通信


一个房子里面有两个房间,在房子里面有一个客厅,是两个房间所共有的)


2.有哪几种通信方式?


管道通信:无名管道、有名管道(文件系统中有名)


信号通信:信号(通知)通信包括:信号的发送、信号的接收和信号的处理。


IPC(Inter-Process Communication)通信:共享内存、消息队列和信号灯。


以上是单机模式下的进程通信(只有一个Linux内核)


Socket通信:存在于一个网络中两个进程之间的通信(两个Linux内核)。


3. 进程通信课程的学习思路:每一种通信方式都是基于文件IO的思想。


open:功能:创建或打开进程通信对象。函数形式不一样,有的是有多个函数完成。


write: 功能:向进程通信对象中写入内容。函数形式可能不一样。   


read:  功能:从进程通信对象中读取内容。函数形式可能不一样。


close: 功能:关闭或删除进程通信对象。形式可能不一样。


二:无名管道


通信原理:


管道文件是一个特殊的文件,是由队列来实现的。


在文件IO中创建一个文件或打开一个文件是由open函数来实现的,它不能创建管道文件。只能用pipe函数来创建管道。


函数形式:int pipe(int fd[2])


功能:创建管道,为系统调用:unistd.h


参数:就是得到的文件描述符。可见有两个文件描述符:fd[0]和fd[1],管道有一个读端fd[0]用来读和一个写端fd[1]用来写,这个规定不能变。


返回值:成功是0,出错是-1;


例1:pipe函数使用。


注意:


管道中的东西,读完了就删除了;队列


如果管道中没有东西可读,则会读阻塞。


例2:验证读阻塞。


例3:验证写阻塞:可以计算出内核开辟的管道有多大。5456   5457 


例4:实现进程通信。
无名管道的缺点:只能实现父子进程(有亲缘关系进程)之间的通信。


正由于这无名管道的缺点,对无名管道进行改进:有名管道。


所谓的有名,即文件系统中存在这个一样文件节点,每一个文件节点都有一个inode号


而且这是一个特殊的文件类型:p管道类型。


1.创建这个文件节点,不可以通过open 函数,open 函数只能创建普通文件,不能创建特殊文件(管道-mkdifo,套接字-socket,字符设备文件-mknod,块设备文件-mknod,符号链接文件-ln –s,目录文件mkdir)


2.管道文件只有inode号,不占磁盘块空间,和套接字、字符设备文件、块设备文件一样。普通文件和符号链接文件及目录文件,不仅有inode号,还占磁盘块空间。


3.  mkfifo   用来创建管道文件的节点,没有在内核中创建管道。


只有通过open 函数打开这个文件时才会在内核空间创建管道。


3.mkfifo
函数形式:int mkfifo(const char *filename,mode_t mode);


功能:创建管道文件


参数:管道文件文件名,权限,创建的文件权限仍然和umask有关系。


返回值:创建成功返回0,创建失败返回-1。


例1:mkfifo的用法。




三:信号


信号通信,其实就是内核向用户空间进程发送信号,只有内核才能发信号,用户空间进程不能发送信号。


 内核可以发送多少种信号呢?


kill  -l


命令:kill  -9 pid


信号通信的框架


信号的发送(发送信号进程):kill raise alarm


信号的接收(接收信号进程) :  pause()  sleep  while(1)


信号的处理(接收信号进程) : signal


1.信号的发送(发送信号进程)


kill :


所需头文件    #include <signal.h>


                        #include <sys/types.h>


函数原型        int kill(pid_t pid, int sig);


函数传入值 pid:    正数:要接收信号的进程的进程号
0:信号被发送到所有和pid进程在同一个进程组的进程
-1:信号发给所有的进程表中的进程(除了进程号最大的进程外)
sig:信号


函数返回值 


kill命令的程序实现实例。


raise: 发信号给自己 == kill(getpid(), sig)


所需头文件      #include <signal.h>


                          #include <sys/types.h>


函数原型          int raise(int sig);


函数传入值


函数返回值


alarm : 发送闹钟信号的函数:


alarm 与 raise 函数的比较:


相同点:让内核发送信号给当前进程


不同点:


1.alarm 只会发送SIGALARM信号


2.alarm 会让内核定时一段时间之后发送信号, raise会让内核立刻发信号


所需头文件    #include <unistd.h>


函数原型         unsigned int alarm(unsigned int seconds)


函数传入值     seconds:指定秒数


函数返回值    成功:如果调用此alarm()前,进程中已经设置了闹钟时间,则


返回上一个闹钟时间的剩余时间,否则返回0。出错:-1


常见信号


信号名    含义    默认操作


SIGHUP    该信号在用户终端连接(正常或非正常)结束时发出,通常是在终端的控制进程结束时,通知同一会话内的各个作业与控制终端不再关联。    终止


SIGINT    该信号在用户键入INTR字符(通常是Ctrl-C)时发出,终端驱动程序发送此信号并送到前台进程中的每一个进程。    终止


SIGQUIT    该信号和SIGINT类似,但由QUIT字符(通常是Ctrl-\)来控制。    终止


SIGILL    该信号在一个进程企图执行一条非法指令时(可执行文件本身出现错误,或者试图执行数据段、堆栈溢出时)发出。    终止


SIGFPE    该信号在发生致命的算术运算错误时发出。这里不仅包括浮点运算错误,还包括溢出及除数为0等其它所有的算术的错误。    终止


SIGKILL    该信号用来立即结束程序的运行,并且不能被阻塞、处理和忽略。    终止


SIGALRM    该信号当一个定时器到时的时候发出。    终止


SIGSTOP    该信号用于暂停一个进程,且不能被阻塞、处理或忽略。    暂停进程


SIGTSTP    该信号用于暂停交互进程,用户可键入SUSP字符(通常是Ctrl-Z)发出这个信号。    暂停进程


SIGCHLD    子进程改变状态时,父进程会收到这个信号    忽略


SIGABORT    该信号用于结束进程    终止


2. 信号的接收(接收信号进程)


接收信号的进程,要有什么条件:要想使接收的进程能收到信号,这个进程不能结束 :


sleep


pause:进程状态为S


while(1)


所需头文件    #include <unistd.h>


函数原型    int pause(void);


函数返回值    成功:0,出错:-1


3.信号的处理(接收信号进程)


    收到信号的进程,应该怎样处理? 处理的方式:


(1)进程的默认处理方式(内核为用户进程设置的默认处理方式)


A:忽略


B:终止进程


C: 暂停


(2)自己的处理方式:


自己处理信号的方法告诉内核,这样你的进程收到了这个信号就会采用你自己的的处理方式


所需头文件    #include <signal.h>


函数原型    void (*signal(int signum, void (*handler)(int)))(int);


函数传入值    signum:指定信号


    handler:     SIG_IGN:忽略该信号。


        SIG_DFL:采用系统默认方式处理信号。


        自定义的信号处理函数指针


函数返回值    成功:设置之前的信号处理方式      出错:-1


signal 函数有二个参数,第一个参数是一个整形变量(信号值),第二个参数是一个函数指针,是我们自己写的处理函数;


这个函数的返回值是一个函数指针。


练习:有二个进程,一个server.c  一个client.c,要求server.c负责创建有名管道文件,即server要先运行,client后运行。


四:IPC通信


IPC通信(Inter-Process Communication)


三种: 共享内存、消息队列、信号灯


这个IPC对象,肯定是存在于内核中。而且用户空间的文件系统中有没有IPC的文件类型?没有。


有名管道为什么能实现无亲缘关系的进程之间的通信?


是因为用户空间有管道这种文件类型。


IPC是不是只能用于亲缘关系进程之间的通信呢?肯定不是


它是怎样实现无亲缘关系之间的通信呢?也即你是保证用户空间的二个进程对内核中的同一个IPC对象的操作。(ftok)


IPC对象的打开或创建:  类似于open的函数呢?


IPC和文件I/O函数的比较


文件I/O    IPC


open    Msg_get               


Shm_get


Sem_get


read


write    msgsnd msgrecv


shmat shmdt


semop


close    msgctrl


shmctrl


semctrl


打开或创建一个共享内存对象,共享内核在内核是什么样子的?


一块缓存,变类似于用户空间的数组或malloc函数分配的空间一样


所需头文件    #include <sys/types.h>


#include <sys/ipc.h>


#include <sys/shm.h>


函数原型        int shmget(key_t key, int size, int shmflg);


函数参数


                        size:共享内存区大小


                        shmflg:同open函数的权限位,也可以用8进制表示法


函数返回值    成功:共享内存段标识符---ID---文件描述符    出错:-1


查看IPC对象    ipcs –m               -q    -s


删除IPC对象    ipcrm  -m  id


返回值:共享内存段标识符  IPC的ID号