Linux系统编程-进程间通信(管道、消息队列、共享内存、信号、信号量)

Linux进程间通信简介

进程间通信(IPC,InterProcess Communication)是指在不同进程之间传播或交换信息。
IPC 的方式通常有管道(包括无名管道和命名管道)消息队列共享内存、信号、信号量、Socket、Streams 等。其中 Socket 和 Streams 支持不同主机上的两个进程 IPC。

管道

无名管道(pipe)

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

特点:

1、它是半双工的(即数据只能在一个方向上流动),具有固定的读端(fd[0])和写端(fd[1])。

2、它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)。

3、它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。

4、管道中的数据读走就没了

原型:

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

当一个管道建立时,它会创建来个文件描述符,fd[0]是为了读而打开,fd[1]是为了写而打开,要关闭管道只需要关闭两个文件描述符即可。

 

 

 管道属于特殊文件,无法使用lseek函数获取管道中有多少字节的数据,并且在子进程在读管道数据时会堵塞进程,直到父进程向管道内写入数据

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
	int fd[2];
	pid_t pid;
	char readBuf[128];
	int nwrite;
	int nread;
	// int pipe(int pipefd[2]);
	if(pipe(fd) == -1){
		printf("创建管道失败\n");
	}
	pid=fork();//创建子进程
	if(pid == -1){
		printf("创建子进程失败\n");
	//1.在父进程写入
	}else if(pid > 0){
		printf("这是父进程\n");
		close(fd[0]);//关闭读文件
		//ssize_t write(int fd, const void *buf, size_t count);
		nwrite=write(fd[1],"hello from father process",strlen("hello from father process"));
		wait(NULL);
		
	//2.在子进程读
	}else{
		printf("这是子进程\n");
		close(fd[1]);
		//ssize_t read(int fd, void *buf, size_t count);
		nread=read(fd[0],readBuf,128);//如果度端没有内容,会阻塞等待
		printf("来自父进程的写入内容是:%s\n",readBuf);
		exit(0);
	}

	return 0;
}

注意: 管道为半双工通信,读和写操作在同一时间内只能进行一个,所以在读的时候要关闭写端,写的时候关闭读端。

有名管道(fifo)

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

特点

FIFO可以在无关的进程之间交换数据,与无名管道不同。

FIFO有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。

原型

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

pathname:
当前路径下要生成的fifo文件名

mode:

=0600为可读可写 4可读 2可写 1可执行 所以0600为可读可写权限

返回值:成功返回0,失败返回-1;

fiforead.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main()
{
        int nread;
        mkfifo("./file",0600);
        char buf[1024] = {0};
        int fd = open("./file",O_RDONLY);//若没有指定O_NONBLOCK(默认),只读 open 要阻塞到某个其他进程为写而打开此 FIFO
        printf("open success\n");
        while(1){
                nread = read(fd,buf,1024);
                printf("read %d byte ,neirong:%s\n",nread,buf);
        }
        return 0;
}

fifowrite.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main()
{
        mkfifo("./file",0600);
        char *str = "this is a fifo demo!";
        int fd = open("./file",O_WRONLY);
        printf("open success\n");
        while(1){
                write(fd,str,strlen(str));
                sleep(1);
        }
        return 0;
}

运行read会阻塞,一直到运行write后read才会继续往下执行,数据读出时管道数据清除。

消息队列(msg)

息队列,是消息的链接表(链表结构),存放在Linux内核中,一个消息队列由一个标识符(队列ID)来标识

特点:

1、消息队列是面向记录的,其中的消息具有特定的格式和特定的优先级

2、消息队列独立于接收和发送进程,当进程终止时,消息队列及其内容并不会被删除

3、消息队列可以实现随机查询,消息不一定要以先入先出的方式读取,也可以按照消息的类型读取

API原型

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

 

1、创建或打开消息队列

int msgget(key_t key, int msgflg);
函数作用:创建或打开消息队列

key:ftok()函数获得

msgflg:IPC_CREAT|0777代表没有这个消息队列则创建,权限为可读可写可执行

返回值:成功返回消息队列ID(非负整数),失败返回-1;


2、添加消息
int msgsnd(int msqid,
const void *msgp, size_t msgsz, int msgflg);


函数作用:添加消息队列数据

msqid:消息队列ID,由msgget()函数获得

msgp:是一个结构体指针,指向struct msgbuf
struct msgbuf {
        long mtype;       /* message type, must be > 0 */    消息类型,必须大于0
        char mtext[1];    /* message data */                        消息数据
};

msgsz:消息的长度,一般用sizeof()关键字或strlen()函数来计算

msgflg:只有在消息队列满或队列达到系统范围限制才有效该参数才有效,一般设置为0

返回值:成功返回0,失败返回-1

3、接收消息
ssize_t msgrcv(int msqid,
void *msgp, size_t msgsz, long msgtyp,int msgflg);


函数作用:接收消息队列数据

msqid:消息队列ID,由msgget()函数获得

msgp:是一个结构体指针,指向struct msgbuf
struct msgbuf {
        long mtype;       /* message type, must be > 0 */    消息类型,必须大于0
        char mtext[1];    /* message data */                        消息数据
};

msgsz:消息的长度,一般用sizeof()关键字或strlen()函数来计算

msgtyp:消息类型

msgflg:只有在消息队列满或队列达到系统范围限制才有效该参数才有效,一般设置为0

返回值:成功返回消息数据长度,失败返回-1


4、控制消息队列

int msgctl(int msqid, int cmd, struct msqid_ds *buf);

函数作用:控制消息队列

msqid:消息队列ID,由msgget()函数获得

cmd:设置为IPC_RMID代表立即删除消息队列

buf:配合cmd参数一起使用,无需使用时设置为NULL

返回值:成功返回0,失败返回-1

获取key值

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

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

//函数作用:将一个已存在的路径名(该路径一定要存在)一个整数转换为一个key值(这个key是独一无二,它不用和其他共享内存的key相同)。这个key是会被设置进维护共享内存的数据结构当中。

//pathname:路径名

//proj_id:整数,随意

//返回值:失败返回-1,成功返回key键值

msgread.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>

struct msgbuf
{
    long mtype;
    char mtext[128];
};
int main()
{
    struct msgbuf readBuf;
    struct msgbuf sendBuf = {999,"i have received"};
    key_t key;
    key = ftok(".",'z');
    printf("key = %x\n",key);

    int msgId = msgget(key,IPC_CREAT|0777);
    if(msgId == -1){
        puts("creat failed\n");
    }
    msgrcv(msgId,&readBuf,sizeof(readBuf.mtext),888,0);
    printf("读取来自队列的内容:%s\n",readBuf.mtext);

    msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0);
    printf("发送完毕。\n");

    msgctl(msgId,IPC_RMID,NULL);

    return 0;
}

msgwrite.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>

struct msgbuf
{
    long mtype;
    char mtext[128];
};

int main()
{
    struct msgbuf sendBuf = {888,"this is msg from que"};
        struct msgbuf readBuf;

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

    int msgId = msgget(key,IPC_CREAT|0777);
    if(msgId == -1){
        puts("creat failed\n");
    }
    msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0);
    printf("发送完毕\n");

    msgrcv(msgId,&readBuf,sizeof(readBuf.mtext),999,0);
    printf("读取来自队列的内容:%s\n",readBuf.mtext);

    msgctl(msgId,IPC_RMID,NULL);
    return 0;
}

 

 共享内存(shm)

共享内存(Shared Memory),指两个或多个进程共享一个给定的存储区

特点

  1. 共享内存是最快的一种 IPC,因为进程是直接对内存进行存取。
  2. 因为多个进程可以同时操作,所以需要进行同步。

信号量+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问

和消息队列的区别:

        消息队列就像两个人聊天,一方要将要说的话写在一张纸上放入箱子里,另外一个人去箱子里取出来阅读。这个箱子(消息队列)不会自行销毁,要调用msgctl才可以。

        共享内存就好像两个学生在上课的时候,由于不能说话,就只好拿一个本子来聊天,这个本子就相当于共享的内存,双方可以同时看到纸上的内容。比消息队列效率高。调用shmctl删除共享内存。

流程:创建-映射-(操作)-释放-关闭

原型

#include <sys/shm.h>

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

//函数作用:创建或获取一个共享内存

//key:由ftok生成的key标识,标识系统的唯一IPC资源。

//size:需要申请共享内存的大小。在操作系统中,申请内存的最小单位为页,一页是4k字节,为了避免内存碎片,我们一般申请的内存大小为页的整数倍,一般而言是4096的整数倍,因为内存的块的大小就是4KB即4096B

//flag:如果要创建新的共享内存,需要使用IPC_CREAT,可配选项0666代表可读可写
//返回值:成功返回共享内存ID,失败返回-1

void *shmat(int shm_id,const void *shmaddr,int flag);

//函数作用:连接共享内存到当前进程的地址空间,不同的进程通过此函数,挂载到同一片内存上

//shm_id:共享存储段的标识符(共享内存ID)
//shmaddr设置为0,代表让Linux内核为进程自动分配一个内存地址(推荐使用)

//flag设置为0,代表分配的内存为可读可写的。设置为SHM_RDONLY,代表映射的内存只读

//返回值:成功返回共享存储段的指针(虚拟地址),并且内核将使其与该共享存储段相关的shmid_ds结构中的shm_nattch计数器加1(类似于引用计数); 出错返回-1。

int shmdt(void *shmaddr);

//函数作用:断开与共享内存的连接,该函数并不删除所指定的共享内存区,而是将之前用shmat函数连接好的共享内存区脱离目前的进程。

//shmaddr:调用shmat函数连接成功返回的地址

//返回值:成功返回0,并将shmid_ds结构体中的 shm_nattch计数器减1;出错返回-1。

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

//函数作用:控制共享内存的相关信息

//shm_id:共享存储段的标识符(共享内存ID)

//cmd:指定的执行操作,设置为IPC_RMID表示立即删除共享内存

//buf:设置为NULL即可

//返回值:成功返回0,失败返回-1

获取key值

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

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

//函数作用:将一个已存在的路径名(该路径一定要存在)一个整数转换为一个key值(这个key是独一无二,它不用和其他共享内存的key相同)。这个key是会被设置进维护共享内存的数据结构当中。

//pathname:路径名

//proj_id:整数,随意

//返回值:失败返回-1,成功返回key键值

查看系统中有哪些共享内存

指令:ipcs -m
 

删除共享内存

指令:ipcrm -m 884739  //红字为共享内存ID

shmsend.c

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



int main()
{


	key_t key;
	char *shmaddr;
	// key_t ftok(const char *pathname, int proj_id);
	key=ftok(".",2);
	printf("key = %x\n",key);

	//int shmget(key_t key, size_t size, int shmflg);
	//创建或获取一个共享内存:成功返回共享内存ID,失败返回-1
	int shmId=shmget(key,1024*4,IPC_CREAT|0666);//共享内存大小必须以字节为单位
	if(shmId == -1){
		printf("创建共享内存失败\n");
		exit(-1);//异常退出
	}

	//void *shmat(int shmid, const void *shmaddr, int shmflg);
	//连接共享内存到当前进程的地址空间:成功返回指向共享内存的指针,失败返回-1
	shmaddr=shmat(shmId,0,0);//挂载映射,如果引用一个已存在的共享内存(上方改革创建),则将 size 指定为0 
	printf("shmat ok\n");
	strcpy(shmaddr,"hello sharemessage");
	
	sleep(5);

	//int shmdt(const void *shmaddr);
	// 断开与共享内存的连接
	shmdt(shmaddr);

	 //int shmctl(int shmid, int cmd, struct shmid_ds *buf);
	//控制共享内存的相关信息:成功返回0,失败返回-1
	shmctl(shmId,IPC_RMID,0);//IPC_RMID删除消息队列
	printf("退出\n");
	return 0;
}

shmrcv.c

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



int main()
{


	key_t key;
	char *shmaddr;
	// key_t ftok(const char *pathname, int proj_id);
	key=ftok(".",2);
	printf("key = %x\n",key);

	//int shmget(key_t key, size_t size, int shmflg);
	//创建或获取一个共享内存:成功返回共享内存ID,失败返回-1
	int shmId=shmget(key,1024*4,0);//只要打开就行不必创建
	if(shmId == -1){
		printf("创建共享内存失败\n");
		exit(-1);//异常退出
	}

	//void *shmat(int shmid, const void *shmaddr, int shmflg);
	//连接共享内存到当前进程的地址空间:成功返回指向共享内存的指针,失败返回-1
	shmaddr=shmat(shmId,0,0);//挂载映射,如果引用一个已存在的共享内存,则将 size 指定为0 
	printf("shmat ok\n");
	
	printf("内容是:%s\n",shmaddr);
	//int shmdt(const void *shmaddr);
	 //断开与共享内存的连接
	shmdt(shmaddr);
	return 0;
}

        

 

信号(signal)

优质博文参考:https://www.jianshu.com/p/f445bfeea40a

概述

1.信号:对Linux来说就是软中断,与单片机的硬件中断(串口)类似。如在linux中输入 ctrl+c 来停止一个程序

2.信号的名字与编号:可在linux中通过 kill -l 查询(Linux系统一共有64个信号,编号1-64。不存在0信号,0信号有特殊的应用:在系统级的应用中被占用)2.信号的名字与编号:可在linux中通过 kill -l 查询(Linux系统一共有64个信号,编号1-64。不存在0信号,0信号有特殊的应用:在系统级的应用中被占用)

部分信号的说明:

SIGINT:ctrl+c 终止信号

SIGQUIT:ctrl+\ 终止信号

SIGTSTP:ctrl+z 暂停信号

SIGALRM:闹钟信号 收到此信号后定时结束,结束进程

SIGCHLD:子进程状态改变,父进程收到信号

SIGKILL:杀死信号

信号处理的三种方式:忽略,捕捉和默认动作

  1. 忽略:就跟字面意思一样忽略掉它(注意:SIGKILL,SIGSTOP不能被忽略
  2. 捕捉:就是一些信号处理的函数,然后让这个函数告诉内核,当信号产生时,内核调用该函数,实现某种信号的处理
  3. 默认动作:每个信号都有其对应的默认的处理动作,当触发了某种信号,系统就会立刻去执行。

 信号的使用

其实对于常用的 kill 命令就是一个发送信号的工具,kill -9 PID或者使用命令kill -SIGKILL PID (二者作用一样)来杀死进程。比如,我在后台运行了一个 top 工具,通过 ps 命令可以查看他的 PID,通过 kill -9 来发送了一个终止进程的信号来结束了 top 进程。如果查看信号编号和名称,可以发现9对应的是 9) SIGKILL,正是杀死该进程的信号。而以下的执行过程实际也就是执行了9号信号的默认动作——杀死进程。

对于信号来说,最大的意义不是为了杀死信号,而是实现一些异步通讯(即信号处理方式第二种–捕捉)的手段

信号编程(入门)

信号处理函数的注册(信号绑定):signal (入门),sigaction(高级,可携带信号)
信号处理发送函数: kill (入门), sigqueue(高级,可携带信号)

信号处理函数的注册和信号处理发送函数-入门版


注册
#include <signal.h>

typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);

函数作用:信号绑定函数

signum:指明要绑定的信号类型,SIGKILLSIGSTOP两个信号外的任何信号都可以

handler:指向绑定的函数指针,设置为SIG_IGN时代表忽略信号

返回值:成功返回信号处理程序的上一个值,出错返回SIG_ERR。


信号发送
int kill(pid_t pid,int sig);


函数作用:向指定进程发送指定信号

pid:进程ID

sig:指定发送的信号

返回值:成功返回0,失败返回-1

signal_demo.c

注册信号函数,实现Ctrl+c无法中止进程

#include <signal.h>
#include <stdio.h>

void handler(int sigNum)
{
	printf("sigNum is %d \n",sigNum);
	printf("never quit\n");
}

int main()
{
	signal(SIGINT,handler);
	while(1);
	return 0;
}

kill_demo.c 

发送信号函数,触发信号,两种方式(KILL和system) 

#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <stdlib.h>



//typedef void (*sighandler_t)(int);

//sighandler_t signal(int signum, sighandler_t handler);



int main(int argc,char **argv)
{
	int signum;
	int pid;
	char cmd[128]={0};
	signum=atoi(argv[1]);//将字符串str转换成一个整数并返回结果
	pid=atoi(argv[2]);
	printf("signum=%d pid=%d\n",signum,pid);
	// int kill(pid_t pid, int sig);
	
	//方法一:
	//kill(pid,signum);   发送信号x
	
    //方法二 system调用脚本
	sprintf(cmd,"kill -%d %d",signum,pid);
	system(cmd);
	
	printf("发送指令成功\n");
	
	return 0;
}	

信号的忽略

signal(SIGINT,SIG_IGN)//可以忽略掉ctrl c 的信号

信号携带消息(高级)

发信号:

1.用什么发 sigqueue()

2.怎么发消息

#include <signal.h>
            //发给谁   发什么信号(比如上面讲的信号编号9 10等)  
int sigqueue(pid_t pid, int sig, const union sigval value);
union sigval {                   //发送的消息(接收端也是用的这个联合体)  
   int   sival_int;//发送整型
   void *sival_ptr;//发送字符串
 };

收信号:

1.用什么绑定函数(以及收到信号如何处理动作)sigaction()

2.如何读出消息

#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
//sigactio(),会依照参数signum指定的信号编号来设置该信号的处理函数
//const struct sigaction *act,你要做什么
//struct sigaction *oldact,是否备份,不备份用NULL

struct sigaction {   凡是带有_t说明是个结构体

   void       (*sa_handler)(int); //信号处理程序,不接受额外数据,和signal()的参数handler一样了 SIG_IGN 为忽略,SIG_DFL 为默认动作
   void       (*sa_sigaction)(int, siginfo_t *, void *); //信号处理程序,能够接受额外数据和sigqueue配合使用
   sigset_t   sa_mask;//阻塞关键字的信号集(默认阻塞),可以再调用捕捉函数之前,把信号添加到信号阻塞字,信号捕捉函数返回之前恢复为原先的值。
   int        sa_flags;//配置为SA_SIGINFO这个宏表示能够接受数据
   
 };
//回调函数句柄sa_handler、sa_sigaction只能任选其一

 siginfo_t {
               int      si_signo;    /* Signal number */
               int      si_errno;    /* An errno value */
               int      si_code;     /* Signal code */
               int      si_trapno;   /* Trap number that caused
                                        hardware-generated signal
                                        (unused on most architectures) */
               pid_t    si_pid;      /* Sending process ID */谁发的
               uid_t    si_uid;      /* Real user ID of sending process */
               int      si_status;   /* Exit value or signal */
               clock_t  si_utime;    /* User time consumed */
               clock_t  si_stime;    /* System time consumed */
               sigval_t si_value;    /* Signal value */接收的数据 是个联合体 信号发送函数使用这个联合体
               int      si_int;      /* POSIX.1b signal */数据
               void    *si_ptr;      /* POSIX.1b signal */
               int      si_overrun;  /* Timer overrun count; POSIX.1b timers */
               int      si_timerid;  /* Timer ID; POSIX.1b timers */
               void    *si_addr;     /* Memory location which caused fault */
               int      si_band;     /* Band event */
               int      si_fd;       /* File descriptor */
}

 信号携带消息编程实现

收信号:

#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
/*struct sigaction {
               void     (*sa_handler)(int);
               void     (*sa_sigaction)(int, siginfo_t *, void *);
               sigset_t   sa_mask;
               int        sa_flags;
               void     (*sa_restorer)(void);
           };*/

void     handler(int signum, siginfo_t *info, void *context)//info里面的参数可以通过man手册查询
{
	printf("signum=%d\n",signum);
	if(context != NULL){
		printf("get data=%d\n",info->si_int);
		printf("get data=%d\n",info->si_value.sival_int);//和上面一样换了种方式
		printf("发送者的pid=%d\n",info->si_pid);
	}
}

int main()
{
	
	struct sigaction act;
	printf("pid=%d\n",getpid());
	act.sa_sigaction=handler;
	act.sa_flags=SA_SIGINFO;//能够获取到信息信息
	//int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
	sigaction(SIGUSR1,&act,NULL);//第三个是参数是备份的这边写NULL SIGUSR1对应的编号为10!!!
	printf("22");
	while(1);
	return 0;
}

发信号:

#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc,char **argv)
{
	int signum;
	int pid;
	signum=atoi(argv[1]);//atoi把字符串转化为整形
	pid=atoi(argv[2]);//接收进程的pid号
	union sigval value;
	value.sival_int=100;//发送一个整型数100的消息
	/*union sigval {
               int   sival_int;
               void *sival_ptr;  发送字符的话把地址传进来
           };*/

	//int sigqueue(pid_t pid, int sig, const union sigval value);
	sigqueue(pid,signum,value);
	printf("我的pid是:%d\n",getpid());
	printf("发送完毕\n");
	return 0;
}

信号量(Semaphore)

信号量:

信号量(Semaphore是一个计数器,用于实现进程间的互斥与同步,不用于存储进程间的通信数据

特点:

(1).用于进程间同步,若要在进程间传递数据需要结合共享内存

(2).信号量是基于PV操作,程序对信号量是原子操作

所谓原子操作,就是“不可中断的一个或一系列操作”,也就是不会被线程调度机制打断的操作

P、V操作(类似信号量lock、unlock)

可以这样理解:一间房间(临界资源)的门前有一个盒子,盒子里有钥匙(信号量),一个人拿了钥匙(P操作),开了门并走进了房间,且门外还有人等着,得等进去的人出来放钥匙(V操作),这个人才能拿钥匙(P操作)进入房间

多道程序系统中存在许多进程,它们共享各种资源,然而有很多资源一次只能供一个进程使用。一次仅允许一个进程使用的资源称为临界资源。许多物理设备都属于临界资源,如输入机、打印机、磁带机等。

相关api

最简单的信号量是只能取 0 和 1 的变量,这也是信号量最常见的一种形式,叫做二值信号量(Binary Semaphore)。而可以取多个正整数的信号量被称为通用信号量。

Linux 下的信号量函数都是在通用的信号量数组上进行操作,而不是在一个单一的二值信号量上进行操作。

API:

#include <sys/sem.h>

int semget(key_t key,int num_sems,int sem_flags);

//函数作用:创建或获取一个信号量组:

//key:由ftok生成的key标识,标识系统的唯一IPC资源。

//num_sems:需要创建的信号量数量

//sem_flags:如果要创建新的共享内存,需要使用IPC_CREAT,可配选项0666代表可读可写

//返回值:若成功返回信号量集ID,失败返回-1;


int semop(int semid,struct sembuf semoparray[],size_t numops);

//函数作用:对信号量组进行操作,改变信号量的值

//semid:信号量ID,由semget()函数获得

//semoparray[]:信号量操作数组

//numops:进行操作信号量的个数,即sops结构变量的个数,需大于或等于1。最常见设置此值等于1,只完成对一个信号量的操作

//返回值:成功返回0,失败返回-1


int semctl(int semid,int sem_num,int cmd,union semun arg);

//函数作用:控制信号量的相关信息

//semid:信号量ID,由semget()函数获得

//sem_num:第几个信号量,默认设为0,当使用到信号量集才设置其他值

//cmd:设置为SETVAL代表把信号量设置为一个已知的值

//arg:semun 联合体,存放信号量数值
//返回值:失败返回-1


union semun {
int val;

//就把这个val值设置一下就好了,其他的不用管,默认就好


struct semid_ds *buf;

//IPC_STAT、IPC_SET用的semid_ds结构


unsigned short *array;

//SETALL、GETALL用的数组值
                     

struct seminfo *_buf;  

//为控制IPC_INFO提供的缓存
};


struct sembuf {
short semnum;

/*信号量集合中的信号量编号,0代表第1个信号量*/


short val;

//若val>0进行V操作信号量值加val,表示进程释放控制的资源 
//若val<0进行P操作信号量值减val,则调用进程阻塞,直到资源可用

//若设置IPC_NOWAIT不会睡眠,进程直接返回EAGAIN错误
//若val==0时阻塞等待信号量为0,调用进程进入睡眠状态,直到信号值为0;

//若设置IPC_NOWAIT,进程不会睡眠,直接返回EAGAIN错误


short flag;

//0 设置信号量的默认操作
//IPC_NOWAIT设置信号量操作不等待            
//SEM_UNDO 选项会让内核记录一个与调用进程相关的UNDO记录,如果该进程崩溃,则根据这个进程的UNDO记录自动恢复相应信号量的计数值

};

sem.c

#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("get key\n");
}
void vPutKey(int id)
{
	struct sembuf set;
	set.sem_num = 0;        
    set.sem_op = 1;       
    set.sem_flg = SEM_UNDO;
 
	semop(id,&set,1);
	printf("put key\n");
}
 
int main(int argc, char const *argv[])
{
	key_t key; 
	int semid;
	int pid;
	union semun seminit;
	seminit.val = 0;
 
	key = ftok(".",1);
 
	semid = semget(key,1,IPC_CREAT|0666);
 
	semctl(semid,0,SETVAL,seminit);
 
	pid = fork();
 
	if(pid > 0){
		pGetKey(semid);
		printf("this is father\n");
		vPutKey(semid);
	}else if (pid == 0){
		printf("this is child\n");
		vPutKey(semid);
	}else{
		printf("fork error\n");
	}
 
	return 0;
} 

信号量与共享内存配合实例

不加信号量实例

shmsend.c 

#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <stdlib.h>
 
int main()
{
	key_t key_shm;
	int shmid;	
	char *send;
	int i,cnt = 0;
 
	if((key_shm = ftok(".",1)) == -1){
		perror("ftok");
		exit(-1);
	}
 
	printf("ftok successed! key is 0x%x\n",key_shm);
 
	if((shmid =shmget(key_shm,4*1024,IPC_CREAT|0666)) == -1){
		perror("shmget");
		exit(-2);
	}else{
		printf("shmget successed! shmid is %d\n",shmid);
	}
 
	if(*(send=shmat(shmid,0,0)) == -1){
		perror("shmat");
		exit(-3);
	}else{
		printf("shmat successed!\n");
	}	
 
	while(1){
		for(i=0;i<10;i++){
			
			*send = i+48;
			send++;
			memset(send,'\0',4*1024);
			sleep(1);
		}
		send = send -i;
		printf("cnt is %d\n",++cnt);;
		if(cnt ==3){
			break;
		}
	}
	if(shmdt(send) == -1){
		perror("shmdt");
		exit(-4);
	}else{
		printf("shmdt successed\n");
	}
 
	if(shmctl(shmid,IPC_RMID,NULL) == -1){
		perror("shmctl");
		exit(-5);
	}else{
		printf("shmctl successed!\n");
	}
	
	return 0;
}
 

shmreceive.c

#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <stdlib.h>
 
int main()
{
	key_t key_shm;
	int shmid;	
	char *rcv;
 
	if((key_shm = ftok(".",1)) == -1){
		perror("ftok");
		exit(-1);
	}
 
	printf("ftok successed!key is %x\n",key_shm);
 
	if((shmid =shmget(key_shm,4*1024,IPC_CREAT|0666)) == -1){
		perror("shmget");
		exit(-2);
	}else{
		printf("shmget successed! shmid is %d\n",shmid);
	}
 
	if(*(rcv=shmat(shmid,0,0)) == -1){
		perror("shmat");
		exit(-3);
	}else{
		printf("shmat successed!\n");
	}	
 
	while(1){
		sleep(1);
		printf("%s\n",rcv);
	}
 
	if(shmdt(rcv) == -1){
		perror("shmdt");
		exit(-4);
	}else{
		printf("shmdt successed\n");
	}
 
	if(shmctl(shmid,IPC_RMID,NULL) == -1){
		perror("shmctl");
		exit(-5);
	}else{
		printf("shmctl successed!\n");
	}
 
	return 0;
 
}
 

此时终端上的体现如下:

 共享内存加信号量的实例

shm_sem_

目录

Linux进程间通信简介

管道

无名管道(pipe)

特点:

原型:

有名管道(fifo)

特点

原型

fiforead.c

fifowrite.c

消息队列(msg)

特点:

msgread.c

msgwrite.c

 共享内存(shm)

特点

和消息队列的区别:

流程:创建-映射-(操作)-释放-关闭

shmsend.c

shmrcv.c

信号(signal)

概述

部分信号的说明:

信号处理的三种方式:忽略,捕捉和默认动作

 信号的使用

信号编程(入门)

signal_demo.c

kill_demo.c 

信号的忽略

信号携带消息(高级)

发信号:

收信号:

 信号携带消息编程实现

收信号:

发信号:

信号量(Semaphore)

信号量:

特点:

P、V操作(类似信号量lock、unlock)

相关api

sem.c

信号量与共享内存配合实例

shmsend.c 

shmreceive.c

 共享内存加信号量的实例

shm_sem_

send.c

shm_sem_receive.c


send.c

#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <stdlib.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) */
};
 
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("get key\n");
}
void vPutKey(int id)
{
	struct sembuf set;
	set.sem_num = 0;        
	set.sem_op = 1;       
	set.sem_flg = SEM_UNDO;
 
	semop(id,&set,1);
	printf("put key\n");
}
 
int main()
{
	key_t key_shm;
	key_t key_sem;
	int shmid;
	int semid;	
	char *send;
	int i,cnt = 0;
	union semun seminit;
	seminit.val = 1;
 
	if((key_shm = ftok(".",1)) == -1){
		perror("ftok");
		exit(-1);
	}
 
	printf("ftok successed! key is 0x%x\n",key_shm);
 
	if((shmid =shmget(key_shm,4*1024,IPC_CREAT|0666)) == -1){
		perror("shmget");
		exit(-2);
	}else{
		printf("shmget successed! shmid is %d\n",shmid);
	}
 
	if(*(send=shmat(shmid,0,0)) == -1){
		perror("shmat");
		exit(-3);
	}else{
		printf("shmat successed!\n");
	}
 
 
	if((key_sem = ftok(".",2)) == -1){
		perror("ftok");
		exit(-7);
	}
	printf("sem ftok successed! key is 0x%x\n",key_sem);
 
	if((semid =semget(key_sem,1,IPC_CREAT|0666) == -1)){
		perror("semget");
		exit(-6);
	}
 
	if(semctl(semid,0,SETVAL,seminit) == -1){
		perror("semctl");
		exit(-8);
	}else{
		printf("semctl successed!\n");
	}
 
	while(1){
		pGetKey(semid);
		for(i=0;i<10;i++){
 
			*send = i+48;
			send++;
			memset(send,'\0',4*1024);
			sleep(1);
		}
		send = send -i;
		vPutKey(semid);
		printf("cnt is %d\n",++cnt);;
		if(cnt ==3){
			break;
		}
	}
	if(shmdt(send) == -1){
		perror("shmdt");
		exit(-4);
	}else{
		printf("shmdt successed\n");
	}
 
	if(shmctl(shmid,IPC_RMID,NULL) == -1){
		perror("shmctl");
		exit(-5);
	}else{
		printf("shmctl successed!\n");
	}
 
	return 0;
}
 

shm_sem_receive.c

#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <stdlib.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) */
};
 
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("get key\n");
}
void vPutKey(int id)
{
        struct sembuf set;
        set.sem_num = 0;
        set.sem_op = 1;
        set.sem_flg = SEM_UNDO;
 
        semop(id,&set,1);
        printf("put key\n");
}
 
 
int main()
{
	key_t key_shm;
	key_t key_sem;
	int shmid;
	int semid;	
	char *rcv;
 
	if((key_shm = ftok(".",1)) == -1){
		perror("ftok");
		exit(-1);
	}
 
	printf("ftok successed!key is %x\n",key_shm);
 
	if((shmid =shmget(key_shm,4*1024,IPC_CREAT|0666)) == -1){
		perror("shmget");
		exit(-2);
	}else{
		printf("shmget successed! shmid is %d\n",shmid);
	}
 
	if(*(rcv=shmat(shmid,0,0)) == -1){
		perror("shmat");
		exit(-3);
	}else{
		printf("shmat successed!\n");
	}
 
	if((key_sem = ftok(".",2)) == -1){
                perror("ftok");
                exit(-7);
        }
        printf("sem ftok successed! key is 0x%x\n",key_sem);
 
        if((semid =semget(key_sem,1,IPC_CREAT|0666) == -1)){
                perror("semget");
                exit(-6);
        }
 
 
	while(1){
		pGetKey(semid);
		sleep(1);
		printf("%s\n",rcv);
		vPutKey(semid);
	}
 
	if(shmdt(rcv) == -1){
		perror("shmdt");
		exit(-4);
	}else{
		printf("shmdt successed\n");
	}
 
	if(shmctl(shmid,IPC_RMID,NULL) == -1){
		perror("shmctl");
		exit(-5);
	}else{
		printf("shmctl successed!\n");
	}
 
	return 0;
 
}
 
 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的 Android Socket 服务端的示例代码: ``` import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.ServerSocket; import java.net.Socket; public class SocketServer { private ServerSocket serverSocket; private Socket clientSocket; private BufferedReader input; public void start(int port) { try { serverSocket = new ServerSocket(port); System.out.println("Server started, listening on port " + port); // Wait for client connection clientSocket = serverSocket.accept(); System.out.println("Client connected: " + clientSocket.getInetAddress().getHostName()); // Get input stream from client input = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); // Read input from client String inputLine; while ((inputLine = input.readLine()) != null) { System.out.println("Client says: " + inputLine); } // Close streams and sockets input.close(); clientSocket.close(); serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { SocketServer server = new SocketServer(); server.start(8080); } } ``` 在这个示例中,我们创建了一个 `SocketServer` 类,其中包含 `start` 方法,它将启动一个服务端套接字,并等待客户端连接。一旦客户端连接,我们获取其输入流,并读取来自客户端的输入。该示例假设客户端将使用文本输入,因此我们使用 `BufferedReader` 来读取输入。读取完毕后,我们关闭流和套接字。 要使用此代码,您需要将其添加到 Android 应用程序中,并在需要启动服务端套接字的地方调用 `start` 方法。请注意,由于 Android 应用程序可能会受到网络限制,因此您可能需要在清单文件中请求网络权限。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值