进程间的通信
历史:
最初的unix IPC : 管道=pipe FIFO 信号
AT&T : 系统改进以及扩充形成 system V IPC (unix单个计算机内的进程通信) : system v 消息队列 信号灯 共享内存
BSD : 基于socket的 IPC
linux 则继承以上,并加入了posix
POSIX : POSIX 表示可移植操作系统接口(Portable Operating System Interface : posix 消息队列 信号灯 共享内存
概念:
信号signal : 通知其他进程某事发生(可用于通知自己) linux 支持 signal & signaction 。 信息量少
message报文/消息队列 :消息链表,信息量大,有格式
共享内存 : 效率最高,结合信号量使用,达到进程间的同步以及互斥
进程的资源:
一段程序
内存中有进程控制块
专用的系统堆栈空间
独立的存储空间
管道:
1.半双工; 2. 父子进程间通信 int pipe(int filedes[2]); // filedes[0]读, 1写
写端关闭时,读完了,则返回0;读端关闭时,仍然写,产生SIGPIPE信
实例:进程建立一个pipe, 然后调用fork生成一个子进程。父进程关闭读,子进程关闭写端。父进程读文件的内容写入写端
子进程调用dup2,将读端复制到标准输入,并关闭该读端,然后exec使用分页程序从读中分页。
TELL_WAIT等的管道实现,建立管道,等P,C的传递
popen && pclose
fout = popen(PAGER, "w"); // 往pageer程序中写东西,
fputs(line, fout);
pclose(fout);
协同进程 coprocess
一个add程序从标准输入中读a,b,把和C写到标准输出。这些输入输出都是从父进程的两个管道获得的,add称为协同进程
setvbuf(stdin, NULL, _IOLBF, 0)
fgets的标准输入是个管道时,默认为全缓存。即有一行输入时,也不返回。若此时作为协同进程,则无法工作
setvbuf显示的使fgets等标准IO函数使用行缓存,立即返回。
或者使协同进程认为调用它的是个终端
FIFO
命名管道
mkfifo(路径,模式同open) // 创建后像文件一样可以用open, read等函数来处理,可以用stat来测试是否为FIFO类型
O_NONBLOCK 无此标志时,只读阻塞到有写进程打开此FIFO;反之,一样
用途:shell, 客服
1) shell 用FIFO以及tee 将 infile-->prog1--->tee--->fifo--->prog3
|--->prog2
mkfifo fifo1
prog3 < fifo1 &
prog1 < infile | tee fifo1 | prog2
2)
系统V IPC : 消息队列 信号量 共享存储段
IPC标示符从0增到最大
创建IPC结构时(msgget, semget, shmget),需要指定key为长整数
客户机和服务器在同一IPC结构上会合
1)服务器用IPC_PRIVATE创建一个新的IPC,并把其标示符存到文件中给客户进程
2)服务器和客户机都用一个认可的key, 如果该key已经和一个现存的IPC, 则需要删除后再创建
3)路径名 + 0~255 + ftok ==> key ,and 2)
许可权 ipc_perm , 可使用(msgctl)修改
缺点:要显示读取或者删除(msgrcv, msgctl),否则一直在系统中
消息队列
struct msqid_ds {许可权,上个消息,队列中消息个数,字节数}
msgget
msgctl(cmd // cmd: 统计,设置,删除,eg IPC_STAT
msgsnd(id, ptr, n, IPC_NOWAIT) // ptr = &struct mymsg{ long, char [512] }
msgrcv( type // 可以通过type取消息
信号量 semaphore // 实际创建的是进程或线程间通信的 信号量 的 集合
semget(key, 信号量的个数, flag==权限,创建)
semop(key, &sembuf, 数组的大小) // sembuf = {信号量的索引,op == P V 操作申请释放资源, flag==不等返回 }
semctl(key, 信号量个数,CMD,...) // ?
共享内存 mmap() vs shmget()
key = ftok(filename, flag); // 将文件名转换为一个key
shmid = shmget(key, size, flag); // 创建或得到一个共享内存的ID
p_shm = shmat(shmid, null, flag); // 将共享内存映射到进程空间中
shmdt() // 将共享内存同进程分离
shmctl() // 控制共享内存的属性等
calllog example:
(shmid = shmget((key_t)CONFIG_SHM_KEY, sizeof(call_log_file_t), IPC_CREAT | 0666)) < 0
calllogfile_p = (call_log_file_t*)shmat(shmid, NULL, 0);
比如,标准VGA 16色模式的实模式地址是A000:0000,而线性地址则是A0000。设定显存大小为0x10000,则可以如下操作
mem_fd = open( "/dev/mem", O_RDWR );
vga_mem = mmap( 0, 0x10000, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, 0xA0000 );
close( mem_fd );
然后直接对vga_mem进行访问,就可以了。
15 高级进程通信
流管道:
每端都是全双工 s_pipe(fd) // so fd[0]就可读写
socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
传送文件描述符
接受进程与发送进程共享同一个文件表项,但其对应的文件描述符不一定相同 // fd-->文件表-->V节点表(文件内容信息)
send_fd
send_err
recv_fd