嵌入式Linux视频笔记----Linux内核--进程和进程间通信

7 篇文章 0 订阅

野火【第二期】Linux系列教学视频之“内核编程”篇,手把手教学,硬件基于野火i.MX6ULL Pro/MINI开发板_哔哩哔哩_bilibili

看第一遍只简单总结了一些文字,第二遍实操、增加大量截图

P1 第48讲 进程的由来

程序是静态文件,进程是运行中的实体

查看进程间的关系:pstree


区分进程:PID;ps -ef | more【每次只显示一屏,按q退出】

P2 第49讲 创建一个进程

fork函数:头文件、函数原型、返回值

函数特性复制1个进程;返回两次--新进程返回0,老进程返回新进程PID;fork之前代码执行1次,之后代码执行2次

c函数演示

P3 第50讲 子进程偷梁换柱

执行fork函数后子进程仍然与父进程程序相同,使用exec函数可以让子进程运行不同的程序

exec函数族l--以列表list形式传参;lp--使用环境变量Path来寻找指定文件;v--以矢量数组vector形式传参;ve--用户提供自定义的环境变量

execl用法,将父进程替换为  ls 并传递参数 -lh 【指定ls程序路径】

execlp用法,将父进程替换为  ls 并传递参数 -lh【在PATH路径中寻找ls程序】

execv用法,将父进程替换为  ls 并传递参数 -lh

execve用法,貌似只是打印了env的字符

要点l v 2选1;p e为可选功能;排列组合任选;可能执行失败,需要注意--路径错误、参数错误、权限不足

P4 第51讲 进程的退出

正常退出:main中return、exit、_exit


exit和_exit:_exit直接退出,不考虑IO缓存区;exit先处理IO缓存区

子进程直接退出,无打印输出;父进程退出前处理IO缓冲区,打印 parent

P5 第52讲 等待子进程的终结

wait函数:通过wait函数获取子进程退出状态;成功则返回子进程ID;子进程退出前会一直阻塞父进程【实际上不会直接用wait阻塞,而是子进程退出前发信号,父进程收到信号后调用wait】;子进程退出状态相关宏

若忘记函数对应的头文件,可以使用man命令查找,如下图

man fork
父进程打印子进程的返回值

P6 第53讲 进程的生老病死

就绪态、运行态、睡眠态【可中断即响应中断,不可中断即不响应中断】、暂停态【包含暂停态和调试态,收到信号进入暂停态,收到调试命令进入调试态】、僵尸态、死亡态
转换关系、对应宏

P7 第54讲 进程组、会话、终端

进程组:管理相同类型的进程;

诞生--shell中执行应用程序、父进程fork生成子进程、shell中使用管道连接的应用程序;

进程组ID PGID 即首进程ID;

会话:管理进程组

诞生--调用setsid函数、shell登录;

会话ID SID即会话首进程ID;

 


前台进程组占用终端;后台转前台fg ID

后台进程组不占用终端;指定为后台 & ctrl+z;jobs查看后台进程组

进程sleep占用终端5s
& 后台进程不占用终端      ctrl+z前台转后台且停止进程
jobs查看后台进程组
fg+jobid 后台进程组切换为前台


终端:开发板物理终端--串口、LCD;

伪终端--ssh远程连接、桌面系统启动的终端;

虚拟终端--linux内核自带,ctrl+alt+f0~f6

实测ctrl+alt+f2~f6进入相应终端,ctrl+alt+f1返回ubuntu图形界面

P8 第55讲 守护进程

会话管理前后台进程组,会话一般关联1个终端,终端关闭,会话进程全关闭。

sleep 999进程PID=2106
关掉中断后重新打开  sleep 999进程消失


守护进程不受终端影响,始终后台运行;创建守护进程--fork、setsid、chdir、umask、close;

umask对文件权限的影响
注意共享文件夹ebf_dir比较特殊,不符合视频中的规则
注意执行时加sudo,否则打开文件失败
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>

#define MAXFILE 3

int main()
{
	pid_t pid;
	int fd,len,i,num,cnt=0;
	char *buf="the daemon is running\n";
	char cnt_str[20];
	int cnt_str_len;

	len = strlen(buf)+1;

	//1 创建子进程,销毁父进程
	pid = fork();		
	if(pid<0)			
	{
		printf("fork fail\n");
		exit(1);
	}

	if(pid>0)			//退出父进程
		exit(0);

	printf("son ok!\n");
	fd = open("/var/log/daemon.log",O_CREAT|O_WRONLY|O_APPEND,0666);
	printf("file ID is %d\n",fd);
	if(fd<0)
		exit(66);

	//2 创建新会话,摆脱终端影响
	setsid();			//子进程
	
	//3 改变当前工作目录
	chdir("/"); 

	//4 重设文件权限掩码
	umask(0);			//相当于umask无效

	//5 关闭默认的文件描述符
	for(i=0;i<MAXFILE;i++)
		close(i);

	//6 实现守护进程的功能
	while(1)
	{
		cnt++;
		cnt_str_len = snprintf(cnt_str,20,"%d\t",cnt);		//相当于行号

		fd = open("/var/log/daemon.log",O_CREAT|O_WRONLY|O_APPEND,0666);
		write(fd,cnt_str,cnt_str_len);
		write(fd,buf,len);
		close(fd);
		sleep(1);
	}
}

普通进程伪装成守护进程nohup

关闭终端后,新打开终端,进程依然存在

P9 第56讲 ps命令详解

历史悠久、派系众多导致用法复杂
aux、axjf 字母含义:a--显示1个终端所有进程;u--显示进程的归属用户及内存使用情况;x--显示没有关联控制终端的进程;j--显示进程归属的进程组ID、会话ID、父进程ID;f--以ASCII形式显示出进程的层次关系

ps aux:各项内容;VSZ--虚拟内存大小;RSS--物理内存大小;TTY--关联终端;STAT--进程状态 熟悉DRSTXZ;command进程执行的具体程序;


 

ps axjf:各项内容;pgid进程组ID;tpgid守护进程;UID启动进程的用户;command进程的层次关系


使用场景:关注进程本身--ps aux;关注进程间关系--ps axjf

P10 第57讲 僵尸进程和托孤进程

进程的正常退出步骤:子进程exit、父进程wait;


僵尸进程:只有exit,没有wait

前台运行时需要手动终止,看不到僵尸进程
后台运行时PID1862即为僵尸进程
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
	int pid;

	if( (pid=fork()) < 0)
	{
		printf("fail to fork\n");
		return -1;
	}
	else
		if(pid==0)
		{
			printf("child exit now\n");
			exit(0);			//子进程打印后退出
		}
		else
		{
			while(1)			//父进程没有调用wait,子进程变为僵尸进程
				sleep(1);		//加点延时,避免ubuntu不响应
		}

	return 0;
}


托孤进程:父进程先结束,linux会把子进程托孤给1号进程init

P11 第58讲 什么是进程间通信(ipc)

数据传输、资源共享、事件通知、进程控制

linux系统4种ipc:项目开发常用posix

早期unix系统ipc--管道 数据传输、信号 事件通知、fifo 数据传输;

system-v ipc(贝尔实验室)--消息队列信号量 资源共享 进程控制、共享内存 数据传输【高效】;

socket ipc(BSD)--复杂,允许不同机器间通信;

posix ipc(IEEE)--消息队列、信号量、共享内存

P12 第59讲 无名管道

pipe函数【父子进程之间传输数据】

特点:特殊文件,无法open,可以close;子进程继承文件描述符;读写可能会阻塞进程【读空时读 或 写满时写】;相关文件关闭后管道销毁。

 使用步骤:pipe、fork、close无用端口【一般1个进程不会同时使用读写端口】、write read、close读写端口

C文件演示:父进程写、子进程读

//父进程写、子进程读

#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_DATA_LEN 256

int main()
{
	pid_t pid;
	int pipe_fd[2];				//0--读描述符	1--写描述符
	int status;
	char buf[MAX_DATA_LEN];
	const char data[]="Pipe test program";
	int real_read_num,real_write_num;

	memset( (void *)buf,0,sizeof(buf) );

	//创建管道
	if( pipe(pipe_fd)<0 )			//创建管道失败
	{
		printf("pipe create error\n");
		exit(1);
	}

	//创建子进程
	if( (pid=fork()) == 0 )			//创建管道成功则创建子进程
	{
		//子进程关闭写描述符
		close(pipe_fd[1]);

		//子进程读取管道内容【无数据则阻塞】
		if(  ( real_read_num = read(pipe_fd[0],buf,MAX_DATA_LEN) ) > 0  )
			printf("%d bytes read form the pipe is '%s'\n",real_read_num,buf);
	
		//关闭子进程读描述符
		close(pipe_fd[0]);

		exit(0);
	}
	else if(pid>0)
	{
		//父进程关闭读描述符
		close(pipe_fd[0]);
	
		//父进程写数据【缓冲区满则阻塞】
		if(    ( real_write_num = write(pipe_fd[1],data,strlen(data)) ) != -1   )
			printf("Parent write %d bytes : '%s'\n",real_write_num,data);
		
		//关闭父进程读描述符
		close(pipe_fd[1]);

		//收集子进程退出信息
		wait(&status);

		exit(0);
	}

}

P13 第60讲 有名管道

mkfifo函数【任意进程之间传输数据】

特点:有文件名,可以open;任意进程间传输数据;读写可能会阻塞进程【读空时读 或 写满时写】;write有原子性,要么全写,要么不写【缓冲区剩余空间足够则写,否则不写】

使用步骤:mkfifo、进程1 open write read、close、进程2 open write read、close

C文件演示:进程1写、进程2读

未执行读,先执行写,写进程第1次执行参数错误,第2次执行打开fifo失败
随后执行读,成功创建 fifo管道文件,打开fifo时被阻塞
写执行3次,收到3次,收阻塞在读fifo,收进程ID为2160,运行态
中断读进程后,再次执行读,管道文件已经存在
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/wait.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//有名管道文件名
#define MYFIFO "/tmp/myfifo"

//4096 定义在 limits.h 中
#define MAX_BUFFER_SIZE PIPE_BUF


//参数为即将写入的字符串
int main(int argc,char *argv[])
{
	char buff[MAX_BUFFER_SIZE];
	int fd;
	int nread;

	//判断有名管道是否已存在,若尚未创建,则以相应的权限【666可读可写】创建
	if( access(MYFIFO,F_OK) == -1 )	
	{
		printf("Fifo doesn't exist\n");			//fifo不存在
		if( (mkfifo(MYFIFO,0666)<0) && (errno!=EEXIST)  )
		{
			printf("Can't create fifo file\n");	//创建fifo失败
			exit(1);
		}
		printf("Create fifo success\n");		//创建fifo成功
	}
	else
		printf("Fifo exist\n");				//fifo存在

	//以只读阻塞方式打开有名管道
	fd = open(MYFIFO,O_RDONLY);				
	
	if(fd==-1)
	{
		printf("Open fifo file error");			//打开fifo失败	
		exit(1);
	}

	printf("Open fifo file success\n");			//打开fifo成功	
	//循环读取有名管道数据
	while(1)
	{
		memset(buff,0,sizeof(buff));
		if( (nread = read(fd,buff,MAX_BUFFER_SIZE)) > 0  )
			printf("Read '%s' from fifo\n",buff);
	}
}
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/wait.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//有名管道文件名
#define MYFIFO "/tmp/myfifo"

//4096 定义在 limits.h 中
#define MAX_BUFFER_SIZE PIPE_BUF


//参数为即将写入的字符串
int main(int argc,char *argv[])
{
	char buff[MAX_BUFFER_SIZE];
	int fd;
	int nwrite;

	if(argc<=1)
	{
		printf("Usage: ./fifo_write string\nPara error\n");
		exit(1);
	}

	//填充命令行第一个参数到buff
	sscanf(argv[1],"%s",buff);


	//以只写阻塞方式打开有名管道
	printf("Ready to open fifo\n");
	fd = open(MYFIFO,O_WRONLY);				//若管道未创建,则阻塞在这里	
	
	if(fd==-1)
	{
		printf("Open fifo file error\n");		//打开fifo失败	
		exit(1);
	}
	printf("Open fifo file success\n");

	//向管道中写入字符串
	if( (nwrite=write(fd,buff,MAX_BUFFER_SIZE)) > 0 )	
		printf("Write '%s' to fifo\n",buff);

	close(fd);
	exit(0);
}

P14 第61讲 信号简介

软件模拟中断,进程接收信号后作出相应响应

所有可用的62种信号,均以SIG开头【没有32 33】


产生信号硬件--执行非法指令、访问非法内存、驱动程序通知;软件--控制台命令、kill命令、程序调用kill函数

3种杀死进程的方式
注意停止态的进程使用kill -9才能杀死
注意停止信号后进程只是进入停止态,但仍然存在
中断信号、退出信号,进程不存在

信号处理方式:忽略、捕获并调用函数、系统默认

P15 第62讲 常用信号分析

常用的是UNIX早期信号【kill -l的前31个信号】

SIGHUP--关闭终端;SIGINT--ctrl+c;SIGQUIT SIGABRT--ctrl+\  abort() 终止+转储【记录程序结束前的信息】;SIGPE--算术错误;SIGKILL--kill -9 不可捕获 不可忽略;SIGUSR1 SIGUSR2--自定义;SIGSEGV--段错误、非法内存;SIGALRM--alarm()【类似定时关机】;SIGTERM--kill pid;SIGCHLD--子进程状态变化;SIGSTOP【截图少了S】--ctrl+z 不可捕获 不可忽略

pkill命令:不看进程PID,使用进程名称

P16 第63讲 signal_kill_raise函数

signal函数:设置信号处理方式,设置信号对应的处理函数

//第1次ctrl+c打印字符,第2次ctrl+c终止运行

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


//信号处理函数
void signal_handler(int sig)
{
	printf("\n\nThe signal number is %d\n",sig);

	if(sig==SIGINT)
	{
		printf("I have get SIGINT\n");
		printf("The signal has been restored to the default processing mode\n\n");

		//恢复信号为默认情况
		signal(SIGINT,SIG_DFL);
	}
}
		

int main(int argc,char *argv[])
{
	printf("\nThis is an signal test function\n\n");
	
	//设置信号处理的回调函数
	signal(SIGINT,signal_handler);

	while(1)
	{
		printf("Waitinig for the SIGINT signal , please enter \"ctrl+c\" ...\n");
		sleep(1);
	}
	
	exit(0);
}

kill函数给其他进程发送信号

raise函数:给本进程发送信号【相比kill,少了参数pid】

子进程停止自己,父进程随后杀死子进程
#include <signal.h>


int main(void)
{
        pid_t pid;
        int ret;

        //创建子进程
        if( (pid=fork()) < 0 )                  //进程创建失败
        {
                printf("Fork error\n");
                exit(1);
        }

        if(pid==0)                              //子进程                        
        {
                //在子进程中使用raise函数发出SIGSTOP信号,使子进程暂停
                printf("Child is waiting for SIGSTOP signal\n\n");

                //子进程停在这里【自己给自己发了停止信号】
                raise(SIGSTOP);

                //子进程不会执行到这里
                printf("Child won't run here forever\n");

                exit(0);
        }
        else                                    //父进程
        {
                //睡眠3秒,让子进程先执行
                sleep(3);

                //发送SIGKILL信号杀死子进程
                if( (ret=kill(pid,SIGKILL)) == 0 )
                        printf("Parent kill child which ID = %d\n\n",pid);

                //一直阻塞直到子进程被杀死
                wait(NULL);

                //父进程退出运行
                printf("Parent exit\n");

                exit(0);
        }
}

P17 第64讲 信号集处理函数

屏蔽信号集:手动;自动--处理某信号时再有信号自动屏蔽

未处理信号集被屏蔽的信号发生时进入未处理信号集;非实时信号1-31,只保留1个;实时信号34-64,FIFO;

非实时信号        未处理信号只保留1个
//运行后打印字符,按一次ctrl+c打印一次

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

void my_func(int signo)
{
        printf("\nhello\n");
        sleep(5);
        printf("       world\n");
}

int main()
{
        signal(SIGINT,my_func);
        while(1);
        return 0;
}
实时信号        未处理信号 排队执行
c文件只改了1行代码        signal( 35,my_func);

信号集API:信号集全部清0、全部置1、某1位置1、某1位清0,sigprocmask信号集生效

解除自动屏蔽SIGINT信号        3个world几乎同时打印【类似3个进程几乎同时执行】
//运行后打印字符,按一次ctrl+c打印一次

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

void my_func(int signo)
{
	//解除自动屏蔽SIGINT信号
	sigset_t set;
	sigemptyset(&set);
	sigaddset(&set,SIGINT);
	sigprocmask(SIG_UNBLOCK,&set,NULL);

	printf("\nhello\n");
	sleep(5);
	printf("       world\n");
}

int main()
{
	signal(SIGINT,my_func);
	//signal(35,my_func);
	while(1);
	return 0;
}

上述代码极易出现ubuntu死机【使用gnome-system-monitor检测时也死机】,重启后就好,不知道为什么

P18 第65讲 system-V 消息队列

system-V ipc特点:unix系统的第5【V】个版本;独立于进程;key ID类似文件名和文件描述符

消息队列用法ftok函数--获取key;msgget函数--获取或创建消息队列;msgsend函数--发送消息到消息队列;msgrcv函数--读取消息队列消息;msgctl函数--设置获取属性或删除消息队列

2个终端对应2个进程,1收1发,都阻塞在睡眠态,后台运行
实测多次执行发送程序,消息队列从0一直累加
//利用终端发送消息,quit退出

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

#define BUFFER_SIZE 512

struct message
{
	long msg_type;
	char msg_text[BUFFER_SIZE];
};

int main()
{
	int qid;
	key_t key;
	struct message msg;

	//根据不同的路经和关键字产生标准的key
	if( (key=ftok("/tmp",11)) == -1 )
	{
		printf("ftok failed\n");	//生成key失败
		exit(1);
	}

	//创建消息队列并得到ID【可读可写】
	if( (qid=msgget(key,IPC_CREAT|0666)) == -1 )
	{
		printf("msgget failed\n");	//创建消息队列失败
		exit(1);
	}

	printf("Open queue %d success\n",qid);

	while(1)
	{	//从控制台终端读取数据填充至结构体
		printf("Enter some message to the queue:");
		if( (fgets(msg.msg_text,BUFFER_SIZE,stdin)) == NULL )
		{
			puts("no message");
			exit(1);
		}

		msg.msg_type = getpid();

		//添加消息到消息队列【消息发送失败则阻塞在这里】
		if( (msgsnd(qid,&msg,strlen(msg.msg_text),0)) < 0)
		{
			printf("message posted");
			exit(1);
		}

		//在终端输入 quit 结束进程
		if( strncmp(msg.msg_text,"quit",4) == 0 )
			break;
	}
}
//读取消息并打印在终端,quit退出

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

#define BUFFER_SIZE 512

struct message
{
	long msg_type;
	char msg_text[BUFFER_SIZE];
};

int main()
{
	int qid;
	key_t key;
	struct message msg;

	//根据不同的路经和关键字产生标准的key
	if( (key=ftok("/tmp",11)) == -1 )	//参数必须与发送程序一致
	{
		printf("ftok failed\n");	//生成key失败
		exit(1);
	}

	//创建消息队列并得到ID【可读可写】
	if( (qid=msgget(key,IPC_CREAT|0666)) == -1 )
	{
		printf("msgget failed\n");	//创建消息队列失败
		exit(1);
	}

	printf("Open queue %d success\n",qid);

	do
	{	
		//读取消息队列【0--什么数据都要  0--阻塞读取】
		memset(msg.msg_text,0,BUFFER_SIZE);
		if( msgrcv(qid,(void*)&msg,BUFFER_SIZE,0,0) < 0 )
		{
			printf("message received");
			exit(1);
		}
		printf("The message from process %ld : %s",msg.msg_type,msg.msg_text);

	}while( strncmp(msg.msg_text,"quit",4) );

	//从系统内核中移走消息队列
	if( (msgctl(qid,IPC_RMID,NULL)) < 0 )
	{
		printf("msgctl");
		exit(1);
	}

	exit(0);
}

P19 第66讲 system-V 信号量

本质:计数器

作用:保护共享资源--互斥、同步【在互斥的基础上增加顺序】

用法fork函数;semget函数--获取 构造信号量;semctl函数--初始化信号量 获取或设置信号量属性;semop函数--信号量加减操作;删除信号量

利用信号量实现进程同步

先执行子进程,后执行父进程
//封装信号量底层函数
#include <sys/ipc.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/sem.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

union semun
{
	int val;
	struct semid_ds *buf;
};

//初始化信号量,给信号量sem_id赋值init_value
int init_sem(int sem_id,int init_value)
{
	union semun sem_union;
	sem_union.val = init_value;

	//给编号为0的信号量赋值
	if( semctl(sem_id,0,SETVAL,sem_union) == -1 )
	{
		printf("Initialize semaphore\n");
		return -1;
	}

	return 0;
}

//删除信号量
int del_sem(int sem_id)
{
	union semun sem_union;
	if( semctl(sem_id,0,IPC_RMID,sem_union) == -1 )
	{
		perror("Delete semaphore");
		return -1;
	}
}

//P操作,即减操作
int sem_p(int sem_id)
{
	struct sembuf sops;
	sops.sem_num =0;		//单个信号量的编号为0
	sops.sem_op = -1;		//表示P操作
	sops.sem_flg = SEM_UNDO;	//系统自动释放系统中残留的信号量

	if( semop(sem_id,&sops,1) == -1 )
	{
		perror("P operation");
		return -1;
	}
	return 0;
}

//V操作,即加操作
int sem_v(int sem_id)
{
	struct sembuf sops;
	sops.sem_num =0;		//单个信号量的编号为0
	sops.sem_op = 1;		//表示V操作
	sops.sem_flg = SEM_UNDO;	//系统自动释放系统中残留的信号量

	if( semop(sem_id,&sops,1) == -1 )
	{
		perror("V operation");
		return -1;
	}
	return 0;
}
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/sem.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>

#include "sem.h"

#define DELAY_TIME 3

int main(void)
{
	pid_t result;
	int sem_id;

	//创建1个信号量【没有使用ftok函数,自定义6666,权限可读可写】
	sem_id = semget( (key_t)6666,1,0666|IPC_CREAT );

	init_sem(sem_id,0);

	result = fork();
	if(result==-1)
		perror("Fork\n");
	else if(result==0)		//子进程
	{
		printf("Child process will wait for some seconds...\n");
		sleep(DELAY_TIME);
		printf("The child process is running...\n");
		sem_v(sem_id);
	}
	else				//父进程
	{
		sem_p(sem_id);
		printf("The father process is running ...\n");
		sem_v(sem_id);

		del_sem(sem_id);
	}

	exit(0);
}


int init_sem(int sem_id,int init_value);

int del_sem(int sem_id);

int sem_p(int sem_id);

int sem_v(int sem_id);

P20 第67讲 system-V 共享内存

作用:高效率大量数据传输,不同进程虚拟地址映射到同一片物理内存

用法ftok函数;shmget函数--获取 创建共享内存ID;shmat函数--映射共享内存;shmdt函数--解除映射;shmctl函数--获取 设置属性 删除共享内存

利用信号量实现进程同步,通过共享内存传输数据,相比上个视频,仅修改test.c

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>

#include "sem.h"

#define DELAY_TIME 3

int main(void)
{
	pid_t result;
	int sem_id;
	int shm_id;
	char *addr;

	//创建1个信号量【没有使用ftok函数,自定义6666,权限可读可写】
	sem_id = semget( (key_t)6666,1,0666|IPC_CREAT );
	//创建1个共享内存对象【没有使用ftok函数,自定义7777,权限可读可写】
	shm_id = shmget( (key_t)7777,1024,0666|IPC_CREAT );

	init_sem(sem_id,0);

	result = fork();
	if(result==-1)
		perror("Fork\n");
	else if(result==0)		//子进程
	{
		printf("Child process will wait for some seconds...\n");
		sleep(DELAY_TIME);

		//映射共享内存【NULL--映射地址自动设置,0--可读可写】
		addr = shmat(shm_id,NULL,0);
		if(addr==(void *)-1)
		{
			printf("Shmat111 error\n");
			exit(-1);
		}

		//设置共享内存中的内容
		memcpy(addr,"helloworld",11);

		printf("The child process is running...\n");
		sem_v(sem_id);
	}
	else				//父进程
	{
		sem_p(sem_id);
		printf("The father process is running ...\n");

		//映射共享内存地址
		addr = shmat(shm_id,NULL,0);
		if(addr==(void *)-1)
		{
			printf("Shmat222 error\n");
			exit(-1);
		}

		printf("Shared memory string:%s\n",addr);

		//解除共享内存映射
		shmdt(addr);

		shmctl(shm_id,IPC_RMID,NULL);

		sem_v(sem_id);

		del_sem(sem_id);
	}

	exit(0);
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值