2019年8月13日星期二(系统编程)
作者:互联网
2019年8月13日星期二
一. 进程之间的通信方式 - 共享内存
1. 共享内存作用范围是什么?机制如何?
可以作用于linux下任意两个进程,机制就是使用同一片共享的内存区域,使得两个任意的进程访问这个区域,实现数据的交换。
2. 关于共享内存的API函数接口?
1)由于共享内存属于IPC对象,所以在使用前必须申请key值
key = ftok(".",10);
2)根据key值申请共享内存的ID号。 -> shmget() -> man 2 shmget
功能:allocates a shared memory segment -> 申请共享内存块
使用格式:
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
key:key值
size:共享内存的总字节数 PAGE_SIZE的倍数 #define PAGE_SIZE 1024
shmflg: IPC_CREAT|0666 -> 不存在则创建
IPC_EXCL -> 存在则报错
返回值:
成功:共享内存的ID
失败:-1
3)根据ID号去内存空间中映射一块区域。 -> shmat() -> man 2 shmat
使用格式:
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
shmid:共享内存的ID
shmaddr: NULL -> 系统自动分配 99%
不为NULL -> 用户自己分配 1%
shmflg: 普通属性代表内存空间可读可写,填0
返回值:
成功:该内存空间的起始地址
失败:(void *)-1
4)撤销内存空间的区域。 -> shmdt() -> man 2 shmdt
使用格式:
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
shmaddr: 该内存空间的起始地址
返回值:
成功:0
失败:-1
5)删除共享内存的IPC对象 -> shmctl() -> man 2 shmctl
使用格式:
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmid:共享内存的ID
cmd:IPC_RMID -> 删除
buf:NULL
返回值:
成功:0
失败:-1
练习1:尝试使用共享内存实现进程之间的通信。 -> 单独使用共享内存,容易造成数据的践踏!
/* 读端 */
#include "head.h"
int main()
{
//1. 申请key值
key_t key = ftok(".",10);
//2. 根据key值申请共享内存ID号
int shmid = shmget(key,2048,IPC_CREAT|0666);
//3. 根据ID号申请共享内存的起始地址
char *p = (char *)shmat(shmid,NULL,0);
//4. 不断读取共享内存的数据
while(1)
{
printf("from shm:%s",p);
usleep(500000);
if(strncmp(p,"quit",4) == 0)
{
break;
}
}
shmdt(p);
shmctl(shmid,IPC_RMID,NULL);
return 0;
}
/* 写端 */
#include "head.h"
int main()
{
//1. 申请key值
key_t key = ftok(".",10);
//2. 根据key值申请共享内存ID号
int shmid = shmget(key,2048,IPC_CREAT|0666);
//3. 根据ID号申请共享内存的起始地址
char *p = (char *)shmat(shmid,NULL,0);
//4. 往共享内存中写入数据
bzero(p,2048);
while(1)
{
fgets(p,2048,stdin);
if(strncmp(p,"quit",4) == 0)
{
break;
}
}
return 0;
}
二. 处理进程之间通信(共享内存)同步互斥方式 -- 信号量
1. 什么是信号量?
信号量虽然属于IPC对象,但是它不属于进程之间的通信,它只是用于处理同步互斥。
2. 关于信号量的API函数接口?
1)由于信号量属于IPC对象,所以要申请key值。
key = ftok(".",10);
2)根据key值申请信号量的ID号。 -> semget() -> man 2 semget
使用格式:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
key:key值
nsems:信号量的元素个数。 例如: 空间和数据 -> 2
semflg: IPC_CREAT|0666 -> 不存在则创建
IPC_EXCL -> 存在则报错
返回值:
成功:信号量的ID
失败:-1
3)如何实现信号量P/V操作? (如何使得1->0 0->1?) -> semop() -> man 2 semop
使用格式:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, unsigned nsops);
semid:信号量的ID
sops:进行P/V操作的结构体
struct sembuf{
unsigned short sem_num; 需要操作的成员的下标: 空间 -> 0 数据 ->1
short sem_op; P操作/V操作 P操作 ->-1 V操作 ->1
short sem_flg; 普通属性,填0
}
nsops:信号量操作结构体个数 -> 1
返回值:
成功:0
失败:-1
4)控制信号量参数。 -> semctl() -> man 2 semctl
使用格式:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);
semid:信号量的ID
semnum:需要操作的成员的下标: 空间 -> 0 数据 ->1
cmd: SETVAL -> 用于设置信号量元素的起始值
IPC_RMID -> 删除信号量
...:空间/数据的起始值
例子: 初始化空间:0 数据:1
semctl(semid,0,SETVAL,0);
semctl(semid,1,SETVAL,1);
三. linux最小调用资源单位 - 线程。
1. 进程与线程的区别?
进程: ./xxx -> 开启一个新的进程 -> 是系统中最小的资源分配单位。
int main()
{
/* 进程开始 */
....
/* 进程结束 */
return 0;
}
线程: 是一个进程内部的资源,是系统中最小调度单位。
2. 线程的函数接口特点?
1)由于线程函数接口都是被封装在线程库中,所以我们是看不到源码,查看线程函数时,一定是在第3个手册。
2)所有的线程函数的头文件: #include <pthread.h>
3. 函数接口有哪些?
1)如何在正在运行的进程中创建一个新的线程? -> pthread_create() -> man 3 pthread_create
功能: create a new thread -> 创建子线程。
使用格式:
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
thread:存放线程TID号的变量的地址
pthread_t其实是线程TID号的数据类型
attr:属性变量 -> 不为NULL,自定义属性
-> 为NULL,普通属性
start_routine: 线程执行例程函数,类型必须是: void *fun(void *arg)
arg:传递给例程函数的参数
返回值:
成功:0
失败:非0错误码
注意:
编译所有包含线程函数接口在内.c文件时,必须链接线程库: Compile and link with -pthread.
如果不链接,则编译不会通过,会报错: undefined reference to `pthread_create'
例子: gcc test.c -o test -lpthread
例题: 尝试创建一个新的线程,使得同时做两件事情。
#include "head.h"
void *fun(void *arg) //arg = &a
{
int a = *(int *)arg;
printf("a = %d\n",a);
int i;
for(i=0;i<10;i++)
{
printf("child thread:%d\n",i);
sleep(1);
}
}
int main(int argc,char *argv[])
{
/* 暂时来讲,都只是一个单线程 */
printf("helloworld!\n");
/* 创建一个新的子线程 */
pthread_t tid;
int ret,i;
int a = 10;
ret = pthread_create(&tid,NULL,fun,(void *)&a);
printf("ret = %d\n",ret);
printf("tid = %d\n",(int)tid);
for(i=0;i<5;i++)
{
printf("main thread:%d\n",i);
sleep(1);
}
return 0;
}
结论:
创建线程与调用函数接口有什么不同? -> 见"调用函数与创建线程的区别.jpg"
2)如何接合子线程?(等待子线程的退出) -> pthread_join() -> man 3 pthread_join
功能: join with a terminated thread -> 接合一个结束的子线程
使用格式:
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
thread:需要接合的线程的TID号
retval:存储子线程退出值的指针 如果填NULL,不关心子线程的退出。
返回值:
成功:0
失败:错误码
例子:主线程主动接合子线程。
#include "head.h"
void *fun(void *arg) //arg = &a
{
int a = *(int *)arg;
printf("a = %d\n",a);
int i;
for(i=0;i<10;i++)
{
printf("child thread:%d\n",i);
sleep(1);
}
}
int main(int argc,char *argv[])
{
/* 暂时来讲,都只是一个单线程 */
printf("helloworld!\n");
/* 创建一个新的子线程 */
pthread_t tid;
int ret,i;
int a = 10;
//fun((void *)&a);
ret = pthread_create(&tid,NULL,fun,(void *)&a);
printf("ret = %d\n",ret);
printf("tid = %d\n",(int)tid);
for(i=0;i<5;i++)
{
printf("main thread:%d\n",i);
sleep(1);
}
// 接合子线程
ret = pthread_join(tid,NULL);
printf("ret = %d\n",ret);
return 0;
}
3)如何退出线程? -> pthread_exit() -> man 3 pthread_exit
该函数的功能:
1)terminate calling thread -> 提前结束线程
2)为了返回一个退出值给主线程。
使用格式:
#include <pthread.h>
void pthread_exit(void *retval);
retval:子线程的退出值。 -> 退出值不能是局部变量。
返回值:无。
例子: 主线程主动结合子线程,并且关心子线程退出状态。
#include "head.h"
int exit_state = 5;
void *fun(void *arg) //arg = &a
{
int a = *(int *)arg;
printf("a = %d\n",a);
int i;
for(i=0;i<10;i++)
{
printf("child thread:%d\n",i);
sleep(1);
}
pthread_exit((void *)&exit_state);
}
int main(int argc,char *argv[])
{
/* 暂时来讲,都只是一个单线程 */
printf("helloworld!\n");
/* 创建一个新的子线程 */
pthread_t tid;
int ret,i;
int a = 10;
void *p = NULL;
//fun((void *)&a);
ret = pthread_create(&tid,NULL,fun,(void *)&a);
printf("ret = %d\n",ret);
printf("tid = %d\n",(int)tid);
for(i=0;i<5;i++)
{
printf("main thread:%d\n",i);
sleep(1);
}
// 接合子线程
ret = pthread_join(tid,&p); // p = (void *)&exit_state
printf("ret = %d\n",ret);
printf("exit_state = %d\n",*(int *)p);
return 0;
}
四. 什么情况下,线程会退出?
1. 在线程的内部调用pthread_exit()
2. 在线程的例程函数中执行return
3. 该线程被取消掉了。
4. 只要进程调用exit(),所有的线程都马上退出。
标签:13,int,星期二,void,线程,key,pthread,2019,include 来源: https://www.cnblogs.com/zjlbk/p/11347552.html