A. 架構師進階:linux進程間如何共享內存
共享內存 IPC 原理
共享內存進程間通信機制主要用於實現進程間大量的數據傳輸,下圖所示為進程間使用共享內存實現大量數據傳輸的示意圖:
640
共享內存是在內存中單獨開辟的一段內存空間,這段內存空間有自己特有的數據結構,包括訪問許可權、大小和最近訪問的時間等。該數據結構定義如下:
from /usr/include/linux/shm.h
struct shmid_ds {
struct ipc_perm shm_perm; /* operation perms 操作許可權 */
int shm_segsz; /* size of segment (bytes) 段長度大小 */
__kernel_time_t shm_atime; /* last attach time 最近attach時間 */
__kernel_time_t shm_dtime; /* last detach time 最近detach時間 */
__kernel_time_t shm_ctime; /* last change time 最近change時間 */
__kernel_ipc_pid_t shm_cpid; /* pid of creator 創建者pid */
__kernel_ipc_pid_t shm_lpid; /* pid of last operator 最近操作pid */
unsigned short shm_nattch; /* no. of current attaches */
unsigned short shm_unused; /* compatibility */
void *shm_unused2; /* ditto - used by DIPC */
void *shm_unused3; /* unused */|
};
兩個進程在使用此共享內存空間之前,需要在進程地址空間與共享內存空間之間建立聯系,即將共享內存空間掛載到進程中。
系統對共享內存做了以下限制:
#define SHMMAX 0x2000000 /* max shared seg size (bytes) 最大共享段大小 */
#define SHMMIN 1 /* min shared seg size (bytes) 最小共享段大小 */
#define SHMMNI 4096 /* max num of segs system wide */
#define SHMALL (SHMMAX/getpagesize()*(SHMMNI/16))|
define SHMSEG SHMMNI /* max shared segs per process */
Linux 共享內存管理
1.創建共享內存
#include <sys/ipc.h> #include <sys/shm.h>
/*
* 第一個參數為 key 值,一般由 ftok() 函數產生
* 第二個參數為欲創建的共享內存段大小(單位為位元組)
* 第三個參數用來標識共享內存段的創建標識
*/
int shmget(key_t key, size_t size, int shmflg);
2.共享內存控制
#include <sys/ipc.h> #include <sys/shm.h>
/*
* 第一個參數為要操作的共享內存標識符
* 第二個參數為要執行的操作
* 第三個參數為 shmid_ds 結構的臨時共享內存變數信息
*/
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
3.映射共享內存對象
系統調用 shmat() 函數實現將一個共享內存段映射到調用進程的數據段中,並返回內存空間首地址,其函數聲明如下:
#include <sys/types.h>
#include <sys/shm.h>
/*
* 第一個參數為要操作的共享內存標識符
* 第二個參數用來指定共享內存的映射地址,非0則為此參數,為0的話由系統分配
* 第三個參數用來指定共享內存段的訪問許可權和映射條件
*/
void *shmat(int shmid, const void *shmaddr, int shmflg);
4.分離共享內存對象
在使用完畢共享內存空間後,需要使用 shmdt() 函數調用將其與當前進程分離。函數聲明如下:
#include <sys/types.h>
#include <sys/shm.h>
/*
* 參數為分配的共享內存首地址
*/
int shmdt(const void *shmaddr);
共享內存在父子進程間遵循的約定
1.使用 fork() 函數創建一個子進程後,該進程繼承父親進程掛載的共享內存。
2.如果調用 exec() 執行一個新的程序,則所有掛載的共享內存將被自動卸載。
3.如果在某個進程中調用了 exit() 函數,所有掛載的共享內存將與當前進程脫離關系。
程序實例
申請一段共享內存,父進程在首地址處存入一整數,子進程讀出。
#include
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include
#include
#define SHM_SIZE 1024
int main()
{
int shm_id, pid;
int *ptr = NULL;
/* 申請共享內存 */
shm_id = shmget((key_t)1004, SHM_SIZE, IPC_CREAT | 0600);
/* 映射共享內存到進程地址空間 */
ptr = (int*)shmat(shm_id, 0, 0);
printf("Attach addr is %p ", ptr);
*ptr = 1004;
printf("The Value of Parent is : %d ", *ptr);
if((pid=fork()) == -1){
perror("fork Err");
exit(0);
}
else if(!pid){
printf("The Value of Child is : %d ", *ptr);
exit(0);
}else{
sleep(1);
/* 解除映射 */
shmdt(ptr);
/* 刪除共享內存 */
shmctl(shm_id, IPC_RMID, 0);
}
return 0;
}
輸出結果:
640
B. 如何設置linux的共享內存
首先先使用shmget建立一塊共享內存,然後向該內存中寫入數據並返回該共享內存shmid
使用另一個程序通過上一程序返回的shmid讀該共享內存內的數據
建立共享內存並寫入數據的程序
#include<stdio.h>
#include<string.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<stdlib.h>
#include<errno.h>
voidget_buf(char*buf)
{
inti=0;
while((buf[i]=getchar())!=' '&&i<1024)
i++;
}
intmain(void)
{
intshmid;
shmid=shmget(IPC_PRIVATE,sizeof(char)*1024,IPC_CREAT|0666);
if(shmid==-1)
{
perror("shmget");
}
char*buf;
if((int)(buf=shmat(shmid,NULL,0))==-1)
{
perror("shmat");
exit(1);
}
get_buf(buf);
printf("%d ",shmid);
return0;
}
讀取數據的程序
#include<stdio.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<stdlib.h>
intmain(intargc,char**argv)
{
intshmid;
shmid=atoi(argv[1]);
char*buf;
if((int)(buf=shmat(shmid,NULL,0))==-1)
{
perror("shmat");
exit(1);
}
printf("%s ",buf);
shmdt(buf);
return0;
}
命令行的第一個參數設為第一個程序輸出的數字
如
使用完以後可以使用
ipcrm -m 19562507
來刪除該共享內存
C. linux下通過shmget創建的共享內存,是屬於用戶空間還是內核空間
屬於用戶空間. shmat後返回的地址空間屬於用戶空間, 不同進程可以將同一物理內存區域映射到各自的用戶空間中。該空間可以隨意讀寫。note: 一個小屁進程,在用戶態時,是沒有許可權操作內核空間的。
虛擬地址空間=用戶空間+內核空間。
D. 一般來說,在linux中使用共享內存的流程應該怎樣
可以使用samba協議。Samba是在Linux和UNIX系統上實現SMB協議的一個免費軟體,由伺服器及客戶端程序構成。SMB是一種在區域網上共享文件和列印機的一種通信協議,它為區域網內的不同計算機之間提供文件及列印機等資源的共享服務。具體配置步驟如下:
步驟1:安裝samba
sudo apt-get install samba samba-common-bin
步驟2:新建共享目錄並設置許可權
sudo mkdir /home/share
sudo chmod 777 /home/share
步驟3:修改配置文件
sudo nano /etc/samba/smb.conf
在smb.conf最後添加:
[share]
path = /home/share
browseable = yes
writable = yes
comment = smb share test
public = no#yes無需密碼 no需要密碼
步驟4:添加遠程訪問用戶
sudo useradd smbuser
sudo smbpasswd -a smbuser
步驟5:重新啟動服務
sudo service samba restart
對於windows操作系統,在資源管理器里輸入\\Linux的IP地址,會提示輸入上述步驟設置的用戶名和密碼,輸入正確後即可訪問分享內容。
E. LINUX共享內存如何實現
首先,美化Linux並啟動橋接器。SAMBA服務可以在Linux環境中共享。
F. linux內核如何勾選共享內存
所謂共享內存就是使得多個進程可以訪問同一塊內存空間,是最快的可用IPC形式。是針對其他通信機制運行效率較低而設計的。往往與其它通信機制,如信號量結合使用,來達到進程間的同步及互斥。其他進程能把同一段共享內存段「連接到」他們自己的地址空間里去。所有進程都能訪問共享內存中的地址。如果一個進程向這段共享內存寫了數據,所做的改動會即時被有訪問同一段共享內存的其他進程看到。共享內存的使用大大降低了在大規模數據處理過程中內存的消耗,但是共享內存的使用中有很多的陷阱,一不注意就很容易導致程序崩潰。
1.超過共享內存的大小限制?
在一個linux伺服器上,共享內存的總體大小是有限制的,這個大小通過SHMMAX參數來定義(以位元組為單位),您可以通過執行以下命令來確定 SHMMAX 的值:
# cat /proc/sys/kernel/shmmax
如果機器上創建的共享內存的總共大小超出了這個限制,在程序中使用標准錯誤perror可能會出現以下的信息:
unable to attach to shared memory
>解決方法:
1、設置 SHMMAX
SHMMAX 的默認值是 32MB 。一般使用下列方法之一種將 SHMMAX 參數設為 2GB :
通過直接更改 /proc 文件系統,你不需重新啟動機器就可以改變 SHMMAX 的默認設置。我使用的方法是將以下命令放入 />etc/rc.local 啟動文件中:
echo "2147483648" > /proc/sys/kernel/shmmax
您還可以使用 sysctl 命令來更改 SHMMAX 的值:sysctl -w kernel.shmmax=2147483648
最後,通過將該內核參數插入到 /etc/sysctl.conf 啟動文件中,您可以使這種更改永久有效:echo "kernel.shmmax=2147483648" >> /etc/sysctl.conf
2、設置 SHMMNI
G. linux共享內存使用的過程
Linux共享內存使用的過程?
一、什麼是共享內存
顧名思義,共享內存就是允許兩個不相關的進程訪問同一個邏輯內存。共享內存是在兩個正在運行的進程之間共享和傳遞數據的一種非常有效的方式。不同進程之間共享的內存通常安排為同一段物理內存。進程可以將同一段共享內存連接到它們自己的地址空間中,所有進程都可以訪問共享內存中的地址,就好像它們是由用C語言函數malloc分配的內存一樣。而如果某個進程向共享內存寫入數據,所做的改動將立即影響到可以訪問同一段共享內存的任何其他進程。
特別提醒:共享內存並未提供同步機制,也就是說,在第一個進程結束對共享內存的寫操作之前,並無自動機制可以阻止第二個進程開始對它進行讀取。所以我們通常需要用其他的機制來同步對共享內存的訪問,例如前面說到的信號量。
二、共享內存的使用
與信號量一樣,在Linux中也提供了一組函數介面用於使用共享內存,而且使用共享共存的介面還與信號量的非常相似,而且比使用信號量的介面來得簡單。它們聲明在頭文件 sys/shm.h中。
1、shmget函數
該函數用來創建共享內存,它的原型為:
int shmget(key_t key, size_t size, int shmflg);
第一個參數,與信號量的semget函數一樣,程序需要提供一個參數key(非0整數),它有效地為共享內存段命名,shmget函數成功時返回一個與key相關的共享內存標識符(非負整數),用於後續的共享內存函數。調用失敗返回-1.
不相關的進程可以通過該函數的返回值訪問同一共享內存,它代表程序可能要使用的某個資源,程序對所有共享內存的訪問都是間接的,程序先通過調用shmget函數並提供一個鍵,再由系統生成一個相應的共享內存標識符(shmget函數的返回值),只有shmget函數才直接使用信號量鍵,所有其他的信號量函數使用由semget函數返回的信號量標識符。
第二個參數,size以位元組為單位指定需要共享的內存容量
第三個參數,shmflg是許可權標志,它的作用與open函數的mode參數一樣,如果要想在key標識的共享內存不存在時,創建它的話,可以與IPC_CREAT做或操作。共享內存的許可權標志與文件的讀寫許可權一樣,舉例來說,0644,它表示允許一個進程創建的共享內存被內存創建者所擁有的進程向共享內存讀取和寫入數據,同時其他用戶創建的進程只能讀取共享內存。
H. linux共享內存的分配
進程通過調用shmget(Shared Memory GET,獲取共享內存)來分配一個共享內存塊。
該函數的第一個參數是一個用來標識共享內存塊的鍵值。彼此無關的進程可以通過指定同一個鍵以獲取對同一個共享內存塊的訪問。不幸的是,其它程序也可能挑選了同樣的特定值作為自己分配共享內存的鍵值,從而產生沖突。用特殊常量IPC_PRIVATE作為鍵值可以保證系統建立一個全新的共享內存塊。
該函數的第二個參數指定了所申請的內存塊的大小。因為這些內存塊是以頁面為單位進行分配的,實際分配的內存塊大小將被擴大到頁面大小的整數倍。
第三個參數是一組標志,通過特定常量的按位或操作來shmget。這些特定常量包括:
IPC_CREAT:這個標志表示應創建一個新的共享內存塊。通過指定這個標志,我們可以創建一個具有指定鍵值的新共享內存塊。
IPC_EXCL:這個標志只能與 IPC_CREAT 同時使用。當指定這個標志的時候,如果已有一個具有這個鍵值的共享內存塊存在,則shmget會調用失敗。也就是說,這個標志將使線程獲得一個「獨有」的共享內存塊。如果沒有指定這個標志而系統中存在一個具有相同鍵值的共享內存塊,shmget會返回這個已經建立的共享內存塊,而不是重新創建一個。
模式標志:這個值由9個位組成,分別表示屬主、屬組和其它用戶對該內存塊的訪問許可權。其中表示執行許可權的位將被忽略。指明訪問許可權的一個簡單辦法是利用<sys/stat.h>中指定,並且在手冊頁第二節stat條目中說明了的常量指定。例如,S_IRUSR和S_IWUSR分別指定了該內存塊屬主的讀寫許可權,而 S_IROTH和S_IWOTH則指定了其它用戶的讀寫許可權。 下面例子中shmget函數創建了一個新的共享內存塊(當shm_key已被佔用時則獲取對一個已經存在共享內存塊的訪問),且只有屬主對該內存塊具有讀寫許可權,其它用戶不可讀寫。
int segment_id = shmget (shm_key, getpagesize (), IPC_CREAT | S_IRUSR| S_IWUSR ); 如果調用成功,shmget將返回一個共享內存標識符。如果該共享內存塊已經存在,系統會檢查訪問許可權,同時會檢查該內存塊是否被標記為等待摧毀狀態。
I. 共享內存 linux下怎麼跑
linux 共享內存實現
說起共享內存,一般來說會讓人想起下面一些方法:
1、多線程。線程之間的內存都是共享的。更確切的說,屬於同一進程的線程使用的是同一個地址空間,而不是在不同地址空間之間進行內存共享;
2、父子進程間的內存共享。父進程以MAP_SHARED|MAP_ANONYMOUS選項mmap一塊匿名內存,fork之後,其子孫進程之間就能共享這塊內存。這種共享內存由於受到進程父子關系的限制,一般較少使用;
3、mmap文件。多個進程mmap到同一個文件,實際上就是大家在共享文件pagecache中的內存。不過文件牽涉到磁碟的讀寫,用來做共享內存顯然十分笨重,所以就有了不跟磁碟扯上關系的內存文件,也就是我們這里要討論的tmpfs和shmem;
tmpfs是一套虛擬的文件系統,在其中創建的文件都是基於內存的,機器重啟即消失。
shmem是一套ipc,通過相應的ipc系統調用shmget能夠以指定key創建一塊的共享內存。需要使用這塊內存的進程可以通過shmat系統調用來獲得它。
雖然是兩套不同的介面,但是在內核裡面的實現卻是同一套。shmem內部掛載了一個tmpfs分區(用戶不可見),shmget就是在該分區下獲取名為"SYSV${key}"的文件。然後shmat就相當於mmap這個文件。
所以我們接下來就把tmpfs和shmem當作同一個東西來討論了。
tmpfs/shmem是一個介於文件和匿名內存之間的東西。
一方面,它具有文件的屬性,能夠像操作文件一樣去操作它。它有自己inode、有自己的pagecache;
另一方面,它也有匿名內存的屬性。由於沒有像磁碟這樣的外部存儲介質,內核在內存緊缺時不能簡單的將page從它們的pagecache中丟棄,而需要swap-out;(參閱《linux頁面回收淺析》)
對tmpfs/shmem內存的讀寫,就是對pagecache中相應位置的page所代表的內存進行讀寫,這一點跟普通的文件映射沒有什麼不同。
如果進程地址空間的相應位置尚未映射,則會建立到pagecache中相應page的映射;
如果pagecache中的相應位置還沒有分配page,則會分配一個。當然,由於不存在磁碟上的源數據,新分配的page總是空的(特別的,通過read系統調用去讀一個尚未分配page的位置時,並不會分配新的page,而是共享ZERO_PAGE);
如果pagecache中相應位置的page被回收了,則會先將其恢復;
對於第三個「如果」,tmpfs/shmem和普通文件的page回收及其恢復方式是不同的:
page回收時,跟普通文件的情況一樣,內核會通過prio_tree反向映射找到映射這個page的每一個pagetable,然後將其中對應的pte清空。
不同之處是普通文件的page在確保與磁碟同步(如果page為臟的話需要刷回磁碟)之後就可以丟棄了,而對於tmpfs/shmem的page則需要進行swap-out。
注意,匿名page在被swap-out時,並不是將映射它的pte清空,而是得在pte上填寫相應的swap_entry,以便知道page被換出到哪裡去,否則再需要這個page的時候就沒法swap-in了。
而tmpfs/shmem的page呢?pagetable中對應的pte被清空,swap_entry會被存放在pagecache的radix_tree的對應slot上。
等下一次訪問觸發pagefault時,page需要恢復。
普通文件的page恢復跟page未分配時的情形一樣,需要新分配page、然後根據映射的位置重新從磁碟讀出相應的數據;
而tmpfs/shmem則是通過映射的位置找到radix_tree上對應的slot,從中得到swap_entry,從而進行swap-in,並將新的page放回pagecache;
這里就有個問題了,在pagecache的radix_tree的某個slot上,怎麼知道裡面存放著的是正常的page?還是swap-out後留下的swap_entry?
如果是swap_entry,那麼slot上的值將被加上RADIX_TREE_EXCEPTIONAL_ENTRY標記(值為2)。swap_entry的值被左移兩位後OR上RADIX_TREE_EXCEPTIONAL_ENTRY,填入slot。
也就是說,如果${slot}&RADIX_TREE_EXCEPTIONAL_ENTRY!=0,則它代表swap_entry,且swap_entry的值是${slot}>>2;否則它代表page,${slot}就是指向page的指針,當然其值可能是NULL,說明page尚未分配。
那麼顯然,page的地址值其末兩位肯定是0,否則就可能跟RADIX_TREE_EXCEPTIONAL_ENTRY標記沖突了;而swap_entry的值最大隻能是30bit或62bit(對應32位或64位機器),否則左移兩位就溢出了。
最後以一張圖說明一下匿名page、文件映射page、tmpfs/shmempage的回收及恢復過程:
J. Linux進程間如何共享內存
創建共享內存 shmget()
映射共享內存 shmat()
(使用映射的內存)
解除映射. shmdt()
刪除共享內存 shmctl()