周末整理笔记,发现已经有接近一年的笔记等待整理,主要是Linux进程间资源共享,包括变量、锁、函数,很多笔记中没有记录引用链接,这是个不好的习惯。
Linux内核中是不存在线程的概念的,一切可执行单元都是进程。所谓多线程,其实是一组可以共享内存的进程,即“轻量级进程”。所以应该只要实现内存共享,就能让进程之间互相访问。同一个进程的多个线程天然具有上面的特性。
变量共享
master.c
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/shm.h>
#include <sys/ipc.h>
int main()
{
// 创建(获取)共享内存
int shmid = shmget(1228, sizeof(int), IPC_CREAT);
assert(shmid != -1);
// 共享内存映射到进程地址空间
void* shmptr = shmat(shmid, 0, 0);
assert(shmptr != (void*)(-1));
// 设置一个随机数
int* num = shmptr;
srand(time(0));
int val = rand();
(*num) = val;
printf("set %d\n", val);
return 0;
}
slave.c
#include <stdio.h>
#include <assert.h>
#include <sys/shm.h>
#include <sys/ipc.h>
int main()
{
// 创建(获取)共享内存
int shmid = shmget(1228, sizeof(int), IPC_CREAT);
assert(shmid != -1);
// 共享内存映射到进程地址空间
void* shmptr = shmat(shmid, 0, 0);
assert(shmptr != (void*)(-1));
// 获取随机数
int* num = shmptr;
int val = (*num);
printf("get %d\n", val);
return 0;
}
运行结果:
[root workspace]#ipcs
------ Message Queues --------
key msqid owner perms used-bytes messages
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
------ Semaphore Arrays --------
key semid owner perms nsems
[root workspace]#
[root workspace]#./master
set 1003187732
[root workspace]#./slave
get 1003187732
[root workspace]#ipcs
------ Message Queues --------
key msqid owner perms used-bytes messages
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x000004cc 0 root 0 4 0
------ Semaphore Arrays --------
key semid owner perms nsems
[root workspace]#
执行./master后,使用ipcs命令可以看到共享内存key 4cc(10进制1228),然后多次运行可以看到slave每次取到的值都是master设置的随机值。
锁共享
并发访问不仅需要能够相互访问数据,而且往往需要同步、互斥操作。这些东西在多线程情况下基本都可以使用pthread库搞定。pthread库的各种锁,都有相关的flag可以设置是否进程间共享。以信号量为例,semaphore的初始化函数原型如下:
int sem_init(sem_t *sem, int pshared, unsigned int value);
第二个参数pshared就是用来控制是否能跨进程使用,pthread_spin_init函数第二个参数也是pshared,当然我们设置了锁可以跨进程还不够,还需要多个进程能够访问到所,所以自然而然地想到把锁放在共享内存。
master.c
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <semaphore.h>
int main()
{
// 创建(获取)共享内存
int shmid = shmget(1229, sizeof(sem_t), IPC_CREAT);
assert(shmid != -1);
// 共享内存映射到进程地址空间
void* shmptr = shmat(shmid, 0, 0);
assert(shmptr != (void*)(-1));
sem_t* sem = shmptr;
// 初始化信号量,设置为跨进程同步
int ret = sem_init(sem, 1, 0);
assert(ret == 0);
while(1)
{
ret = sem_post(sem);
assert(ret == 0);
sleep(1);
}
return 0;
}
slave.c
#include <time.h>
#include <stdio.h>
#include <assert.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <semaphore.h>
int main()
{
// 创建(获取)共享内存
int shmid = shmget(1229, sizeof(sem_t), IPC_CREAT);
assert(shmid != -1);
// 共享内存映射到进程地址空间
void* shmptr = shmat(shmid, 0, 0);
assert(shmptr != (void*)(-1));
sem_t* sem = shmptr;
while(1)
{
int ret = sem_wait(sem);
assert(ret == 0);
printf("%lu\n", time(0));
}
return 0;
}
这个例子运行需要开启三个终端,一个运行./master,另外两个运行./slave,两个终端交替输出时间。
运行结果:
终端二:
[root workspace]#./slave
1569146330
1569146330
1569146330
1569146330
1569146330
1569146331
1569146332
1569146333
1569146334
1569146335
1569146337
1569146339
1569146341
1569146343
1569146345
1569146347
1569146349
1569146351
1569146353
终端三:
[root workspace]#./slave
1569146336
1569146338
1569146340
1569146342
1569146344
1569146346
1569146348
1569146350
1569146352
1569146354
1569146355
1569146356
1569146357
1569146358
函数共享
函数的本质是一串连续的机器码,可以把这个连续的机器码看作是一段数据,我们可以把函数的机器码复制到共享内存上,另一个进程去调用该函数。
master.c
#include <assert.h>
#include <string.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#define MEM_SIZE 4096
int add(int a, int b)
{
return a + b;
}
int main()
{
// 创建(获取)共享内存
int shmid = shmget(1230, MEM_SIZE, IPC_CREAT);
assert(shmid != -1);
// 共享内存映射到进程地址空间
void* shmptr = shmat(shmid, 0, 0);
assert(shmptr != (void*)(-1));
// 确保函数addr在函数main之前
assert((void*)add < (void*)main);
// len的长度肯定大于等于add函数的长度
size_t len = (void*)main - (void*)add;
// 确保共享内存放得下add的机器码
assert(len <= MEM_SIZE);
// 把add拷贝到共享内存上去
memcpy(shmptr, add, len);
return 0;
}
slave.c
#include <stdio.h>
#include <assert.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/mman.h>
#define MEM_SIZE 4096
int main()
{
// 创建(获取)共享内存
int shmid = shmget(1230, MEM_SIZE, IPC_CREAT);
assert(shmid != -1);
// 共享内存映射到进程地址空间
void* shmptr = shmat(shmid, 0, 0);
assert(shmptr != (void*)(-1));
// 修改共享内存权限为可读可写可执行
int ret = mprotect(shmptr, MEM_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC);
assert(ret == 0);
// 得到函数指针
int (*f)(int, int)= shmptr;
// 执行函数
printf("%d\n", f(1, 2));
// 执行函数
printf("%d\n", f(7, 8));
return 0;
}
运行结果:
[root workspace]#gcc -g -o master thread.c
[root workspace]#
[root workspace]#./master
[root workspace]#gcc -g -o slave thread.c
[root workspace]#
[root workspace]#./slave
3
15