Linux进程间通信-System V IPC


System V IPC概述

System V引入了三种高级进程间的通信进制:消息队列、共享内存、信号量

IPC对象(消息队列、共享内存和信号量)存在于内核中而不是文件系统中,由用户控制释放,不像管道的释放由内核控制

IPC对象通过其标识符来引用和访问,所有IPC对象在内核空间有唯一性标志ID,在用户空间的唯一性标识符称为key


System V IPC 对象访问

# IPC对象是全局对象,可用ipcs查看
# 只显示消息队列
$ ipcs -q 
# 只显示信号量
$ ipcs -s
# 只显示共享内存
$ ipcs -m 
# 其他的参数
$ ipcs -help

System V IPC 函数头文件

#include <sys/types.h> //公共头文件,声明了key_t类型
#include <sys/ipc.h>   //公共头文件

#include <sys/msg.h>   //消息队列函数的头文件
#include <sys/sem.h>   //信号量函数的头文件
#include <sys/shm.h>   //共享内存函数的头文件

ftok()函数

// Unix系统中有个重要的概念叫做:万物皆文件。在很多IPC机制中的操作都是针对文件描述符(简称 fd)的,然而System V却不同,它没有对fd进行操作,而是针对 IPC对象的id来操作的,而这个id(标识符)又是通过key(键)来生成的。
// 三种IPC有各自的函数来生成id,但是它们所利用的key却都由函数ftok()生成,看一下函数声明:
#include <sys/types.h>
#include <sys/ipc.h>

key_t ftok(const char *pathname, int proj_id);
// 参数pathname是文件路径名(该文件必须存在,通常用当前路径名  “.”);proj_id被称作子id,自己指定一个整型。 注意如果两个进程要通过System V的IPC通信,那么它们的ftok函数的两个参数必须相同,这样才能生成同样的key,从而产出同样的id。

返回值类型key_t在<sys/types.h>中被定义,实际是一个整型(32位),该key是路径名和子id共同作用的结果。这里要用到该文件路径的stat结构。(系统中每一个文件都有其对应的stat结构)

  • key的31~24位为ftok函数第二个参数proj_id的低8位
  • key的23~16位为该文件stat结构中st_dev属性的低8位
  • key的15~0位为该文件stat结构中st_ino属性的低16位

ipc_perm结构体

// 三种IPC机制都有对应的结构体,这些结构体中有一个 共同的成员就是这个ipc_perm,用来标识IPC对象的权限
struct ipc_perm {
    key_t          __key; /* key */
    uid_t          uid;   /* 所有者的有效用户ID */
    gid_t          gid;   /* 所有者的有效组ID */
    uid_t          cuid;  /* 创造者的有效用户ID */
    gid_t          cgid;  /* 创造者的有效组ID */
    unsigned short mode;  /* 权限 */
    unsigned short __seq; /* 可忽略 */
};

IPC对象函数

分类 创建函数 控制函数 独立函数
消息队列 msgget msgctl msgsnd,msgrcv
信号量 semget semctl semop
共享内存 shmget shmctl shmat、shmdt

  • 创建函数(get函数)创建的是IPC对象的标识符(id),它们以ftok生成的键(key)为参数(以及其他参数)生成。
  • 控制函数(ctl函数)控制的是对应IPC数据结构的成员属性,从而改变IPC对象的状态。

实际上key和id都能唯一地标识一个IPC对象,但是之所以没有直接对key操作,而是拐弯对id进行操作,是因为id除了能唯一标识IPC对象之外,还包含其他信息(比如权限)。因此通过get函数生成的id,可以类比 文件描述符(fd),而get函数在功能上来说可以类比 open函数。

只能说IPC的id可以类比文件描述符fd,实际上它并不是fd的一种。不信你可以写个程序创建一个消息队列,然后进入死循环,去/proc/进程id/fd/目录下面看看有没有这个id值。fd是进程相关的,进程终止之后fd被释放,而IPC对象在进程结束之前如果没有显示的删除,那么及时进程结束了,它还独立存在。

int msgget(key_t key, int flag);
int semget(key_t key, int nsems, int flag);
int shmget(key_t key, size_t size, int flag);

这个函数都有一个flag参数(由逻辑或组成),该参数也可类比open函数的flag参数,虽然取值不尽相同。这三个函数的flag取值是一样的

通常的用法是 IPC_CREAT|IPC_EXCL ,如果不存在key则创建它,如果已存在则返回失败(EEXIST)


Linux中有三个命令是和System V的三个IPC相关的

  • ipcmk
  • ipcrm
  • ipcs

其中ipcmk命令用于创建IPC对象,来看一下它的三个主要选项:

选项 描述
-Q 创建一个消息队列
-S 创建信号量,后跟一参数指明数量
-M 创建共享内存,后跟一参数指明大小

可知创建消息队列的时候选项后面是没有参数的,而创建信号量和共享内存的时候选项后面还有一参数(用于指明数量或大小)。正好信号量和共享内存的get函数也比消息队列多一个

// CTL函数
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
int semctl(int semid, int semnum, int cmd, ...);     //有三参数和四参数两种,根据cmd的不同而不同
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

三个ctl控制函数其实是在操作三种IPC机制对应的三种数据结构:

此处要注意的是消息队列的对应的结构体名称,其前缀为msq而非msg(这个缩写有点违和,取了队列的首字母q)

  • msqid_ds
  • semid_ds
  • shmid_ds

这些结构体中有一个共同的成员就是前面提到的 ipc_perm


这三个函数都有一个cmd参数(控制参数),不同的IPC机制它们的控制参数是不一样的。但是由几个控制参数是公共的(定义在ipc.h中)。下面以消息队列为例(也适用于信号量和共享内存)

cmd 意义
IPC_SET 设置消息队列的属性。按照buf指向的结构中的值,来设置此IPC对象
IPC_STAT 读取消息队列的属性。取得此队列的msqid_ds结构,并存放在buf中
IPC_INFO (只有Linux有)返回系统级的限制,结果放在buf中
IPC_RMID 删除消息队列。只能由其创建者或超级用户(root)来删除

来源于:System V IPC概述

System V 共享内存

shmget函数

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

int shmget(key_t key, size_t size, int shmflg);
  1. key 通过 ftok 获取
  2. size 以字节为单位指定内存区大小
  3. shmflg 表示权限组合 可以是 IPC_EXCL | IPC_CREAT | 0600
  4. 调用成功后,该共享内存区会被以size的大小初始化为0,并返回一个shmid让我们操作该共享内存区

shmat函数

// 该函数可以将打开的共享内存区将其映射到调用进程的地址空间
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
  1. shmid 由 shmget返回
  2. shmaddr是一个非空指针返回值取决于shmflg(如果指定NULL,则由系统选择)
  3. shmflg如果指定了SHM_RND,则相应的共享内存区附接到由shmaddr参数指定的地址

shmdt函数

// 调用这个函数进程可以主动切断这个内存区,不过如果没有调用该函数,当进程结束时会自动切断。切断连接后,共享内存区依然存在
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);

shmctl函数

// 该函数完成对共享内存区的各种操作
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

//其中
struct shmid_ds {
    struct ipc_perm shm_perm;    /* 对应该共享内存的ipc_perm结构 */
    size_t          shm_segsz;   /* 以字节表示的共享内存区域的大小 */
    time_t          shm_atime;   /* 最近一次附加操作的时间 */
    time_t          shm_dtime;   /* 最近一次分离操作的时间 */
    time_t          shm_ctime;   /* 最近一次改变的时间 */
    pid_t           shm_cpid;    /* 创建该共享内存的进程ID */
    pid_t           shm_lpid;    /* 最近一次调用shmop函数的进程ID */
    shmatt_t        shm_nattch;  /* 当前使用该共享内存区域的进程数 */
    ...
    };

struct ipc_perm {
    key_t          _key;    /* 对象的键 */
    uid_t          uid;      /* 所有者的有效用户ID */
    gid_t          gid;      /* 所有者的有效组ID */
    uid_t          cuid;     /* 创建者的有效用户ID */
    gid_t          cgid;     /* 创建者的有效组ID */
    unsigned short mode;     /* 表示改对象的访问权限 */
    unsigned short __seq;    /* 对象的应用序号 */
};

该函数的命令

  • IPC_RMID:删除
  • IPC_SET:设置
  • IPC_STAT:获取
  • SHM_LOCK:将共享内存区域上锁
  • SHM_UNLOCK:将上锁的共享内存区域释放

共享内存区的限制

# 共享内存区最大字节数
$ cat /proc/sys/kernel/shmmax
# 最多共享内存区数
$ cat /proc/sys/kernel/shmall
# 最大共享内存区标识符数
$ cat /proc/sys/kernel/shmmni

实例

// 头文件
// shm.h
#include <sys/mman.h>
#include <sys/stat.h>        /* For mode constants */
#include <fcntl.h>           /* For O_* constants */
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <semaphore.h>
#include <time.h>
#include <assert.h>
#include <errno.h>
#include <signal.h>
#include <pthread.h>
#include <sys/ipc.h>
#include <sys/shm.h>
//shmwrite.c

#include "shm.h"
#define PATHNAME "."
int main(int argc, char **argv)
{

    int      id;
    int flag;
    char    *ptr;
    size_t  length=1024;
    key_t key;
    struct shmid_ds buff;
    key = ftok(PATHNAME,1);

    if(key<0)
    {
        printf("ftok error\r\n");
        return -1;
    }

    id = shmget(key, length,IPC_CREAT | IPC_EXCL| S_IRUSR | S_IWUSR  );
    if(id<0)
    {
        printf("errno: %s\r\n",strerror(errno));
        printf("shmget error\r\n");
        return -1;
    }
    ptr = shmat(id, NULL, 0);
    if(ptr==NULL)
    {
        printf("shmat error\r\n");
        return -1;
    }

    shmctl(id,IPC_STAT,&buff);
    int i;
    for(i=0;i<buff.shm_segsz;i++)
    {
        *ptr++ = i%256;
    }
    return 0;
}
#include "shm.h"
int main(int argc, char **argv)
{
    const char * const pathname=".";

    int      id;
    int flag;
    char    *ptr;
    size_t  length=1024;
    key_t key;
    struct shmid_ds buff;
    key = ftok(pathname,1);

    if(key<0)
    {
    printf("ftok error\r\n");
    return -1;
    }

    flag = IPC_CREAT |  0400;

    id = shmget(key, length, flag);
    if(id<0)
    {
        printf("shmget error\r\n");
        return -1;
    }
    ptr = shmat(id, NULL, 0);
    if(ptr==NULL)
    {
        printf("shmat error\r\n");
        return -1;
    }

    shmctl(id,IPC_STAT,&buff);
    int i;
    unsigned char c;
    for(i=0;i<buff.shm_segsz;i++)
    {
        c=*ptr++;
        printf("ptr[%d]=%d\r\n",i,c);
    }
    shmctl(id, IPC_RMID, NULL);
    return 0;
}

System V 消息队列

概述:消息队列可以认为是一个消息链表,System V 消息队列使用消息队列标识符标识。具有足够特权的任何进程都可以往一个队列放置一个消息,具有足够特权的任何进程都可以从一个给定队列读出一个消息。在某个进程往一个队列写入消息之前,并不需要另外某个进程在该队列上等待消息的到达。System V 消息队列是随内核持续的,只有在内核重起或者显示删除一个消息队列时,该消息队列才会真正被删除。可以将内核中的某个特定的消息队列画为一个消息链表

image-20210418175625310
// 对于系统中每个消息队列,内核维护一个msqid_ds的信息结构
struct msqid_ds
  {
    struct msqid_ds {
    struct ipc_perm msg_perm;
    struct msg *msg_first;      /* 队列中的第一条信息,未使用 */
    struct msg *msg_last;       /* 队列中的最后一条信息,未使用 */
    __kernel_time_t msg_stime;  /* 上次msgsnd时间 */
    __kernel_time_t msg_rtime;  /* 上次msgrcv时间 */
    __kernel_time_t msg_ctime;  /* 最后更改时间 */
    unsigned long  msg_lcbytes; /* Reuse junk fields for 32 bit */
    unsigned long  msg_lqbytes; /* ditto */
    unsigned short msg_cbytes;  /* 队列上的当前字节数 */
    unsigned short msg_qnum;    /* 队列中的消息数 */
    unsigned short msg_qbytes;  /* 队列中的最大字节数 */
    __kernel_ipc_pid_t msg_lspid;   /* 最后一个msgsnd的pid */
    __kernel_ipc_pid_t msg_lrpid;   /* 最后收到的pid */
};

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
// 调用返回:成功返回消息队列描述字,否则返回-1
int msgget(key_t key, int msgflg)

参数key是一个键值,由ftok获得;msgflg参数是一些标志位。该调用返回与健值key相对应的消息队列描述字。

在以下两种情况下,该调用将创建一个新的消息队列:

  • 如果没有消息队列与键值key相对应,并且msgflg中包含了IPC_CREAT标志位;
  • key参数为IPC_PRIVATE,参数key设置成常数IPC_PRIVATE并不意味着其他进程不能访问该消息队列,只意味着即将创建新的消息队列;

参数msgflg可以为以下:IPC_CREAT、IPC_EXCL、IPC_NOWAIT或三者的或结果


// 该系统调用从msgid代表的消息队列中读取一个消息,并把消息存储在msgp指向的msgbuf结构中
// msqid为消息队列描述字;消息返回后存储在msgp指向的地址,msgsz指定msgbuf的mtext成员的长度(即消息内容的长度),msgtyp为请求读取的消息类型;

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
// msgp是一个结构指针(即是该消息队列内部一个节点的结构),其结构一般为
struct msgbuf{
	long mtype;
	char mtext[1];     //其中元素个数可为任意值但是小于256;
}// 调用返回:成功返回0,否则返回-1

msgtyp为请求读取的消息类型;读消息标志msgflg可以为以下几个常值的或:

  • IPC_NOWAIT 如果没有满足条件的消息,调用立即返回,此时,errno=ENOMSG
  • IPC_EXCEPT 与msgtyp>0配合使用,返回队列中第一个类型不为msgtyp的消息
  • IPC_NOERROR 如果队列中满足条件的消息内容大于所请求的msgsz字节,则把该消息截断,截断部分将丢失

msgrcv()解除阻塞的条件有三个:

  1. 消息队列中有了满足条件的消息;
  2. msqid代表的消息队列被删除;
  3. 调用msgrcv()的进程被信号中断;

// 向msgid代表的消息队列发送一个消息,即将发送的消息存储在msgp指向的msgbuf结构中,消息的大小由msgze指定
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
// msgp是一个结构指针(即是该消息队列内部一个节点的结构),其结构一般为
struct msgbuf{
	long mtype;
	char mtext[1];     //其中元素个数可为任意值但是小于256;
}// 调用返回:成功返回0,否则返回-1

对发送消息来说,有意义的msgflg标志为IPC_NOWAIT,指明在消息队列没有足够空间容纳要发送的消息时,msgsnd是否等待。造成msgsnd()等待的条件有两种:

  • 当前消息的大小与当前消息队列中的字节数之和超过了消息队列的总容量
  • 当前消息队列的消息数(单位”个”)不小于消息队列的总容量(单位”字节数”),此时,虽然消息队列中的消息数目很多,但基本上都只有一个字节

msgsnd()解除阻塞的条件有三个:

  1. 不满足上述两个条件,即消息队列中有容纳该消息的空间;
  2. msqid代表的消息队列被删除;
  3. 调用msgsnd()的进程被信号中断;


int msgctl(int msqid, int cmd, struct msqid_ds *buf);
// 调用返回:成功返回0,否则返回-1

该系统调用对由msqid标识的消息队列执行cmd操作,共有三种cmd操作:IPC_STAT、IPC_SET 、IPC_RMID

  1. IPC_STAT:该命令用来获取消息队列信息,返回的信息存贮在buf指向的msqid结构中;
  2. IPC_SET:该命令用来设置消息队列的属性,要设置的属性存储在buf指向的msqid结构中;可设置属性包括:msg_perm.uid、msg_perm.gid、msg_perm.mode以及msg_qbytes,同时,也影响msg_ctime成员;
  3. IPC_RMID:删除msqid标识的消息队列;

实例

// server.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/msg.h>
#include<sys/ipc.h>
#include<string.h>

#define ID 1
#define MAXTEXT 64

struct mybuf{
    long mtype;
    char mtext[MAXTEXT];
};
int main()
{
    key_t key;
    int id;
    struct mybuf data;
    key=ftok(".",ID);
    if(key==-1){
        perror("ftok error");
        exit(1);
    }
    if((id=msgget(key,IPC_CREAT|0644))==-1){
        perror("msgget error");
        exit(1);
    }
    data.mtype=1;
    strncpy(data.mtext,"type 1",strlen("type 1")+1);
    if(msgsnd(id,&data,MAXTEXT,0)==-1){
        perror("msgsnd error");
        exit(1);
    }
    data.mtype=2;
    strncpy(data.mtext,"type 2",strlen("type 2")+1);
    if(msgsnd(id,&data,MAXTEXT,0)==-1){
        perror("msgsnd error");
        exit(1);
    }
    return 0;
}
// cilent
#include<stdio.h>          
#include<stdlib.h>         
#include<string.h>         
#include<unistd.h>         
#include<sys/ipc.h>        
#include<sys/msg.h>        
#define MAXTEXT 64         
#define ID 1               
struct mybuf{              
    long mtype;            
    char mtext[MAXTEXT];   
};                         
int main()                                      
{                                               
    struct mybuf rec;                           
    key_t key;                                  
    int id;                                     
    key=ftok(".",ID);                           
    if(key==-1){                                
        perror("ftok error");                   
        exit(1);                                
    }                                           
    if((id=msgget(key,IPC_EXEC|0644))==-1){              
        perror("msgget error");                 
        exit(1);                                
    }                                           
    if((msgrcv(id,&rec,MAXTEXT,2,0))==-1){      
        perror("msgrcv error");                 
        exit(1);                                
    }                                           
    printf("%s\n",rec.mtext);                   
    return 0;                                   
}                                               

server运行后

image-20210418203323877

cilent运行后

image-20210418203459841


System V 信号量

semget函数

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
  • 第一个参数key通过ftok根据特定path获取
  • 第二个参数表示信号量集里面的信号量的个数
  • 第三个参数标志位比如:semflg=O_CREAT|O_EXCL|0644

semget调用成功之后,会在内核维护一个信号结构

struct semid_ds {
    struct ipc_perm sem_perm;  //例如0644,0600,也有一些宏特别指定,不过还是数字好记
    time_t          sem_otime; 
    time_t          sem_ctime; 
    unsigned long   sem_nsems; 

    struct sem * sem_base;//这一项在man page中没有明确表示
}   //但man page提到信号量集中有这样的结构
struct sem
{
    unsigned short  semval;   /* 信号量的值 */
    unsigned short  semzcnt;  /* # waiting for zero */
    unsigned short  semncnt;  /* # waiting for increase */
    pid_t           sempid;   /* ID of process that did last op */
};
image-20210420160351727

semop函数

// 使用semget打开一个信号量后,可以对其中一个或多个信号量操作使用semop函数来执行
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
struct sembuf{
    unsigned short sem_num;  /* 指定特定信号量的操作 */
    short          sem_op;   /* 信号量的操作 */
    short          sem_flg;  /* 操作标志 */
};
int semop(int semid, struct sembuf *sops, unsigned nsops);
// sem_num指定特定信号量的操作
// semop函数通过命令执行了信号量操作
  • sem_op > 0;将值添加到semval上,对应与释放某个资源
  • sem_op = 0;希望等待到semval值变为0,如果已经是0,则立即返回,否则semzcnt+1,并线程阻塞
  • sem_op < 0;希望等待到semval值变为大于或等于|sem_op||sem_op|。这对应分配资源。如果已经满足条件,则semval减去sem_op的绝对值,否则semncnt+1并且线程投入睡眠

semctl函数

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
// 第四个参数
union semun {
    int              val;    /* Value for SETVAL */
    struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
    unsigned short  *array;  /* Array for GETALL, SETALL */
    struct seminfo  *__buf;  /* Buffer for IPC_INFO (Linux-specific) */
};
// semnum指定信号量集中的某个成员,类似于数组下标(0,1,2…直到nsems-1)
int semctl(int semid, int semnum, int cmd, ...);
命令 作用
GETVAL 返回semval
SETVAL 把semval设定为指定值
GETPID 返回sempid
GETNCNT 返回semncnt
GETZCNT 返回semzcnt
GETALL 返回所有semval值,由array指针返回
SETALL 设置所有semval
IPC_RMID 删除指定id信号量集
IPC_SET 设置uid,gid和mode
IPC_STAT 返回semid_ds结构

实例

// semsend.c,实现不断的通过共享内存的方式发送1 2 3 4 5
#include<stdio.h>
#include<stdlib.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/sem.h>
#define DATA 0
#define SPACE 1
union semun {
    int              val;    /* Value for SETVAL */
    struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
    unsigned short  *array;  /* Array for GETALL, SETALL */
    struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                (Linux-specific) */
};
int main()
{
    key_t shmkey,semkey;
    int shmid,semid;
    union semun semctla;
    char *ptr=NULL;
    shmkey=ftok(".",0);
    if(shmkey==-1){
        perror("shm ftok error");
    }
    shmid=shmget(shmkey,1,IPC_EXCL|IPC_CREAT|0644);
    if(shmid<0){
        shmid=shmget(shmkey,1,0644);
    }
    ptr=shmat(shmid,NULL,0);
    if(ptr==NULL){
        perror("shmat error");
    }
    semkey=ftok(".",1);
    if(semkey==-1){
        perror("sem ftok error");
    }
    semid=semget(semkey,2,IPC_EXCL|IPC_CREAT|0644);
    if(semid==-1){
        semid=semget(semkey,2,0644);
    }else{
        semctla.val=0;
        semctl(semid,DATA,SETVAL,semctla);
        semctla.val=1;
        semctl(semid,SPACE,SETVAL,semctla);
    }
    char *p="12345";
    struct sembuf sem;
    int i=0;
    while(1){
        sem.sem_num=SPACE;
        sem.sem_op=-1;
        sem.sem_flg=0;
        semop(semid,&sem,1);
        *ptr=p[i];
        i=(i+1)%5;
        sem.sem_num=DATA;
        sem.sem_op=1;
        sem.sem_flg=0;
        semop(semid,&sem,1);
    }                                                                                                                        
    return 0;
}
// semrev.c
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/sem.h>
#define DATA 0
#define SPACE 1
union semun {
    int              val;    /* Value for SETVAL */
    struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
    unsigned short  *array;  /* Array for GETALL, SETALL */
    struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                (Linux-specific) */
};
int main()
{
    key_t shmkey,semkey;
    int shmid,semid;
    union semun semctla;
    char *ptr=NULL;
    shmkey=ftok(".",0);
    if(shmkey==-1){
        perror("shm ftok error");
    }
    shmid=shmget(shmkey,1,IPC_EXCL|IPC_CREAT|0644);
    if(shmid<0){
        shmid=shmget(shmkey,1,0644);
    }
    ptr=shmat(shmid,NULL,0);
    if(ptr==NULL){
        perror("shmat error");
    }
    semkey=ftok(".",1);
    if(semkey==-1){
        perror("sem ftok error");
    }
    semid=semget(semkey,2,IPC_EXCL|IPC_CREAT|0644);
    if(semid==-1){
        semid=semget(semkey,2,0644);
    }else{
        semctla.val=0;
        semctl(semid,DATA,SETVAL,semctla);                                                                                 		
        semctla.val=1;
        semctl(semid,SPACE,SETVAL,semctla);
    }
    struct sembuf sem;
    while(1){
        sem.sem_num=DATA;
        sem.sem_op=-1;
        sem.sem_flg=0;
        semop(semid,&sem,1);
        write(STDOUT_FILENO,ptr,1);
        usleep(100);
        sem.sem_num=SPACE;
        sem.sem_op=1;
        sem.sem_flg=0;
        semop(semid,&sem,1);
    }
    return 0;  
}

image-20210420172509023


文章作者: dyl
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 dyl !
 上一篇
Linux扩容根分区(非lvm) Linux扩容根分区(非lvm)
本文章描述当Linux根分区空间不够的情况下,如何进行扩容,在Ubuntu的操作与centos下的操作,大同小异,本文未进行标注的表示操作都一样;不同之处本文都进行了标注,该方法来源于网络
2023-12-10 dyl
下一篇 
HTTP协议 HTTP协议
HTTP协议主要是HTTP请求和HTTP响应,HTTP请求分为:请求行、请求头信息、请求主体信息,HTTP响应分为:响应行、响应头信息、响应主体信息。该文章详细的列出了每个部分包含哪些部分
2023-12-10 dyl
  目录