4月「掘金·日新计划」第7天
一、通信方式实践使用
2.1、管道
2.1.1、pipe
#include <unistd.h>
int pipe(int fd[2]);
返回值:成功0,失败-1设置erron
成功建立2文件描述符,fd[0]读,fd[1]写
用法举例:
- 建立管道:pipe(fd),判断是否成功
- 创建子进程:fork,判断是否成功
- 在父子进程读写:父进程写,子进程读
- 父:close(fd[0]),write(fd[1],内容,个数) 子:close(fd[1]),read(fd[0],,)
- 注:写时要关闭读管道,读时要关闭写管道 读写方向一但固定就很少改变。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main ()
{
pid_t pid;
int fd[2];
char a[10] = {0};
if(pipe(fd) == -1)
{
perror("pipe");
exit(-1);
}
pid = fork();
if(pid < 0)
{
perror("fork");
exit(-1);
}
else if(pid == 0)
{
close(fd[1]);
read(fd[0],a,strlen("xiao wei"));
printf("%s\n",a);
exit(0);
}
else
{
close(fd[0]);
write(fd[1],"xiao wei",strlen("xiao wei"));
wait(NULL);
}
return 0;
}
2.1.2、mkfifo
int mkfifo(const char* pathname,mode_t mode)
创建fifo管道文件p文件类型
返回值:成功0 ,失败-1,可以用perror查看失败原因 参数:
-
和open一样,文件路径名,0600(读写)
-
创建后使用open打开文件
- 可设置是否阻塞O_NONBLOCK,默认是阻塞(一般阻塞,不然失去意义)。
用法举例:
-
读进程
- open打开管道
- read阻塞等待write(要用while)
- 输出读的数据
- close关闭管道
-
写进程
- open打开管道
- write写数据
- close关闭管道
-
注:管道文件数据读后立马清空,先进先出
/*读代码*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
int main ()
{
int cnt = 3;
int fd;
char a[14] = {0};
if(mkfifo("./fifo",0600) == -1 && errno != EEXIST)
{
perror("pipe");
exit(-1);
}
fd = open("./fifo",O_RDONLY);
if(fd == -1)
{
perror("open");
}
while(cnt)
{
read(fd,a,13);
printf("read:%s\n",a);
cnt--;
}
close(fd);
return 0;
}
/*写代码*/
int main ()
{
int cnt = 3;
int fd;
char *a = "xiao wei ...";
fd = open("./fifo",O_WRONLY);
if(fd == -1)
{
perror("open");
exit(-1);
}
while(cnt)
{
write(fd,a,strlen(a));
sleep(3);
cnt--;
}
close(fd);
return 0;
}
/*运行结果*/
/*
读进程那边输出:
read:xiao wei ...
read:xiao wei ...
read:xiao wei ...
写进程没3秒发数据一次,发3次后退出程序
*/
2.2、msgget,msgsnd,msgrcv,msgctl,ftok
#include <sys/msg.h>
int msgget(key_t key,int flag)
功能:创建或打开消息队列,如果有就打开,没有就创建
参数:
- key键值(哪一个队列),可以用ftok函数,也可以直接0x1234
- 创建队列IPC_CREAT,打开方式可读可写可执行0777
返回值:返回队列id,失败-1
int msgsnd(int msqid,const void*ptr,size_t size,int flag)
功能:添加消息
参数:
-
队列号key
-
添加的消息内容,一般用结构体指针
struct msgbuf { long mtype; char mtext[123]; } -
个数strlen,结构体的mtext大小
-
0非阻塞
返回值:成功0,失败-1
int msgrcv(int msqid,void*ptr,size_t size,long type,int flag)
功能:读取消息
参数:
- 队列号key
- 结构体指针
- 个数strlen,结构体的mtext大小
- 消息类型,结构体的myupe(就是队列的哪一个结构体数据)
- 0阻塞等待
返回值:成功返回数据长度,失败-1
int msgctl(int msqid,int cmd,struct msqid_ds*buf)
功能:控制消息队列,释放移除队列
参数:
- 队列号key
- 常用IPC_RMID,释放移除队列
- 通常NULL
返回值:成功0,失败-1
key_t ftok(const char*fname,int id)
功能:返回key,通常用在消息队列
参数:
- 当前目录,索引值(inode号码)
- id,随便一个数
返回值:key,队列号
用法举例:
-
读进程
- 创建结构体
- 创建打开队列
- 接受消息
- 移除队列
-
写进程
- 创建结构体
- 创建打开队列
- 发送消息
- 移除队列
/*读代码*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[128]; /* message data */
};
int main()
{
struct msgbuf readbuf;
memset(&readbuf,0,sizeof(struct msgbuf));
key_t key;
key = ftok(".",'z');
int msgid = msgget(key,IPC_CREAT|0777);
if(msgid == -1)
{
printf("get que failuer\n");
}
msgrcv(msgid,&readbuf,sizeof(readbuf.mtext),666,0);
printf("readbuf:%s\n",readbuf.mtext);
msgctl(msgid,IPC_RMID,NULL);
return 0;
}
/*写代码*/
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[128]; /* message data */
};
int main()
{
struct msgbuf writebuf = {666,"xiao wei..."};
key_t key;
key = ftok(".",'z');
int msgid = msgget(key,IPC_CREAT|0777);
if(msgid == -1)
{
printf("get que failuer\n");
}
msgsnd(msgid,&writebuf,strlen(writebuf.mtext),0);
msgctl(msgid,IPC_RMID,NULL);
return 0;
}
/*运行结果*/
/*
先运行读
在运行写
读端输出
readbuf:xiao wei...
后结束程序
*/
2.3、shmget,shmat,shmdt,shmctl
int shmget(key_t key,size_t size,int flag)
功能:创建打开共享内存
参数:
- key,用ftok
- 创建内存大小,必须以m为单位
- 创建共享内存IPC_CREAT,可读写0666
返回值:成功返回id,失败-1
void *shmat(int shm_id,const void* addr,int flag)
功能:连接共享内存地址空间
参数:
- 共享内存id
- 0
- 0
返回值:成功地址void可以是任何类型例如结构体,失败-1
注:这个指针不需要free释放
int shmdt(void* addr)
功能:断开连接
参数:地址
返回值:成功0,失败-1
int shmctl(int shm_id,int cmd,struct shmid_ds* buf)
功能:控制共享内存,释放
参数:
- id
- IPC_RMID
- 0
返回值:成功0,失败-1
size_t getpagesize(void);
功能:返回一分页的大小,单位为字节(byte)。此为系统的分页大小,不一定会和硬件分页大小相同。
用法举例:
-
创建打开共享内存
-
连接映射共享内存
-
读写数据(用memcpy,strcpy) 尤其在接收端,必须要拷贝到本地的结构体中,本地化才行!!!
不要直接操作共享内存内容 原文链接:blog.csdn.net/shine_journ…
-
断开连接
-
释放干掉共享内存
//发
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
struct student{
int age;
char name[12];
};
int main(int argc, char **argv)
{
int shm_id;
struct student *shmatadd;
struct student stu = {
.age = 24,
.name = "xiaowei",
};
key_t key;
key = ftok(".",'a');
shm_id = shmget(key,getpagesize(),IPC_CREAT|0666);
if(shm_id == -1) {
perror("shmget()");
}
shmatadd = (struct student *)shmat(shm_id,0,0);
printf("shmat ok\n");
memcpy(shmatadd, &stu, sizeof(stu));
sleep(5);
shmdt(shmatadd);
shmctl(shm_id,IPC_RMID,0);
return 0;
}
//收
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <string.h>
struct student{
int age;
char name[12];
};
int main(int argc, char **argv)
{
int shm_id;
struct student *shmatadd;
struct student stu;
key_t key;
key = ftok(".",'a');
shm_id = shmget(key,getpagesize(),IPC_CREAT|0666);
if(shm_id == -1) {
perror("shmget()");
}
shmatadd = (struct student *)shmat(shm_id,0,0);
memcpy(&stu, shmatadd, sizeof(stu));
printf("shmat ok\n");
printf("stu.age=%d\n",stu.age);
printf("stu.name=%s\n",stu.name);
shmdt(shmatadd);
printf("quit\n");
return 0;
}
2.4、信号
2.4.1、signal,kill,atoi,sprintf
typedef void(*sighandler_t)(int) //函数指针类型
sighandler_t signal(int signum,sighandler_t handler)
功能:信号注册,信号忽略
参数:
- 信号编号
- 处理函数,函数指针类型 SIG_ICN忽略信号
int kill(pid_t pid,int sig)
atoi()//char转int
sprintf()//构造字符串,参数:字符串地址,和printf一样参数
功能:发送信号
参数:pid,信号
2.4.2、sigaction,sigqueue
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
功能:
参数:
-
信号编号
-
sigaction结构体指针,一般用法,配置24
struct sigaction { void (*sa_handler)(int); void (*sa_sigaction)(int, siginfo_t *, void *); sigset_t sa_mask; int sa_flags; void (*sa_restorer)(void); };-
处理函数,和signal的一样,不能传递消息
-
处理函数,可以传递信息的,3个参数
-
整形数,信号编号
-
结构体
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 */ long si_band; /* Band event (was int in glibc 2.3.2 and earlier) */ int si_fd; /* File descriptor */ short si_addr_lsb; /* Least significant bit of address (since Linux 2.6.32) */ void *si_lower; /* Lower bound when address violation occurred (since Linux 3.19) */ void *si_upper; /* Upper bound when address violation occurred (since Linux 3.19) */ int si_pkey; /* Protection key on PTE that caused fault (since Linux 4.6) */ void *si_call_addr; /* Address of system call instruction (since Linux 3.5) */ int si_syscall; /* Number of attempted system call (since Linux 3.5) */ unsigned int si_arch; /* Architecture of attempted system call (since Linux 3.5) */ }-
pid
-
si_init,int数据
-
si_valut 联合体
union sigval{ int sival_int; void *sival_ptr; };- 整形,字符串,结构体等
-
-
指针,空无数据,非空有数据
-
-
是否阻塞,默认阻塞,处理这个信号的时候不处理其他信号
-
SA_SIGINFO,收数据必须设置为这个宏
-
一般不用
-
-
sigaction结构体指针,备份原操作,可以NULL
int sigqueue(pid_t pid, int sig, const union sigval value);
功能:发送消息,信号
参数:
- 发给谁pid
- 发什么信号
- 数据联合体(整形,字符,结构体都可以)
用法举例:
- 2个程序,一个传信号,一个收信号
- 传,用完整main,2个参数转int,给谁传,传什么信号
- 收,打印,谁传的pid,传的数据是什么结构体(进程之间不能传递指针,所以无法实现)传int
- 进程之间,无法传递指针:blog.csdn.net/Dontla/arti…
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
void handler(int signum, siginfo_t *info, void *context)
{
printf("get 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("from:%d\n",info->si_pid);
}
}
int main()
{
struct sigaction buf;
buf.sa_sigaction = handler;
buf.sa_flags = SA_SIGINFO;
sigaction(SIGUSR1,&buf,NULL);
while(1);
return 0;
}
/*输出
get signum 10
get data:10
get data:10
from:48116
*/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc,char **argv)
{
int signum;
int pid;
union sigval value;
value.sival_int = 10;
signum = atoi(argv[1]);
pid = atol(argv[2]);
sigqueue(pid,signum,value);
printf("%ddone\n",getpid());
return 0;
}
2.5、semget,semop,semctl
int semget(key_t key,int num_sems,int sem_flags)
功能:创建,获取信号集
参数:
- key
- 信号量个数
- IPC_CREAT|0666,和共享内存的一样
返回值:信号量集合id
int semop(int semid,struct sembuf *sops,size_t numops)
功能:操作信号量组,改变信号量值
参数:
-
信号量集id
-
结构体指针,可以操作多个信号量,就是指针数组
- sem_num,信号量编号
- sem_op,原来的钥匙,-1 或 1
- sem_flg,SEM_UNDO,拿不到锁阻塞
-
操作几个信号量
int semctl(int semid,int sem_num,int cmd,...)
功能:控制信号量(设置信号量值,几把锁)
参数:
-
信号量集id
-
操作第几个信号量
-
很多宏
- SETVAL设置信号量值
- IPC_RMID销毁
-
联合体,union semun,开头要定义
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) */ };- val,初始化信号量值,几把锁
用法举例:
- 创建信号量组
- 初始化信号量
- 创建父子进程
- 写2个函数,一个信号量加,一个减
- 子进程运行完信号量加
- 父进程先减然后运行在加
- 销毁锁
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.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("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()
{
key_t key;
int sem_id;
key = ftok(".",'a');
union semun initsem;
initsem.val = 0;//锁初始没有钥匙
sem_id = semget(key,1,IPC_CREAT|0666);
semctl(sem_id,0,SETVAL,initsem);
int pid = fork();
if(pid > 0){
pGetKey(sem_id);//父进程那不到,因为没有钥匙
printf("this is father\n");
vPutBackKey(sem_id);
}else if(pid == 0){
printf("this is child\n");
vPutBackKey(sem_id);//子进程执行完,放钥匙进去
}else
{
printf("fork error\n");
exit(-1);
}
semctl(sem_id,0,IPC_RMID);
return 0;
}