进程间通信

进程间通信

进程间通信(IPC,InterProcess Communication)是指在不同进程之间传播或交换信息。

IPC的方式通常有管道(包括无名管道和命名管道)、消息队列、信号量、共享存储、Socket、Streams等。其中 Socket和Streams支持不同主机上的两个进程IPC。

1. 管道(无名管道)

管道,通常指无名管道,是 UNIX 系统IPC最古老的形式。

特点:#

  1. 它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。
  2. 它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)。
  3. 它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。

原型:#

1 #include <unistd.h>
2 int pipe(int fd[2]);    // 返回值:若成功返回0,失败返回-1

当一个管道建立时,它会创建两个文件描述符:fd[0]为读而打开,fd[1]为写而打开。如下图:

img

要关闭管道只需将这两个文件描述符关闭即可。

例子#

若要数据流从父进程流向子进程,则关闭父进程的读端(fd[0])与子进程的写端(fd[1]);反之,则可以使数据流从子进程流向父进程。

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main()
{
	int fd[2];
	int pid;
	char buf[128];

//	int pipe(int pipefd[2]);

	if(pipe(fd) == -1){
		printf("creat pipe failed\n");
	}
	pid = fork();
	
	if(pid<0){
		printf("creat child failed\n");
	}
	else if(pid > 0){
		sleep(3);
		printf("this is father\n");
		close(fd[0]);
		write(fd[1],"hello from father",strlen("hello form father"));
		wait();
	}else{
		printf("this is child\n");
		close(fd[1]);
		read(fd[0],buf,128);
		printf("read from father: %s\n",buf);
		exit(0);
	}
	return 0;
}

fork()编程实战 参考博客#

一个现有进程 可以调用fork函数创建一个新进程。

#include <unistd.h>

Pid_t fork(void);

​ 返回值:子进程中返回0,父进程中返回子进程ID,出错返回-1

由fork创建的新进程被称为子进程。fork函数被调用一次,但返回两次。两次返回的唯一区别是子进程的返回值是0,而父进程的返回值则是新子进程的进程ID。将子进程ID返回给父进程的理由是:因为一个进程的子进程可以有多个,并且没有一个函数使一个进程可以获得其所有子进程的进程ID。fork使子进程得到返回值0的理由是:一个进程只会有一个父进程,所以子进程总是可以调用getpid以获得其父进程的进程ID(进程ID 0总是由内核交换进程使用,所以一个子进程的进程ID不可能为0)。

子进程和父进程继续执行fork调用之后的指令。子进程是父进程的副本。例如,子进程获得父进程数据空间、堆和栈的副本。注意,这是子进程所拥有的副本。父、子进程并不共享这些存储空间部分。父、子进程共享正文段。

由于在fork之后经常跟随着exec,所以现在的很多实现并不执行一个父进程、栈和堆的完全复制。作为替代,使用了写时复制(Copy-On-Write,COW)技术。这些区域由父、子进程共享,而且内核将它们的访问权限改变为只读的。如果父、子进程中的任一个试图修改这些区域,则内核只为修改区域的那块内存制作一个副本,通常是虚拟存储器系统中的一“页”。

  • pid_t fork(void);//pid_t为int类型,进行了重载
  • pid_t getpid();// 获取当前进程的 pid 值。
  • pid_t getppid(); //获取当前进程的父进程 pid 值

fork()特性#

fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:

在父进程中,fork返回新创建子进程的进程ID;
在子进程中,fork返回0;
如果出现错误,fork返回一个负值;
因此我们可以通过fork返回的值来判断当前进程是子进程还是父进程。(注: fork 调用生成的新进程与其父进程谁先执行不一定,哪个进程先执行要看系统的进程调度策略)

fork创建一个子进程的目的:#

1、一个父进程希望复制自己,使父、子进程同时执行不同的代码段。这在网络服务进程

中是常见的——父进程等待客户端的服务请求。当这种请求到达时,父进程调用fork,使子进程处理此请求。父进程则继续等待下一个服务请求到达。

2、一个进程要执行一个不同的程序。这对shell是常见的情况。在这种情况下,子进程从fork返回后立即调用exec。

一个现有进程 可以调用fork函数创建一个新进程。

#include <unistd.h>

Pid_t fork(void);

​ 返回值:子进程中返回0,父进程中返回子进程ID,出错返回-1

由fork创建的新进程被称为子进程。fork函数被调用一次,但返回两次。两次返回的唯一区别是子进程的返回值是0,而父进程的返回值则是新子进程的进程ID。将子进程ID返回给父进程的理由是:因为一个进程的子进程可以有多个,并且没有一个函数使一个进程可以获得其所有子进程的进程ID。fork使子进程得到返回值0的理由是:一个进程只会有一个父进程,所以子进程总是可以调用getpid以获得其父进程的进程ID(进程ID 0总是由内核交换进程使用,所以一个子进程的进程ID不可能为0)。

子进程和父进程继续执行fork调用之后的指令。子进程是父进程的副本。例如,子进程获得父进程数据空间、堆和栈的副本。注意,这是子进程所拥有的副本。父、子进程并不共享这些存储空间部分。父、子进程共享正文段。

由于在fork之后经常跟随着exec,所以现在的很多实现并不执行一个父进程、栈和堆的完全复制。作为替代,使用了写时复制(Copy-On-Write,COW)技术。这些区域由父、子进程共享,而且内核将它们的访问权限改变为只读的。如果父、子进程中的任一个试图修改这些区域,则内核只为修改区域的那块内存制作一个副本,通常是虚拟存储器系统中的一“页”。

vfork函数#

也可以创建进程,与fork的区别:

1、vfork直接使用父进程的存储空间,不拷贝

2、vfork保证子进程先运行,当子进程调用exit退出后,父进程才执行

2. FIFO(命名管道)

FIFO,也称为命名管道,它是一种文件类型。

  1. FIFO可以在无关的进程之间交换数据,与无名管道不同。
  2. FIFO有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。

mkfifo#

1 #include <sys/stat.h>
2 // 返回值:成功返回0,出错返回-1
3 int mkfifo(const char *pathname, mode_t mode);

其中的 mode 参数与open函数中的 mode 相同。一旦创建了一个 FIFO,就可以用一般的文件I/O函数操作它。

当 open 一个FIFO时,是否设置非阻塞标志(O_NONBLOCK)的区别:

  • 若没有指定O_NONBLOCK(默认),只读 open 要阻塞到某个其他进程为写而打开此 FIFO。类似的,只写 open 要阻塞到某个其他进程为读而打开它。
  • 若指定了O_NONBLOCK,则只读 open 立即返回。而只写 open 将出错返回 -1 如果没有进程已经为读而打开该 FIFO,其errno置ENXIO。

函数说明#

mkfifo()会依参数pathname建立特殊的FIFO文件,该文件必须不存在,而参数mode为该文件的权限(mode%~umask),因此 umask值也会影响到FIFO文件的权限。Mkfifo()建立的FIFO文件其他进程都可以用读写一般文件的方式存取。当使用open()来打开 FIFO文件时,O_NONBLOCK旗标会有影响
1、当使用O_NONBLOCK 旗标时,打开FIFO 文件来读取的操作会立刻返回,但是若还没有其他进程打开FIFO 文件来读取,则写入的操作会返回ENXIO 错误代码。
2、没有使用O_NONBLOCK 旗标时,打开FIFO 来读取的操作会等到其他进程打开FIFO文件来写入才正常返回。同样地,打开FIFO文件来写入的操作会等到其他进程打开FIFO 文件来读取后才正常返回。
返回值
若成功则返回0,否则返回-1,错误原因存于errno中。
错误代码
EACCESS 参数pathname所指定的目录路径无可执行的权限
EEXIST 参数pathname所指定的文件已存在。
ENAMETOOLONG 参数pathname的路径名称太长。
ENOENT 参数pathname包含的目录不存在
ENOSPC 文件系统的剩余空间不足
ENOTDIR 参数pathname路径中的目录存在但却非真正的目录。
EROFS 参数pathname指定的文件存在于只读文件系统内。

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include<errno.h>
#include <fcntl.h>
//open 要阻塞到某个其他进程为读而打开它
//       int mkfifo(const char *pathname, mode_t mode);
int main()
{
	if( (mkfifo("./file",0600) == -1) && errno!=EEXIST){
		printf("mkfifo failuer\n");
		perror("why");
	}
	int fd = open("./file",O_RDONLY);
	printf("open success\n");
	return 0;
}
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include<errno.h>
#include <fcntl.h>
//
//       int mkfifo(const char *pathname, mode_t mode);
int main()
{
	int fd = open("./file",O_WRONLY);
	printf("write open success\n");
	return 0;
}
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
//read
int main() {
    char buf[30] = {0};
    int nread = 0;

    if ((mkfifo("./file", 0600) == -1) && errno != EEXIST) {
        printf("mkfifo failuer\n");
        perror("why");
    }

    int fd = open("./file", O_RDONLY);
    printf("open success\n");

    while (1) {
        nread = read(fd, buf, 30);
         
        printf("read %d byte from fifo,context:%s\n", nread, buf);
        if(nread == 0){
            break;
        }
    }
    close(fd);
    return 0;
}
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
//write
int main() {
    int cnt = 0;
    char *str = "message from fifo";

    int fd = open("./file", O_WRONLY);
    printf("write open success\n");

    while (1) {
        write(fd, str, strlen(str));
        sleep(1);
        cnt++; 
        if (cnt == 5) {
            break;
        }
    }
    close(fd);
    return 0;
}

函数说明
mkfifo()会依参数pathname建立特殊的FIFO文件,该文件必须不存在,而参数mode为该文件的权限(mode%~umask),因此 umask值也会影响到FIFO文件的权限。Mkfifo()建立的FIFO文件其他进程都可以用读写一般文件的方式存取。当使用open()来打开 FIFO文件时,O_NONBLOCK旗标会有影响
1、当使用O_NONBLOCK 旗标时,打开FIFO 文件来读取的操作会立刻返回,但是若还没有其他进程打开FIFO 文件来读取,则写入的操作会返回ENXIO 错误代码。
2、没有使用O_NONBLOCK 旗标时,打开FIFO 来读取的操作会等到其他进程打开FIFO文件来写入才正常返回。同样地,打开FIFO文件来写入的操作会等到其他进程打开FIFO 文件来读取后才正常返回。
返回值
若成功则返回0,否则返回-1,错误原因存于errno中。
错误代码
EACCESS 参数pathname所指定的目录路径无可执行的权限
EEXIST 参数pathname所指定的文件已存在。
ENAMETOOLONG 参数pathname的路径名称太长。
ENOENT 参数pathname包含的目录不存在
ENOSPC 文件系统的剩余空间不足
ENOTDIR 参数pathname路径中的目录存在但却非真正的目录。
EROFS 参数pathname指定的文件存在于只读文件系统内。

消息队列(参考博客

ftok函数详解

在ipc通信中 system V 模式的ipc通信中都需要一个key值来生成对应的ID,那么key是如何生成的呢?#

通过函数ftok生成

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

       key_t ftok(const char *pathname, int proj_id);


  • 参数:
    pathname: 传入一个路径(一般是当前路径“ . ”)
    proj_id : 随便填写一个数(要做通信的话通信的另外一端要与这个数保持一致才能找到对应的icpID)

  • 返回值: 生成一个独有的数

  • 具体如何生成?
    key 31-24 proj_id 低8位
    key 23-16 pathname的st_dev属性的低8位
    key 15-0 pathname的st_ino属性的低16位

  • 32位组合而成一个int值,就是我们的ftok的返回值了

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
//       int msgget(key_t key, int msgflg);
//        int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

//       ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
//                      int msgflg);
struct msgbuf {
               
	long mtype;       /* message type, must be > 0 */
        char mtext[256];    /* message data */
};
//msgGet

int main()
{
	//1.huoqu
	struct msgbuf readBuf;	


	key_t key;
	key = ftok(".",'m');
	printf("key=%x\n",key);

	int msgId = msgget(key, IPC_CREAT|0777);
	if(msgId == -1 ){
		printf("get que failuer\n");
	}
	memset(&readBuf,0,sizeof(struct msgbuf));

	msgrcv(msgId, &readBuf,sizeof(readBuf.mtext),888,0);	
	printf("read from que:%s\n",readBuf.mtext);

    struct msgbuf sendBuf = {988,"thank you for reach"};
    msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0);

	msgctl(msgId,IPC_RMID,NULL);

	return 0;
}
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
//       int msgget(key_t key, int msgflg);
//        int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

//       ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
//                      int msgflg);
struct msgbuf {
               
	long mtype;       /* message type, must be > 0 */
        char mtext[256];    /* message data */
};
//msgSend

int main()
{
	//1.huoqu
	struct msgbuf sendBuf = {888,"this is message from quen"};	
	struct msgbuf readBuf;

	memset(&readBuf,0,sizeof(struct msgbuf));
	key_t key;
    key = ftok(".",'m');
    printf("key=%x\n",key);

    int msgId = msgget(key, IPC_CREAT|0777);

	if(msgId == -1 ){
		printf("get que failuer\n");
	}
	
	msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0);
	printf("send over\n");

    msgrcv(msgId, &readBuf,sizeof(readBuf.mtext),988,0);
	printf("reaturn from get:%s\n",readBuf.mtext);
	
	msgctl(msgId,IPC_RMID,NULL);
	
	return 0;
}

例子#

使用消息队列进行IPC的例子,服务端程序一直在等待特定类型的消息,当收到该类型的消息以后,发送另一种特定类型的消息作为反馈,客户端读取该反馈并打印出来

msg_server.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/msg.h>

// 用于创建一个唯一的key
#define MSG_FILE "/etc/passwd"

// 消息结构
struct msg_form {
  long mtype;
  char mtext[256];
};

int main() {
  int msqid;
  key_t key;
  struct msg_form msg;

  // 获取key值
  if ((key = ftok(MSG_FILE, 'z')) < 0) {
    perror("ftok error");
    exit(1);
  }

  // 打印key值
  printf("Message Queue - Server key is: %d.\n", key);

  // 创建消息队列
  if ((msqid = msgget(key, IPC_CREAT | 0777)) == -1) {
    perror("msgget error");
    exit(1);
  }

  // 打印消息队列ID及进程ID
  printf("My msqid is: %d.\n", msqid);
  printf("My pid is: %d.\n", getpid());

  // 循环读取消息
  for (;;) {
    msgrcv(msqid, &msg, 256, 888, 0); // 返回类型为888的第一个消息
    printf("Server: receive msg.mtext is: %s.\n", msg.mtext);
    printf("Server: receive msg.mtype is: %d.\n", msg.mtype);

    msg.mtype = 999; // 客户端接收的消息类型
    sprintf(msg.mtext, "hello, I'm server %d", getpid());
    msgsnd(msqid, &msg, sizeof(msg.mtext), 0);
  }
  return 0;
}

msg_client.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/msg.h>

// 用于创建一个唯一的key
#define MSG_FILE "/etc/passwd"

// 消息结构
struct msg_form {
    long mtype;
    char mtext[256];
};

int main()
{
    int msqid;
    key_t key;
    struct msg_form msg;

    // 获取key值
    if ((key = ftok(MSG_FILE, 'z')) < 0) 
    {
        perror("ftok error");
        exit(1);
    }

    // 打印key值
    printf("Message Queue - Client key is: %d.\n", key);

    // 打开消息队列
    if ((msqid = msgget(key, IPC_CREAT|0777)) == -1) 
    {
        perror("msgget error");
        exit(1);
    }

    // 打印消息队列ID及进程ID
    printf("My msqid is: %d.\n", msqid);
    printf("My pid is: %d.\n", getpid());

    // 添加消息,类型为888
    msg.mtype = 888;
    sprintf(msg.mtext, "hello, I'm client %d", getpid());
    msgsnd(msqid, &msg, sizeof(msg.mtext), 0);

    // 读取类型为777的消息
    msgrcv(msqid, &msg, 256, 999, 0);
    printf("Client: receive msg.mtext is: %s.\n", msg.mtext);
    printf("Client: receive msg.mtype is: %d.\n", msg.mtype);
    return 0;
}

共享内存

  1. 创建共享内存
  2. 映射
  3. 数据
  4. 释放共享内存
  5. 干掉

查看系统中的共享内存#

ipcs -m

ipcrm -m shmid (删除共享内存)

例子#

shmr.c

#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

//int shmget(key_t key, size_t size, int shmflg);
int main()
{
	
	int shmid;
	char *shmaddr;

	
	key_t key;
	key = ftok(".",1);
	
	shmid = shmget(key,1024*4,0);
	if(shmid == -1){
		printf("shmget noOk\n");
		exit(-1);
	}
	shmaddr = shmat(shmid,0,0);

	printf("shmat ok\n");
	printf("data: %s\n:",shmaddr);

	shmdt(shmaddr);

	printf("quit\n");
	
	return 0;
}

shmw.c

#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

//int shmget(key_t key, size_t size, int shmflg);
int main()
{
	
	int shmid;
	char *shmaddr;

	
	key_t key;
	key = ftok(".",1);
	
	shmid = shmget(key,1024*4,IPC_CREAT|0666);
	if(shmid == -1){
		printf("cannot shmget\n");
		exit(-1);
	}
	shmaddr = shmat(shmid,0,0);

	printf("shmat ok\n");
	strcpy(shmaddr,"chenlichen");

	sleep(5);
	shmdt(shmaddr);
	shmctl(shmid, IPC_RMID, 0);

	printf("quit\n");
	
	return 0;
}

共享内存是进程间通信中最简单的方式之一。共享内存允许两个或更多进程访问同一块内存,就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针。当一个进程改变了这块地址中的内容的时候,其它进程都会察觉到这个更改。

共享内存的特点:

1)共享内存是进程间共享数据的一种最快的方法。

一个进程向共享的内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容。

2)使用共享内存要注意的是多个进程之间对一个给定存储区访问的互斥。

若一个进程正在向共享内存区写数据,则在它做完这一步操作前,别的进程不应当去读、写这些数据。

常用函数

1)创建共享内存

所需头文件:

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

int shmget(key_t key, size_t size,int shmflg);

功能:

创建或打开一块共享内存区。

参数:

key:进程间通信键值,ftok() 的返回值。

size:该共享存储段的长度(字节)。

shmflg:标识函数的行为及共享内存的权限,其取值如下:

IPC_CREAT:如果不存在就创建

IPC_EXCL: 如果已经存在则返回失败

位或权限位:共享内存位或权限位后可以设置共享内存的访问权限,格式和 open() 函数的 mode_t 一样(open() 的使用请点此链接),但可执行权限未使用。

返回值:

成功:共享内存标识符。

失败:-1。

示例代码如下:

例子#

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
 
#define BUFSZ 1024
 
int main(int argc, char *argv[])
{
	int shmid;
	key_t key;
	
	key = ftok("./", 2015); 
	if(key == -1)
	{
		perror("ftok");
	}
	
	//创建共享内存
	shmid = shmget(key, BUFSZ, IPC_CREAT|0666);	
	if(shmid < 0) 
	{ 
		perror("shmget"); 
		exit(-1); 
	} 
 
	return 0;
}

2)共享内存映射

所需头文件:

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

void *shmat(int shmid, const void *shmaddr, int shmflg);

功能:

将一个共享内存段映射到调用进程的数据段中。简单来理解,让进程和共享内存建立一种联系,让进程某个指针指向此共享内存。

参数:

shmid:共享内存标识符,shmget() 的返回值。

shmaddr:共享内存映射地址(若为 NULL 则由系统自动指定),推荐使用 NULL。

shmflg:共享内存段的访问权限和映射条件( 通常为 0 ),具体取值如下:

0:共享内存具有可读可写权限。

SHM_RDONLY:只读。

SHM_RND:(shmaddr 非空时才有效)

返回值:

成功:共享内存段映射地址( 相当于这个指针就指向此共享内存 )

失败:-1

3)解除共享内存映射

所需头文件:

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

int shmdt(const void *shmaddr);

功能:

将共享内存和当前进程分离( 仅仅是断开联系并不删除共享内存,相当于让之前的指向此共享内存的指针,不再指向)。

参数:

shmaddr:共享内存映射地址。

返回值:

成功:0

失败:-1

4)共享内存控制

所需的头文件:

#include <sys/ipc.h>

#include <sys/shm.h>

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

功能:

共享内存属性的控制。

参数:

shmid:共享内存标识符。

cmd:函数功能的控制,其取值如下:

IPC_RMID:删除。(常用 )

IPC_SET:设置 shmid_ds 参数,相当于把共享内存原来的属性值替换为 buf 里的属性值。

IPC_STAT:保存 shmid_ds 参数,把共享内存原来的属性值备份到 buf 里。

SHM_LOCK:锁定共享内存段( 超级用户 )。

SHM_UNLOCK:解锁共享内存段。

SHM_LOCK 用于锁定内存,禁止内存交换。并不代表共享内存被锁定后禁止其它进程访问。其真正的意义是:被锁定的内存不允许被交换到虚拟内存中。这样做的优势在于让共享内存一直处于内存中,从而提高程序性能。

buf:shmid_ds 数据类型的地址(具体类型请点此链接 ),用来存放或修改共享内存的属性。

返回值:

成功:0

失败:-1

实战示例

接下来我们做这么一个例子:创建两个进程,在 A 进程中创建一个共享内存,并向其写入数据,通过 B 进程从共享内存中读取数据。

写端代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
 
#define BUFSZ 512
 
int main(int argc, char *argv[])
{
	int shmid;
	int ret;
	key_t key;
	char *shmadd;
	
	//创建key值
	key = ftok("../", 2015); 
	if(key == -1)
	{
		perror("ftok");
	}
	
	//创建共享内存
	shmid = shmget(key, BUFSZ, IPC_CREAT|0666);	
	if(shmid < 0) 
	{ 
		perror("shmget"); 
		exit(-1); 
	}
	
	//映射
	shmadd = shmat(shmid, NULL, 0);
	if(shmadd < 0)
	{
		perror("shmat");
		_exit(-1);
	}
	
	//拷贝数据至共享内存区
	printf("copy data to shared-memory\n");
	bzero(shmadd, BUFSZ); // 共享内存清空
	strcpy(shmadd, "how are you, mike\n");
	
	return 0;
}
读端代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
 
#define BUFSZ 512
 
int main(int argc, char *argv[])
{
	int shmid;
	int ret;
	key_t key;
	char *shmadd;
	
	//创建key值
	key = ftok("../", 2015); 
	if(key == -1)
	{
		perror("ftok");
	}
	
	system("ipcs -m"); //查看共享内存
	
	//打开共享内存
	shmid = shmget(key, BUFSZ, IPC_CREAT|0666);
	if(shmid < 0) 
	{ 
		perror("shmget"); 
		exit(-1); 
	} 
	
	//映射
	shmadd = shmat(shmid, NULL, 0);
	if(shmadd < 0)
	{
		perror("shmat");
		exit(-1);
	}
	
	//读共享内存区数据
	printf("data = [%s]\n", shmadd);
	
	//分离共享内存和当前进程
	ret = shmdt(shmadd);
	if(ret < 0)
	{
		perror("shmdt");
		exit(1);
	}
	else
	{
		printf("deleted shared-memory\n");
	}
	
	//删除共享内存
	shmctl(shmid, IPC_RMID, NULL);
	
	system("ipcs -m"); //查看共享内存
	
	return 0;
}

信号

#include <signal.h>
#include <stdio.h>
//       typedef void (*sighandler_t)(int);
//       sighandler_t signal(int signum, sighandler_t handler);
void handler(int signum)
{
	printf("get signum=%d\n", signum);
	switch (signum)
	{
	case 2:
		printf("SIGINT\n");
		break;
	case 9:
		printf("SIGKILL\n");
		break;
	case 10:
		printf("SIGUSR1\n");
		break;
	}
	printf("never quit\n");
}
int main()
{
	signal(SIGINT, SIG_IGN);
	signal(SIGKILL, SIG_IGN);
	signal(SIGUSR1, handler);
	while (1)
		;
	return 0;
}
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
int main(int argc, char **argv)
{
	int signum;
	int pid;
	char cmd[128] = {0};
	signum = atoi(argv[1]);
	pid = atoi(argv[2]);
	printf("num=%d,pid=%d\n", signum, pid);
	//	kill(pid, signum);
	sprintf(cmd, "kill -%d %d", signum, pid);
	system(cmd);
	printf("send signal ok");
	return 0;
}

#include <signal.h>
#include <stdio.h>
//       int sigaction(int signum, const struct sigaction *act,
//                     struct sigaction *oldact);
void handler(int signum, siginfo_t *info, void *context)
{
	printf("get signum %d\n", signum);
	if (context != NULL)
	{
		printf("from:%d\n", info->si_pid);
		printf("get data=%d\n", info->si_int);
		printf("get data=%d\n", info->si_value.sival_int);
		// printf("get data=%d\n", *(int *)(info->si_value.sival_ptr));
	}
}
int main()
{
	struct sigaction act;
	printf("pid = %d\n", getpid());
	act.sa_sigaction = handler;
	act.sa_flags = SA_SIGINFO; // be able to get message
	sigaction(SIGUSR1, &act, NULL);
	while (1);
	return 0;
}
#include <stdio.h>
#include <signal.h>
int main(int argc, char **argv)
{
	int signum;
	int pid;
	signum = atoi(argv[1]);
	pid = atoi(argv[2]);
	union sigval value;
	value.sival_int = 100;
	//	 int sigqueue(pid_t pid, int sig, const union sigval value);
	sigqueue(pid, signum, value);
	printf("%d,done\n", getpid());
	return 0;
}

信号量

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
// int semget(key_t key, int nsems, int semflg);
// int semctl(int semid, int semnum, int cmd, ...);
//  int semop(int semid, struct sembuf *sops, unsigned nsops);

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) */
};

void pGetKey(int id)
{
	struct sembuf set;

	set.sem_num = 0;
	set.sem_op = -1;
	set.sem_flg = SEM_UNDO;

	semop(id, &set, 1);
	printf("getkey\n");
}

void vPutBackKey(int id)
{
	struct sembuf set;

	set.sem_num = 0;
	set.sem_op = 1;
	set.sem_flg = SEM_UNDO;

	semop(id, &set, 1);
	printf("put back the key\n");
}

int main(int argc, char const *argv[])
{
	key_t key;
	int semid;

	key = ftok(".", 2);
	// 信号量集合中有一个信号量
	semid = semget(key, 1, IPC_CREAT | 0666); // 获取/创建信号量

	union semun initsem;
	initsem.val = 0;
	// 操作第0个信号量
	semctl(semid, 0, SETVAL, initsem); // 初始化信号量
									   // SETVAL设置信号量的值,设置为inisem
	int pid = fork();
	if (pid > 0)
	{
		// 去拿锁
		pGetKey(semid);
		printf("this is father\n");
		vPutBackKey(semid);
		// 锁放回去
		semctl(semid, 0, IPC_RMID);
	}
	else if (pid == 0)
	{
		printf("this is child\n");
		vPutBackKey(semid);
	}
	else
	{

		printf("fork error\n");
	}
	return 0;
}

作者:keep--fighting

出处:https://www.cnblogs.com/keep--fighting/p/17277767.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   ⭐⭐-fighting⭐⭐  阅读(64)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示