linux-基础-进程通讯(一)-管道通信/信号/内存共享

来源:互联网 发布:linux命令创建多层目录 编辑:程序博客网 时间:2024/06/09 18:09

大纲:
1.管道通信
2.信号通信
3.内存共享
一.管道通讯
1.管道-概念
管道是单向的,先进先出,它把一个进程的输出和另一个进程的输入连在一起,一个进程(写进程),在管道的尾部写入数据,另一个进程(读进程)
从管道的头部读出数据
2.管道的种类-2种
匿名管道:父进程和子进程的通信 创建int pipe(int filedis[2])成功:0;失败:-1,设置errno。一旦创建成功会产生两个文件描述符fd[0] → 读; fd[1] → 写
匿名管道的调用过程
a.父进程调用pipe函数创建管道,得到两个文件描述符fd[0]、fd[1]指向管道的读端和写端。
b.父进程调用fork创建子进程,那么子进程也有两个文件描述符指向同一管道。
c.父进程关闭管道读端,子进程关闭管道写端。父进程可以向管道中写入数据,子进程将管道中的数据读出。
由于管道是利用环形队列实现的,数据从写端流入管道,从读端流出,这样就实现了进程间通信。
命名管道:同一个系统中任意两个进程的通信-FIFO
这里写图片描述
3.匿名管道-关闭
close(pipe_fd[0])
close(pipe_fd[1])
4.匿名管道-读写
匿名管道无需打开,创建就可以。
管道用于不同进程间的通信,通常先创建一个管道,再通过fork创建子进程,该子进程会继承父进程所创建管道
必须在调用fork之前调用pipe
这里写图片描述
5.命名管道(FIFO)创建

#include<sys/types.h>#include<sys/stat.h>int mkfifo(const char*pathname,mode_t mode)pathname:FIFO文件名mode:指定了文件的读写权限

6.命名管道的访问
a.打开FIFO文件,可以使用open调用来打开。mkfifo函数只是创建一个FIFO文件,要使用命名管道还是将其打开。
(这点和匿名管道不同)
打开方式有四种
open(const char *path, O_RDONLY);
open(const char *path, O_RDONLY | O_NONBLOCK);
open(const char *path, O_WRONLY);
open(const char *path, O_WRONLY | O_NONBLOCK);
注意:
1)程序不能以O_RDWR模式打开FIFO文件进行读写操作
2)传递给open调用的是FIFO的路径名,而不是正常的文件。
O_NONBLOCK—非阻塞,加上这个选项后,表示open调用是非阻塞的,如果没有这个选项,则表示open调用是阻塞的
open调用的为什么需要阻塞?对于O_RDONLY打开的FIFO文件,如果open调用是阻塞的,除非有一个进程以写方式打开同一个FIFO,否则它不会返回;
如果open调用是非阻塞的即第二个参数为O_RDONLY | O_NONBLOCK,则即使没有其他进程以写方式打开同一个FIFO文件,open调用将成功并立即返回。
对于O_WRONLY打开的FIFO文件,如果open调用是阻塞的即第二个参数为O_WRONLY,open调用将被阻塞,直到有一个进程以只读方式打开同一个FIFO
文件为止;如果open调用是非阻塞的即第二个参数为O_WRONLY | O_NONBLOCK,open总会立即返回,但如果没有其他进程以只读方式打开同一个FIFO文件,open调用将返回-1,并且FIFO也不会被打开。
二.信号通讯(signal)
1.如何产生信号
1)用户按键
2)硬件产生信号:除数为0,无效的存储访问等
3)进程用kill函数将信号发送给另一个进程
4)用户用kill命令将信号送给其他进程
2.如何处理信号–主要两种方法1)使用简单的singal函数2)使用信号集函数组
1)忽略信号
有两种信号可以忽略SIGKILL和SIGSTOP(向超级用户提供终止和停止程序的方法)
2)执行用户希望的动作 使用指定函数处理
3)执行系统默认动作
3.信号发送
1)kill和raise
kill可以向自身发送信号,也可以向其他进程发送信号
raise只能向自身发信号

#include<sys/types.h>#include<signal.h>int kill(pid_t pid,int signo)int raise(int signo)

2)alarm
设置时间值,时间到了就产生SIGALAM信号

#include<unistd.h>unsigned int alarm(unsigned int seconds)

3)pause
使进程挂起直到捕捉到一个信号

#include<unistd.h>int pause(void)-----只有执行一个信号后才可以使用

4)signal函数

#include<signal.h>void(*signal (int signo,void(*func)(int)))(int)func取值a.SIG_IGN忽略此信号b.SIG_DFL按系统的默认方式c.信号处理函数名:使用该函数处理

此函数的理解:
a.声明了一个函数signal
b.signal函数有两个参数,分别是int类型的signo和函数指针func,该指针函数其所输入的参数是int,没有返回值
c.函数signal的返回值是个函数指针,该指针所指函数,其输入参数是int,无返回值
d.func是函数指针有个int类型的参数,没有返回值

#include<unistd.h>#include<signal.h>#include<sys/types.h>#include<stdio.h>#include<fcntl.h>#include<stdlib.h>void func(int sign_no){    if(sign_no==SIGINT)        printf("i have get sigint!\n");    else if(sign_no==SIGQUIT)        printf("i have get sigquit!\n");}int main(){    printf("waitting for signal SIGINT or SIGQUIT\n");    signal(SIGINT,func);    signal(SIGQUIT,func);    pause();//使进程挂起直到捕捉到一个信号    exit(0);//程序正常退出#include<stdlib.h>}

1.编译以上程序,打印等待信号
这里写图片描述
2.查看进程–ps aux
这里写图片描述
3.传递信号–kill
这里写图片描述
4.接收到信号
这里写图片描述
三.内存共享
1.概念
被多个进程共享的一部分物理内存,一个进程向共享内存区域写入数据,共享这个内存区域的所有进程就可以立刻看到其中的内容
2.实现共享内存步骤:
1)创建共享内存,使用shmget函数
int shmget(key_t key,int size,int shmflg)
key_t shmkey 是这块共享内存的标识符。如果是父子关系的进程间通信的话,这个标识符用IPC_PRIVATE来代替。但是刚才我们的两个进程没有任何关系,就用fork()算出来一个标识符使用了。
int shmsiz 是这块内存的大小。
int flag 是这块内存的模式以及权限标识。
模式取值:
新建:IPC_CREAT
使用已开辟的内存:IPC_ALLOC,如果标识符已存在,则返回错误值:IPC_EXCL
然后将“模式” 和“权限标识”进行“或”运算,做为第三个参数。
如: IPC_CREAT | IPC_EXCL | 0666
key 共享内存键值:0/IPC_PRIVATE 当key=IPC_PRIVATE则创建新的共享内存
key=0,shmflg=IPC_PRIVATE则创建新的共享内存
创建成功则返回共享标识符,失败返回-1
2)映射共享内存,将这段创建的共享内存映射到具体进程空间去,使用shmat函数
int shmat(int shmid,char *shmaddr,int flag)
int shmid是那块共享内存的ID。
char *shmaddr是共享内存的起始地址
int flag是本进程对该内存的操作模式。如果是SHM_RDONLY的话,就是只读模式。其它的是读写模式
成功时,这个函数返回共享内存的起始地址。失败时返回-1。
3)读写
4)取消进程共享
int shmdt(char *shmaddr)
char *shmaddr是那块共享内存的起始地址。成功时返回0。失败时返回-1。
5)删除这块共享内存 shmctl()或者命令行下ipcrm
int shmctl( int shmid , int cmd , struct shmid_ds *buf );
int shmid是共享内存的ID。
int cmd是控制命令,可取值如下:
IPC_STAT 得到共享内存的状态
IPC_SET 改变共享内存的状态
IPC_RMID 删除共享内存
struct shmid_ds *buf是一个结构体指针。IPC_STAT的时候,取得的状态放在这个结构体中。如果要改变共享内存的状态,用这个结构体指定。
返回值: 成功:0,失败:-1
需要的头文件:

#include <sys/types.h>#include <sys/ipc.h>#include <sys/shm.h>

内存共享实例

#include <sys/types.h>#include <sys/ipc.h>#include <sys/shm.h>#include<unistd.h>#include<signal.h>#include<stdlib.h>#include<stdio.h>#include<errno.h>#include <memory.h>#include<string.h>int main(int argc,char **argv){    int shmid;//开辟共享内存,共享内存的id号    char *p_addr,*c_addr;    if(argc!=2){        fprintf(stderr,"Usager:%s\n",argv[0]);        exit(-1);    }    if(shmid=shmget(IPC_PRIVATE,1024,S_IRUSR|S_IWUSR)==-1){/*创建共享内存,key=IPC_PRIVATE,内存大小1024,权限是可读可写*/        fprintf(stderr,"create share memory error:%s\n",strerror(errno));        exit(-1);    }    if(fork()){//父进程--写        p_addr=shmat(shmid,0,0);/*shmget返回的共享标识符,shmaddr=0表示让系统自动帮你找地址,flag通常为0,返回映射地址*/        memset(p_addr,'\0',1024);/*memset()初始化内存设置,,对p_addr进行清除工作,将其指定为结束符'\0'*,头文件#include <memory.h>*/        strncpy(p_addr,argv[1],1024);/*strncpy()将argv[1]中数据做多1024内存的字符复制到p_addr中---写*/        wait(NULL);//释放资源,不关心终止状态        exit(0);    }else{//子进程--读        sleep(1);//防止子进程先运行        c_addr=shmat(shmid,0,0);        printf("client get %s\n",c_addr);        exit(0);    }}   

crm 释放资源的.先用ipcs 找出ID 然后用ipcrm shm ID 删除.

阅读全文
0 0