Linux 复习2

 

问:什么是信号?

答:信号是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); 

 

工作模式

 

Linux  复习2

 

上一篇:Binder IPC的权限控制


下一篇:c#获取本地IP地址