问:什么是信号?
答:信号是linux 为了处理某些响应进程而产生的软中断事件,进程收到信号后会做出相应的反应,一般有终止信号,非法内存,硬件故障,环境切换,可以用kill -l 查看信号
进程收到信号的处理方式有3 种:默认,忽略,捕获处理,信号的来源有用户,进程,内核
问:改变信号的相应方式
答:signal函数 signal(SIGINT,SIG_IGN);// 忽略 signal(SIGINT,SIG_DFL); 默认 signal(SIGINT,func) // 调用func 函数处理
问:发送信号
答:kill(pid_t pid,int sig) (是个系统调用)(pid>0,指定接收进程的pid,)(pid==0,把信号发给当前进程组的所有进程)(pid==-1,发给系统上所有的进程(权限))(pid<-1)
进程间通信
问:进程间通信的方式有哪些?
答:管道,信号量,共享内存,消息队列,套接字
问:什么是管道?
管道是两个进程通信的一种方式,分为有名管道和匿名管道,有名管道是文件系统实现的一种机制,用于没有任何亲缘关系的进程进行通信,通过mkfifo fifo 创建,提供了一个路径名与之关联,以FIFO形式存在文件系统中,FIFO规则就是先进先出,对于读端进程,若管道阻塞打开,FIFO没有数据,则read()阻塞,若非阻塞,则立即返回-1,写端进程,若阻塞打开,FIFO数据满了,阻塞,若为非阻塞,则返回调用失败。当写端关闭,读端会返回0 就像读完一样,而读端关闭,写端收到SIGPIPE信号,写端异常
匿名管道,用于具有亲缘关系的进程,例如父子进程,他们的用户空间独立,但是内核空间共享,因此在内核空间形成一个管道pipe,由父进程创建,具有读端和写端,fd[0],fd[1],由于子进程复制了父进程的文件信息,因此也具有fd[0],fd[1],所以父进程关闭读端fd[0],子进程关闭写端fd[1]形成一个半双工,由写端流向读端,而同样具有4种情况,当读端fd[0],一直不读时,管道数据写满,则写端fd[1]阻塞,若写端不写,读端一直读,会导致读端阻塞,若写端fd[1]关闭,读端read()返回0,若读端关闭,写端会收到SIGPIPE信号 异常终止。
问:什么是信号量?
答:信号量是一种特殊的变量,本质上是一个计数器,用来保护临界资源一般取正整数,分为二进制信号量和计数信号量,存在两种操作,P (原子减一)V(原子加一)当信号量大于0时,可进入临界区。;临界区就是临界资源的代码段,而临界资源又是,同一时刻只运行一个进程访问的资源。
// 共用体的头文件
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h>
创建信号量:
int semget(key_t key,int nsems,int flags) //返回:成功返回信号集ID,出错返回-1
1)第一个参数key是长整型(唯一非零),系统建立IPC通讯 ( 消息队列、 信号量和 共享内存) 时必须指定一个ID值。通常情况下,该id值通过ftok函数得到,由内核变成标识符,要想让两个进程看到同一个信号集,只需设置key值不变就可以。
(2)第二个参数nsem指定信号量集中需要的信号量数目,它的值几乎总是1。
(3)第三个参数flag是一组标志,当想要当信号量不存在时创建一个新的信号量,可以将flag设置为IPC_CREAT与文件权限做按位或操作。
设置了IPC_CREAT标志后,即使给出的key是一个已有信号量的key,也不会产生错误。而IPC_CREAT | IPC_EXCL则可以创建一个新的,唯一的信号量,如果信号量已存在,返回一个错误。一般我们会还或上一个文件权限
初始化信号量:
int semctl(int semid, int semnum, int cmd, ...)
union semun{ int val; //使用的值 struct semid_ds *buf; //IPC_STAT、IPC_SET 使用的缓存区 unsigned short *arry; //GETALL,、SETALL 使用的数组 struct seminfo *__buf; // IPC_INFO(Linux特有) 使用的缓存区 };
1)sem_id是由semget返回的信号量标识符
(2)semnum当前信号量集的哪一个信号量
(3)cmd通常是下面两个值中的其中一个
SETVAL:用来把信号量初始化为一个已知的值。p 这个值通过union semun中的val成员设置,其作用是在信号量第一次使用前对它进行设置。
IPC_RMID:用于删除一个已经无需继续使用的信号量标识符,删除的话就不需要缺省参数,只需要三个参数即可。
改变信号量的值:
int semop(int semid, struct sembuf *sops, size_t nops);
1)nsops:进行操作信号量的个数,即sops结构变量的个数,需大于或等于1。最常见设置此值等于1,只完成对一个信号量的操作
(2)sembuf的定义如下:
struct sembuf{ short sem_num; //除非使用一组信号量,否则它为0 short sem_op; //信号量在一次操作中需要改变的数据,通常是两个数, //一个是-1,即P(等待)操作, //一个是+1,即V(发送信号)操作。 short sem_flg; //通常为SEM_UNDO,使操作系统跟踪信号量, //并在进程没有释放该信号量而终止时,操作系统释放信号量 };
test:
#include <stdio.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <sys/sem.h> union semun { int val; }; void sem_init(); void sem_p();//阻塞 原子操作 void sem_v(); void sem_destroy(); static int semid = -1; void sem_init() { semid = semget((key_t)1234,1,IPC_CREAT|IPC_EXCL|0600); if ( semid == -1 ) { semid = semget((key_t)1234,1,0600); if (semid == -1 ) { perror("semget error"); return ; } } else { union semun a; a.val = 1; if ( semctl(semid,0,SETVAL,a) == -1 ) { perror("semctl init error"); } } } void sem_p()//阻塞 原子操作 { struct sembuf buf; buf.sem_num = 0; buf.sem_op = -1;//p buf.sem_flg = SEM_UNDO; if ( semop(semid,&buf,1) == -1 ) { perror("semop p error"); } } void sem_v() { struct sembuf buf; buf.sem_num = 0; buf.sem_op = 1;//v buf.sem_flg = SEM_UNDO; if ( semop(semid,&buf,1) == -1 ) { perror("semop p error"); } } void sem_destroy() { if ( semctl(semid,0,IPC_RMID) == -1 ) { perror("semctl error del"); } }
问:什么是进程同步?
进程同步是一种制约的关系,在执行次序上进行协调,例如输入进程A通过单缓冲向进程B提供数据。当该缓冲区空时,进程B不能获得所需数据而阻塞,一旦进程A将数据送入缓冲区,进程B被唤醒。反之,当缓冲区满时,进程A被阻塞,仅当进程B取走缓冲数据时,才唤醒进程A。
问:什么是共享内存?
答:允许两个或者多个进程共享一片存储区,当一个进程改变了该区域的数据,其他进程立马察觉,数据直接写到内存,是最快的一种IPC方式。但是没有任何同步或者互斥操作,需要添加信号量来控制对存储的控制。
也就是不同进程的PCB中的mm_struct 页目录表映射在一块相同的物理内存中
头文件:
#include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h>
创建共享内存:
int shmget(key_t key, size_t size, int shmflg); //成功返回共享内存的ID,出错返回-1
(1)第一个参数key是长整型(唯一非零),系统建立IPC通讯 ( 消息队列、 信号量和 共享内存) 时必须指定一个ID值。通常情况下,该id值通过ftok函数得到,由内核变成标识符,要想让两个进程看到同一个信号集,只需设置key值不变就可以。
(2)第二个参数size指定共享内存的大小,它的值一般为一页大小的整数倍(未到一页,操作系统向上对齐到一页,但是用户实际能使用只有自己所申请的大小)。
(3)第三个参数shmflg是一组标志,创建一个新的共享内存,将shmflg 设置了IPC_CREAT标志后,共享内存存在就打开。而IPC_CREAT | IPC_EXCL则可以创建一个新的,唯一的共享内存,如果共享内存已存在,返回一个错误。一般我们会还或上一个文件权限
操作共享内存:
int shmget(key_t key, size_t size, int shmflg); //成功返回共享内存的ID,出错返回-1
(1)第一个参数,shm_id是shmget函数返回的共享内存标识符。
(2)第二个参数,cmd是要采取的操作,它可以取下面的三个值 :
IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。
IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值
IPC_RMID:删除共享内存段
(3)第三个参数,buf是一个结构指针,它指向共享内存模式和访问权限的结构。 shmid_ds结构至少包括以下成员
struct shmid_ds { uid_t shm_perm.uid; uid_t shm_perm.gid; mode_t shm_perm.mode; };
映射共享内存:
void *shmat(int shm_id, const void *shm_addr, int shmflg);
(1)第一个参数,shm_id是由shmget函数返回的共享内存标识。
(2)第二个参数,shm_addr指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址。
(3)第三个参数,shm_flg是一组标志位,通常为0
断开共享内存:
int shmdt(const void *shmaddr);
addr参数是以前调用shmat时的返回值
问:什么是消息队列?
答:消息队列是一个消息链接表,存放在内核中,由消息队列标识符表示,可以选择性的接收消息
创建访问消息队列:
int msgget(key_t key, int msgflg); //函数原型
int msgsend(int msgid, const void *msg_ptr, size_t msg_sz, int msgflg);
msg_ptr:指向准备发送消息的结构体指针,但是消息的数据结构却有一定的要求,指针msg_ptr所指向的消息结构一定要是以一个长整型成员变量开始的结构体,接收函数将用这个成员来确定接收的消息来自哪一个类型(频道)的消息(节目),其他字段不限。消息结构定义: struct my_message{ long int message_type; //频道(你可以理解为消息通道号) /* The data you wish to transfer*/ //消息主体 };
int msgrcv(int msgid, void *msg_ptr, size_t msg_st, long int msgtype, int msgflg);
int msgctl(int msgid, int command, struct msgid_ds *buf);
工作模式