20155321《信息安全系统设计》第十周 课下作业——研究Linux下IPC机制
共享内存
-
共享内存:因为支持多个进程共享同一块内存空间,并且数据不需要在客户进程和服务器进程之间进行复制,因此共享内存可以使得通信速度十分快速
-
共享内存示意图
-
通过
man
命令查询相关的系统内核函数
发现以下有五个函数与共享内存有关系,但通过man
命令可知,对于最后一个shmop函数的解释就是shmat函数:-
int shmget(key_t key, size_t size, int shmflg):共享内存的建立或者打开
-
void shmat(int shmid,const void shmaddr,int shmflg):将一个共享内存空间映射到调用进程的地址空间上,返回在进程地址空间中的地址。用户可以通过这个地址间接的访问共享内存
-
int shmdt(const void* shmaddr):将一个进程已经映射了的共享内存脱离进程地址空间
-
int shmctl(int shmid, int cmd, struct shmid_ds *buf):删除已创建好的共享内存
-
-
例子:进程write将键盘上输入的字符串存储到共享内存,read进程将共享内存中的数据读出来
-
运行代码
- 生成共享内存
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#include <errno.h>
typedef struct _Teacher
{
char name[64];
int age;
}Teacher;
int main(int argc, char *argv[])
{
int ret = 0;
int shmid;
shmid = shmget(0x2234, sizeof(Teacher), IPC_CREAT | 0666);
if (shmid == -1)
{
perror("shmget err");
return errno;
}
printf("shmid:%d
", shmid);
Teacher *p = NULL;
//将共享内存段连接到进程地址空间
p = shmat(shmid, NULL, 0);//第二个参数shmaddr为NULL,核心自动选择一个地址
if (p == (void *)-1 )
{
perror("shmget err");
return errno;
}
strcpy(p->name, "aaaa");
p->age = 33;
//将共享内存段与当前进程脱离
shmdt(p);
int num;
scanf("%d", &num);
if (num == 1)
{
//用于控制共享内存
ret = shmctl(shmid, IPC_RMID, NULL);//IPC_RMID为删除内存段
if (ret < 0)
{
perror("rmerrr
");
}
}
return 0;
}
- 获取共享内存
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#include <errno.h>
typedef struct _Teacher
{
char name[64];
int age;
}Teacher;
int main(int argc, char *argv[])
{
int ret = 0;
int shmid;
//shmid = shmget(0x2234, sizeof(Teacher), IPC_CREAT |IPC_EXCL | 0666);
//打开获取共享内存
shmid = shmget(0x2234, 0, 0);
if (shmid == -1)
{
perror("shmget err");
return errno;
}
printf("shmid:%d
", shmid);
Teacher *p = NULL;
//将共享内存段连接到进程地址空间
p = shmat(shmid, NULL, 0);
if (p == (void *)-1 )
{
perror("shmget err");
return errno;
}
printf("name:%s
", p->name);
printf("age:%d
", p->age);
//将共享内存段与当前进程脱离
shmdt(p);
int num;
scanf("%d", &num);
if (num == 1)
{
pause();
}
return 0;
}
- 运行截图
管道
-
管道:它是父进程和子进程间,或是子进程与子进程间单向的通讯机制,即一个进程发送数据到管道,另外一个进程从管道中读出数据。如果需要双向,或是多项通信机制,则需要建立两个或者多个管道
-
例子
- 向管道文件中写数据
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
#define FIFO_NAME "/tmp/myfifo2" //定义宏,指向管道文件位置
#define TEN_MEG ( 1024*1024*10 ) //文件缓冲区最大值
int main( int argc, char* argv[ ] )
{
int res;
int c;
int pipe_fd;
int open_mode = O_WRONLY; //设置读写权限
int bytes_send = 0;
if( access( FIFO_NAME, F_OK ) == -1 ) { //F_OK : 检查是否有这个文件; 类似还有检查文件可读等,参见man中access中定义
res = mkfifo( FIFO_NAME, 0777 ); //创建管道文件,文件属性为0777,root可读写
if( res != 0 ){ //管道文件不可重名
printf( "Could not create fifo %s ", FIFO_NAME );
exit( 1 );
}
}
pipe_fd = open( FIFO_NAME, open_mode ); //打开管道,并设置打开权限
if( pipe_fd !=-1 ){
while( (c = getchar( )) > 0 ){
res = write( pipe_fd, &c, 1 ); //向管道中写数据
if( res == -1 ){
perror( "write error" );
close( pipe_fd );
exit( 1 );
}
}
close( pipe_fd );
}
return 0;
}
- 从管道文件中读数据
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <signal.h>
#define FIFO_NAME "/tmp/myfifo2" //定义宏,指向管道文件的位置
void read_pipe(){ //子进程, 处理从管道中读数据
int pipe_fd;
int res;
int c;
int open_mode = O_RDONLY; //设置权限,为只读
pipe_fd = open( FIFO_NAME, open_mode ); //打开管道文件,并设置打开权限,返回int
if( pipe_fd!=-1 ){
while(1){
while( (res = read( pipe_fd, &c, 1 )) > 0 ){ //从管道读数据
putchar( c );
}
fflush( stdout );
}
}else{
exit( 1 );
}
close(pipe_fd);
}
void signal_handler( int n){ //受到子进程退出信号,结束子进程
int child_status;
wait( &child_status );
printf( "child exited. " );
}
int main( int argc, char *argv[ ] )
{
int pid;
int child_status;
signal(SIGCHLD, signal_handler); //子进程退出时所发信号
pid = fork(); //创建子进程,使之读取管道数据
int i = 0;
switch(pid){
case -1:
printf("fork error");
exit( 1 );
case 0:
read_pipe();
exit( 0 );
default: //做它自己无聊的事
for(i;i<100;i++){
printf("%d ", i);
fflush( stdout );
sleep(2);
}
}
return 0;
}
- 运行截图
FIFO
-
FIFO
-
写入数据
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#define FIFO_NAME "/tmp/my_fifo"
#define BUFFER_SIZE PIPE_BUF
#define TEN_MEG (1024 * 1024 * 10)
int main()
{
int pipe_fd;
int res;
int open_mode = O_WRONLY;
int bytes_sent = 0;
char buffer[BUFFER_SIZE + 1];
/* 检查FIFO_NAME文件是否存在,如果不存在就创建它 */
if (access(FIFO_NAME, F_OK) == -1) {
/* mkfifo创建命名管道(即特殊类型的文件FIFO) */
res = mkfifo(FIFO_NAME, 0777);
if (res != 0) {
fprintf(stderr, "Could not create fifo %s
", FIFO_NAME);
exit(EXIT_FAILURE);
}
}
printf("Process %d opening FIFO_OWRONLY
", getpid());
/* open函数以O_WRONLY方式打开FIFO文件,如果成功pipe_fd指向打开的文件 */
pipe_fd = open(FIFO_NAME, open_mode);
printf("Process %d result %d
", getpid(), pipe_fd);
if (pipe_fd != -1) {
/* */
while(bytes_sent < TEN_MEG) {
/* write函数从buffer指向的内存中写入BUFFER_SIZE个字节到pipe_fd文件中
* 如果成功则返回实际写入的字节数 */
res = write(pipe_fd, buffer, BUFFER_SIZE);
if (res == -1) {
fprintf(stderr, "Write error on pipe
");
exit(EXIT_FAILURE);
}
bytes_sent += res;
}
(void)close(pipe_fd);
} else {
exit(EXIT_FAILURE);
}
printf("Process %d finished
", getpid());
exit(EXIT_SUCCESS);
}
- 从FIFO读取数据并丢弃
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#define FIFO_NAME "/tmp/my_fifo"
#define BUFFER_SIZE PIPE_BUF
int main()
{
int pipe_fd;
int res;
int open_mode = O_RDONLY;
char buffer[BUFFER_SIZE + 1];
int bytes_read = 0;
memset(buffer, ' ', sizeof(buffer));
printf("Process %d opening FIFO O_RDONLY
", getpid());
/* open函数打开FIFO_NAME文件,以open_mode的方式(即O_RDONLY)
* 如果成功,则返回文件描述符 */
pipe_fd = open(FIFO_NAME, open_mode);
printf("Process %d result %d
", getpid(), pipe_fd);
if (pipe_fd != -1) {
do {
/* read函数从pipe_fd指向的文件中读入BUFFER_SIZE个字节的数据到buffer指向的内存
* 如果成功,返回实际读入数据的字节数 */
res = read(pipe_fd, buffer, BUFFER_SIZE);
bytes_read += res;
} while (res > 0);
(void)close(pipe_fd);
} else {
exit(EXIT_FAILURE);
}
printf("Process %d finished, %d bytes read
", getpid(), bytes_read);
exit(EXIT_SUCCESS);
}
- 运行截图
信号
-
信号处理流程
- 信号诞生
- 信号在进程中注册
- 信号的执行和注销
-
信号的发送
- kill()
- raise()
- sigqueue()
- alarm()
- setitimer()
- abort()
-
例子
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <signal.h>
void func(int sig)
{
printf("I get a signal!
");
}
int main()
{
char buffer[100];
struct sigaction act;
act.sa_handler=func;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
if(sigaction(SIGINT,&act, NULL) == -1)
{
printf("sigaction error exit now
");
exit(0);
}
printf("pid:%ld
",(long)getpid());
while(1)
{
fgets(buffer,sizeof(buffer),stdin);
printf("buffer is:%s
",buffer);
}
return 0;
}
- 运行截图
运行中通过快捷键Ctrl+C
中断进程
消息队列
-
消息队列:提供一种从一个进程向另一个进程发送一个数据块的方法。
-
每个数据块都被认为含有一个类型,接收进程可以独立地接收含有不同类型的数据结构。可以通过发送消息来避免命名管道的同步和阻塞问题
-
例子
- 发送消息
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/msg.h>
#include <errno.h>
#define MAX_TEXT 512
struct msg_st
{
long int msg_type;
char text[MAX_TEXT];
};
int main()
{
int running = 1;
struct msg_st data;
char buffer[BUFSIZ];
int msgid = -1;
//建立消息队列
msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
if(msgid == -1)
{
fprintf(stderr, "msgget failed with error: %d
", errno);
exit(EXIT_FAILURE);
}
//向消息队列中写消息,直到写入end
while(running)
{
//输入数据
printf("Enter some text: ");
fgets(buffer, BUFSIZ, stdin);
data.msg_type = 1; //注意2
strcpy(data.text, buffer);
//向队列发送数据
if(msgsnd(msgid, (void*)&data, MAX_TEXT, 0) == -1)
{
fprintf(stderr, "msgsnd failed
");
exit(EXIT_FAILURE);
}
//输入end结束输入
if(strncmp(buffer, "end", 3) == 0)
running = 0;
sleep(1);
}
exit(EXIT_SUCCESS);
}
- 接收消息
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/msg.h>
struct msg_st
{
long int msg_type;
char text[BUFSIZ];
};
int main()
{
int running = 1;
int msgid = -1;
struct msg_st data;
long int msgtype = 0; //注意1
//建立消息队列
msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
if(msgid == -1)
{
fprintf(stderr, "msgget failed with error: %d
", errno);
exit(EXIT_FAILURE);
}
//从队列中获取消息,直到遇到end消息为止
while(running)
{
if(msgrcv(msgid, (void*)&data, BUFSIZ, msgtype, 0) == -1)
{
fprintf(stderr, "msgrcv failed with errno: %d
", errno);
exit(EXIT_FAILURE);
}
printf("You wrote: %s
",data.text);
//遇到end结束
if(strncmp(data.text, "end", 3) == 0)
running = 0;
}
//删除消息队列
if(msgctl(msgid, IPC_RMID, 0) == -1)
{
fprintf(stderr, "msgctl(IPC_RMID) failed
");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
- 运行截图