中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當前位置: 首頁 > news >正文

創(chuàng)業(yè)初期要建立公司的網(wǎng)站嗎鄭州百度關(guān)鍵詞seo

創(chuàng)業(yè)初期要建立公司的網(wǎng)站嗎,鄭州百度關(guān)鍵詞seo,東莞企業(yè)網(wǎng)站找誰,潮州seo1.管道 2.信號量 2.1 概念 信號量 是一個計數(shù)器,用于實現(xiàn)進程間互斥和同步。 信號量的取值可以是任何自然數(shù)。 最簡單的信號量是只能取 0 和 1 的變量,這也是信號量最常見的一種形式,叫做二進制信號量(Binary Semaphore&#…

1.管道

2.信號量

2.1 概念

信號量 是一個計數(shù)器,用于實現(xiàn)進程間互斥和同步

信號量的取值可以是任何自然數(shù)。
最簡單的信號量是只能取 0 和 1 的變量,這也是信號量最常見的一種形式,叫做二進制信號量(Binary Semaphore)。
而可以取多個正整數(shù)的信號量被稱為通用信號量。

2.2 特點

1.	信號量用于進程間同步,若要在進程間傳遞數(shù)據(jù)需要結(jié)合共享內(nèi)存。2.	信號量基于操作系統(tǒng)的 PV 操作,程序?qū)π盘柫康牟僮鞫际?原子操作。3.	每次對信號量的 PV 操作不僅限于對信號量值加 1 或減 1,而且可以加減任意正整數(shù)。4.	支持信號量組。

2.3 原型

2.3.1 semget 創(chuàng)建或獲取一個信號量集函數(shù)

#include <sys/sem.h>// 創(chuàng)建或獲取一個信號量集 若成功返回信號量集ID,失敗返回-1
int semget(key_t key, int num_sems, int sem_flags);key:一個鍵值,用來標識一個全局唯一的信號量集。要通過信號量通信的進程使用相同的 key 來創(chuàng)建或獲取該信號量num_sems:指定要 創(chuàng)建/獲取信號量集中信號量的數(shù)目。創(chuàng)建:該值必須被指定。獲取:可以設置為 0.sem_flags:指定一組標志。它低端 9 個比特是信號量的權(quán)限,其格式和含義都與系統(tǒng)調(diào)用的 open、model參數(shù)相同。此外,它還可以和 IPC_CREAT 標志做按位 “或”運算以創(chuàng)建新的信號量集。確保創(chuàng)建一組新的、唯一的信號量集:聯(lián)合使用 IPC_CREAT 和 IPC_EXCL.此時,若創(chuàng)建的信號量集已存在,則 semget 返回錯誤 并設置 errno 為 EEXIST.返回:成功:返回正整數(shù)值,是信號量級的標識符失敗:-1,并設置 errno擴展:若 semget 用于創(chuàng)建信號量集,則與之關(guān)聯(lián)的內(nèi)核數(shù)據(jù)結(jié)構(gòu)體 semid_ds	將被創(chuàng)建并初始化。semid_ds結(jié)構(gòu)體定義如下:#include <sys/sem,h>	//此結(jié)構(gòu)體用于描述 IPC 對象(信號量、共享內(nèi)存和消息隊列)的權(quán)限struct ipc_perm{key_t key;		//鍵值uid_t uid;		//所有者的有效用戶 IDgid_t gid;		//所有者的有效組 IDuid_t cuid;		//創(chuàng)建者的有效用戶 IDgid_t cgid;		//創(chuàng)建者的有效組 IDmode_t mode;	//訪問權(quán)限...				//省略其他字段}struct semid_ds{struct ipc_perm sem_perm;		//信號量的操作權(quán)限unsigned long int sem_nsems;	//該信號量集中的信號量數(shù)目time_t sem_otime;				//最后一次調(diào)用 semop 的時間time_t sem_ctime;				//最后一次調(diào)用 semctl 的時間...								//省略其他填充字段}semget 對 semid_ds 結(jié)構(gòu)體初始化包括:sem_perm.cuid 和 sem_perm.uid 設置為調(diào)用進程的有效用戶 IDsem_perm.cgid 和 sem_perm.gid 設置為調(diào)用進程的有效組 IDsem_perm.mode 的最低 9 位設置為 sem_flags 參數(shù)的最低 9 位sem_nsems 設置為 num_semssem_otime 設置為 0sem_ctime 設置為當前系統(tǒng)時間

2.3.1.1 semget 特殊參數(shù) IPC_PRIVATE

可以給 semget  函數(shù)傳一個特殊的鍵值 IPC_PRIVATE(其值為 0),這樣無論信號量是否已存在, semget 都將創(chuàng)建一個新的信號量。注意,不要因為這個名字就認為這個創(chuàng)建的信號量就是私有的,
其他進程,尤其是子進程,也有方法來訪問這個信號量。如下例,父、子進程間使用一個 IPC_PRIVATE 信號量來同步:
#include <sys/sem.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <time.h>union semun
{int val;                  struct semid_ds* buf;     unsigned short int* array;struct seminfo* __buf;    
};// op=-1 執(zhí)行 P,op=1 執(zhí)行 V 
void pv( int sem_id, int op )
{struct sembuf sem_b;sem_b.sem_num = 0;sem_b.sem_op = op;sem_b.sem_flg = SEM_UNDO;semop( sem_id, &sem_b, 1 );
}int main( int argc, char* argv[] )
{int sem_id = semget( IPC_PRIVATE, 1, 0666 );    //創(chuàng)建一個信號量集 ,權(quán)限設置為 0666union semun sem_un;sem_un.val = 1;     semctl( sem_id, 0, SETVAL, sem_un );       //設置創(chuàng)建的信號量集 信號量 的值  為 1pid_t id = fork();if( id < 0 ){return 1;}else if( id == 0 ){time_t cur = time( NULL );printf( "child try to get binary sem: %s\n" ,ctime(&cur));//在父子進程間共享 IPC_PRIVATE 信號量的關(guān)鍵在于二者都可以操作該信號量的標識符 sem_idpv( sem_id, -1 );   //Vprintf( "child get the sem and would release it after 5 seconds\n" );sleep( 5 );pv( sem_id, 1 );    //Ptime_t cur1 = time( NULL );printf( "child try to get binary sem: %s\n" ,ctime(&cur1));exit( 0 );}else{time_t cur = time( NULL );printf( "parent try to get binary sem:%s\n" ,ctime(&cur));pv( sem_id, -1 );   //Vprintf( "parent get the sem and would release it after 5 seconds\n" );sleep( 5 );time_t cur1 = time( NULL );printf( "child try to get binary sem: %s\n" ,ctime(&cur1));pv( sem_id, 1 );    //P}waitpid( id, NULL, 0 );semctl( sem_id, 0, IPC_RMID, sem_un );  //立即移除信號量集return 0;
}

2.3.2 semop 對信號量集操作函數(shù)

與信號量關(guān)聯(lián)的一些重要內(nèi)核變量:
unsigned short semval;		//信號量的值
unsigned short semzcnt;		//等待信號量值變?yōu)?0 的進程數(shù)量
unsigned short semncnt;		//等待信號量值增加的進程數(shù)量
pid_t sempid;				//最后一次執(zhí)行 semop 操作的進程 ID// 對信號量集進行操作(就是改變上面的內(nèi)核變量),改變信號量的值,即執(zhí)行 P、V 操作,
#include <sys/sem.h>
int semop(int sem_id, struct sembuf * sem_ops, size_t num_sem_ops);sem_id:由 semget 調(diào)用返回的信號量集標識符,用以指定被操作的目標信號量集。
sem_ops:指向一個 sembuf 結(jié)構(gòu)體類型的數(shù)組,struct sembug{unsigned short int sem_num;short int sem_op;short int sem_flg;}		sem_num:信號量集中信號量的編號,0表示信號量中第一個信號量。sem_op:指定操作類型, 可選值為 正整數(shù)、0 和 負整數(shù)。每種類型的操作行為受 參數(shù) sem_flg 影響。sem_flg:可選值: IPC_NOWAIT SEM_UNDO.含義:IPC_NOWAIT :無論信號量操作是否成功,semop 調(diào)用都將立即返回,類似非阻塞 I/O。SEM_UNDO:當進程退出時取消正在進行的 semop 操作。sembuf 中參數(shù) sem_op 和 sem_flg 按如下方式影響 函數(shù) semop 的行為:1.	sem_op > 0:將內(nèi)核信號量的值在增加 sem_op,此操作要求調(diào)用進程對被操作信號量集擁有寫權(quán)限。若此時設置了 SEM_UNDO 標志,則系統(tǒng)將更新進程的 semadj 變量(用以跟蹤進程對信號量的修改情況)2.	sem_op = 0:表示這是一個“等待0(wait-for-zero)” 操作,此操作要求調(diào)用進程對被操作信號量集有 讀 權(quán)限。1.若此時 信號量值是 0:則調(diào)用立即成功返回。2.若此時 信號量值不是 0,則 semop 函數(shù) 失敗返回 或阻塞進程以等待信號量變?yōu)?0.在這種情況下:若 sem_flg 設置了 IPC_NOWAIT , semop 函數(shù)立即返回一個錯誤,并設置 errno 為 EAGAIN.若未設置 IPC_NOWAIT ,則 內(nèi)核信號量值 +1,進程被投入睡眠直到下列 3 個條件之一 發(fā)生:1.信號量的值 semval 變?yōu)?0 ,此時系統(tǒng)將該信號量的 semzcnt 值 -12.被操作信號量所在信號量集被進程移除, 此時 semop 調(diào)用失敗返回,errno 設置為 EIDRM3.調(diào)用被信號中斷,此時 semop 調(diào)用失敗返回, errno 被設置為 EINTR,同時系統(tǒng)將 內(nèi)核信號量 semzcnt 值 -13. sem_op < 0表示對信號量值進行減操作。此操作要求調(diào)用進程對被操作信號量集擁有寫權(quán)限。1.若 內(nèi)核信號量的值 semval 大于或等于 sem_op 的絕對值,則 將 semval - |sem_op|  操作成功。此時若設置了 SEM_UNDO 標志,則系統(tǒng)將更新進程的 semadj 變量。2.若 內(nèi)核信號量的值 semval 小于 sem_op 的絕對值,則 semop 失敗返回或阻塞進程以等待信號量可用。在這種情況下,若 IPC_NOWAIT 標志被指定時, sempop 立即返回一個錯誤,并設置 errno 為 EAGAIN.若 IPC_NOWAIT 標志未指定	, 則 內(nèi)核信號量 semncnt 值 +1,進程被投入睡眠直到下列 3 個條件之一 發(fā)生:1. 信號量 semval 的值變得 >= sem_op的絕對值,此時系統(tǒng)將該信號量的 semncnt 值 -1,并將 semval 減去 sem_op 的絕對值。同時,如果 SEM_UNDO 標志被設置,則系統(tǒng)更新 semadj 變量。2.被操作信號量所在的信號量集被移除,此時 semop 調(diào)用失敗返回, errno 被設置為 EIDRM3.調(diào)用被信號中斷,此時 semop 調(diào)用失敗返回,errno 被設置為 EINTR,同時系統(tǒng)將該信號量的 semncnt 值 -1.num_sem_ops:指定要執(zhí)行的操作個數(shù),即 sem_ops 數(shù)組中元素個數(shù)。返回:成功:0失敗:-1及 errno。失敗時,sem_ops 數(shù)組中指定所有操作都不執(zhí)行。擴展:semop 函數(shù)對數(shù)組 sem_op 中每個成員按照數(shù)組順序依次執(zhí)行,且該過程是原子操作(不可中斷的一個或者一系列操作, 也就是不會被線程調(diào)度機制打斷的操作, 運行期間不會有任何的上下文切換(context switch))。

2.3.3 semctl 對信號量集操作函數(shù)

#include <sys/sem.h>
// 控制信號量集的相關(guān)信息
int semctl(int semid, int sem_num, int command, ...);semid:由 semget 調(diào)用返回的信號量集標識符,以指定被操作的信號量集。
sem_num:指定被操作的信號量在信號量集中的編號。
command:指定要執(zhí)行的命令,詳見下表 13-2有的命令需要第 4 個參數(shù)
...:這個參數(shù)配合 command 使用,由用戶自己定義,但 sys/sem.h 頭文件給出推薦格式如下:union semun{int val;				//用于 SETVAL 命令struct semid_ds * buf;	//用于 IPC_STAT 和 IPC_SET 命令unsigned short * array;	//用于 GETALL 和 SETALL 命令struct seminfo * __buf; //用于 IPC_INFO 命令};struct seminfo{int semmap;		//Linux 內(nèi)核沒有使用int semmni;		//系統(tǒng)最多可以擁有的信號量集數(shù)目int semmns;		//系統(tǒng)最多可以擁有的信號量數(shù)目int semmnu;		//Linux 內(nèi)核沒有使用int semmsl;		//一個信號量集 最多允許包含的信號量數(shù)目int semopm;		//semop 一次最多能執(zhí)行的 sem_op 操作數(shù)目int semume;		//Linux 內(nèi)核沒有使用int semusz;		//sem_undo 結(jié)構(gòu)體的大小int semvmx;		//最大允許的信號量值//最多允許的 UNDO 次數(shù)(帶 SEM_UNDO 標志的 semop 操作的次數(shù))int semaem;		}返回:成功;返回值取決于 command 參數(shù)。失敗:-1 及 errno。		

在這里插入圖片描述

2.共享內(nèi)存

2.1 概念

共享內(nèi)存 是最高效的 IPC 機制,因為 不涉及進程之間任何數(shù)據(jù)傳輸。
但需要輔助手段來同步進程對共享內(nèi)存的訪問,否則會產(chǎn)生竟態(tài)條件。Linux 共享內(nèi)存包含 4 個系統(tǒng)調(diào)用:shmget、shmat、shmdt、shmctl。

2.2 shmget 系統(tǒng)調(diào)用

//創(chuàng)建一段新的共享內(nèi)存,或者獲取一段已經(jīng)存在的共享內(nèi)存#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
key:key 是一個鍵值,用來標識一段全局唯一的共享內(nèi)存。
size:指定共享內(nèi)存大小。若是創(chuàng)建,則 size 必須被指定。若是獲取,則 size 可被設為 0.
shmflg:與信號量的 semget 系統(tǒng)調(diào)用的 sem_flags參數(shù)相同,但 這個再多兩個標志:1.	SHM_HUGETLB系統(tǒng)將使用 “大頁面” 來為共享內(nèi)存分配空間2.	SHM_NORESERVE不為共享內(nèi)存保留交換分區(qū)(swap空間)。這樣,當物理內(nèi)存不足時,對該共享內(nèi)存執(zhí)行寫操作將觸發(fā) SIGSEGV 信號。
返回:成功:正整數(shù)值,共享內(nèi)存標識符。失敗:-1,errno擴展:shmget 創(chuàng)建共享內(nèi)存時,所有字節(jié)都被初始化為 0,與之關(guān)聯(lián)的內(nèi)核數(shù)據(jù)結(jié)構(gòu) shmid_ds 將被創(chuàng)建并初始化:struct shmid_ds{struct ipc_perm shm_perm;		//共享內(nèi)存的操作權(quán)限size_t shm_segsz;				//共享內(nèi)存大小,單位是字節(jié)__time_t shm_atime;				//對這段內(nèi)存最后一次調(diào)用 shmat 的時間__time_t shm_dtime;				//對這段內(nèi)存最后一次調(diào)用 shmdt 的時間__time_t shm_ctime;				//對這段內(nèi)存最后一次調(diào)用 shmct 的時間__pid_t shm_cpid;				//創(chuàng)建者 pid__pid_t shm_lpid;				//最后一次執(zhí)行 shmat 或 shmdt 操作的 進程 pidshmatt_t shm_nattach;			//目前關(guān)聯(lián)到此共享內(nèi)存的進程數(shù)量...								//省略一些字段}#include <sys/sem,h>	//此結(jié)構(gòu)體用于描述 IPC 對象(信號量、共享內(nèi)存和消息隊列)的權(quán)限struct ipc_perm{key_t key;		//鍵值uid_t uid;		//所有者的有效用戶 IDgid_t gid;		//所有者的有效組 IDuid_t cuid;		//創(chuàng)建者的有效用戶 IDgid_t cgid;		//創(chuàng)建者的有效組 IDmode_t mode;	//訪問權(quán)限...				//省略其他字段}shmget 對 shmid_ds 初始化包括:shm_perm.cuid 和 shm_perm.uid 設置為調(diào)用進程的有效用戶 IDshm_perm.cgid 和 shm_perm.gid 設置為調(diào)用進程的有效組 IDshm_perm.mode 的最低 9 位設置為 sem_flags 參數(shù)的最低 9 位shm_segzs 設置為 sizeshm_lpid、shm_nattach、shm_atime、shm_dtime 設置為 0sem_ctime 設置為當前系統(tǒng)時間

2.3 shmat 和 shmdt 系統(tǒng)調(diào)用

共享內(nèi)存創(chuàng)建完后,
1.不能立即訪問,而是首先將它關(guān)聯(lián)到進程地址空間(shmat 實現(xiàn))
2.使用完后,必須將它從進程地址空間中分離(shmdt 實現(xiàn))

#include <sys/shm.h>
void shmat(int shm_id, const void * shm_addr, int shmflg);
shm_id:創(chuàng)建的共享內(nèi)存標識。
shm_addr:指定將共享內(nèi)存關(guān)聯(lián)到進程的哪塊地址空間。最終效果受 參數(shù) shmflg 的可選標志 SHM_RND 的影響??赡艿那闆r:1.	若 shm_addr 為 NULL,則被關(guān)聯(lián)的地址由操作系統(tǒng)選擇。推薦這樣做。2.	若 shm_addr 非空 且 SHM_RND 未設置,則共享內(nèi)存被關(guān)聯(lián)到 addr 指定地址處。3.	若 shm_addr 非空 且 設置了 SHM_RND ,則被關(guān)聯(lián)的地址是 [shm_addr -(shm_addr % SHMLBA)]。SHMLBA 含義是“段低端邊界地址倍數(shù)”(Segment Low Boundary Address Multiple),它必須是內(nèi)存頁面大小(PAGE_SIZE)的整數(shù)倍。SHM_RND 的含義是圓整(round),即將共享內(nèi)存被關(guān)聯(lián)的地址向下圓整到離 shm_addr 最近的 SHMLBA 的整數(shù)倍地址處。
shmflg:可選標志。如下:SHM_RND :SHM_RDONLY:進程僅能讀取共享內(nèi)存的內(nèi)容。若沒有指定此標志,則進程可以對共享內(nèi)存進行讀寫操作(這取需要創(chuàng)建共享內(nèi)存時指定其讀寫權(quán)限)。SHM_REMAP:如果地址 shmaddr 已經(jīng)被關(guān)聯(lián)到一段共享內(nèi)存上,則重新關(guān)聯(lián)。SHM_EXEC:指定對共享內(nèi)存執(zhí)行權(quán)限。
返回:成功:返回共享內(nèi)存被關(guān)聯(lián)到的地址。同時將修改內(nèi)核數(shù)據(jù)結(jié)構(gòu) shmid_ds 部分字段,如下:shm_nattach 加 1.shm_lpid 設置為調(diào)用進程的 PID。shm_atime 設置為當前的時間。失敗:(void*)-1,errno//將 shm_addr 處的共享內(nèi)存從進程分離。
int shmdt (const void * shm_addr);
返回:成功:0。成功將修改內(nèi)核 shmid_ds 部分字段:shm_attach 減 1。shm_lpid 設置為 調(diào)用進程ID。shm_dtime 設置為當前時間。失敗:-1,errno

2.4 shmctl 系統(tǒng)調(diào)用

//控制共享內(nèi)存某些屬性
#include <sys/shm.h>
int shmctl(int shm_id, int command ,struct shmid_ds * buf);
shm_id:共享內(nèi)存標識符。
command:指定要執(zhí)行的命令。所有支持命令如下表 13-3。
bufL:	配合 command 使用,詳見表 13-3.
返回:成功:返回值取決于 command 命令,詳見 表 13-3.失敗:-1,errno。

在這里插入圖片描述

2.5 POSIX 的共享內(nèi)存方法

注意:使用如下 POSIX 共享內(nèi)存函數(shù),編譯時需要指定鏈接選項 -lrt//創(chuàng)建或打開一個 POSIX 共享內(nèi)存對象
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
int shm_open(const char * name, int oflag, mode_t mode);
shm_open 的使用方法與 open 系統(tǒng)調(diào)用完全相同。
name:指定要創(chuàng)建/打開的共享內(nèi)存對象。從可移植性考慮:此參數(shù)應使用 “/somename”的格式:以 “/” 開始,后接多個字符,且這些字符都不是 “/”;以 “\0” 結(jié)尾,長度不超過 NAME_MAX (通常是 255)。
oflag:指定創(chuàng)建方式。可以是下列標志中 一個 或 多個的 按位或。O_RDONLY只讀方式打開共享內(nèi)存對象。O_RDWR		以可讀、可寫方式打開共享內(nèi)存對象O_CREAT	若共享內(nèi)存不存在,則創(chuàng)建之。此時 mode 參數(shù)低 9 位指定該共享內(nèi)存對象的訪問權(quán)限。共享內(nèi)存被創(chuàng)建時,初始長度為 0.O_EXCL和 O_CREAT 一起使用。若由 name 指定的共享內(nèi)存對象已存在,則 shm_open 返回錯誤,否則就創(chuàng)建一個新的共享內(nèi)存對象。O_TRUNC若共享內(nèi)存已存在,則把它截斷,使其長度為 0。//刪除創(chuàng)建的共享內(nèi)存
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
int shm_unlink(const char * name);
name:將該參數(shù)指定的共享內(nèi)存對象標記為等待刪除。當使用該共享內(nèi)存對象的進程都使用 ummap 將它從進程分離后,系統(tǒng)將銷毀這個共享內(nèi)存對象所占據(jù)的資源。

2.6 共享內(nèi)存實例

/*注意點:	
1.每個子進程只會往自己所處理連接所對應的那一部分讀緩存中寫入數(shù)據(jù),所以使用共享內(nèi)存的目的是“共享讀”。
因此,每個子進程在使用共享內(nèi)存時都無需加鎖,這樣符合“聊天室服務器”的應用場景,同時提高性能。2.程序啟動時給 users 分配了足夠的空間,使他可以存儲所有可能的客戶連接的相關(guān)數(shù)據(jù)。
同時,sub_process 也分配了足夠的空間。
這是犧牲空間換時間的例子。
*///一個子進程處理一個客戶連接
//所有客戶 socket 連接的讀緩沖設計為一塊共享內(nèi)存
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>#define USER_LIMIT 5
#define BUFFER_SIZE 1024
#define FD_LIMIT 65535
#define MAX_EVENT_NUMBER 1024
#define PROCESS_LIMIT 65536//處理客戶連接必要的數(shù)據(jù)
struct client_data
{sockaddr_in address;        int connfd; pid_t pid;          //處理這個連接的子進程 IDint pipefd[2];      //和父進程通信用的管道
};static const char* shm_name = "/my_shm";
int sig_pipefd[2];
int epollfd;
int listenfd;
int shmfd;
char* share_mem = 0;
//客戶連接數(shù)組
client_data* users = 0;     
//子進程和客戶連接的映射關(guān)系表,用進程PID來索引這個數(shù)組即可獲得該進程所處理的客戶連接的編號
int* sub_process = 0;//當前客戶數(shù)量
int user_count = 0;
bool stop_child = false;int setnonblocking( int fd )
{int old_option = fcntl( fd, F_GETFL );int new_option = old_option | O_NONBLOCK;fcntl( fd, F_SETFL, new_option );return old_option;
}void addfd( int epollfd, int fd )
{epoll_event event;event.data.fd = fd;event.events = EPOLLIN | EPOLLET;epoll_ctl( epollfd, EPOLL_CTL_ADD, fd, &event );setnonblocking( fd );
}void sig_handler( int sig )
{int save_errno = errno;int msg = sig;send( sig_pipefd[1], ( char* )&msg, 1, 0 );errno = save_errno;
}void addsig( int sig, void(*handler)(int), bool restart = true )
{struct sigaction sa;memset( &sa, '\0', sizeof( sa ) );sa.sa_handler = handler;if( restart ){sa.sa_flags |= SA_RESTART;}sigfillset( &sa.sa_mask );assert( sigaction( sig, &sa, NULL ) != -1 );
}void del_resource()
{close( sig_pipefd[0] );close( sig_pipefd[1] );close( listenfd );close( epollfd );shm_unlink( shm_name );delete [] users;delete [] sub_process;
}//停止一個子進程
void child_term_handler( int sig )
{stop_child = true;
}//子進程運行函數(shù),
//idx:子進程處理客戶連接的編號
//users:保存所有客戶連接數(shù)據(jù)的數(shù)組
//share_mem:住處共享內(nèi)存地址
int run_child( int idx, client_data* users, char* share_mem )
{//子進程用 epoll 監(jiān)聽客戶連接 和 與父進程通信的管道文件描述符epoll_event events[ MAX_EVENT_NUMBER ];int child_epollfd = epoll_create( 5 );assert( child_epollfd != -1 );int connfd = users[idx].connfd;addfd( child_epollfd, connfd );int pipefd = users[idx].pipefd[1];addfd( child_epollfd, pipefd );int ret;//子進程設置自己的信號處理函數(shù)addsig( SIGTERM, child_term_handler, false );while( !stop_child ){int number = epoll_wait( child_epollfd, events, MAX_EVENT_NUMBER, -1 );if ( ( number < 0 ) && ( errno != EINTR ) ){printf( "epoll failure\n" );break;}for ( int i = 0; i < number; i++ ){int sockfd = events[i].data.fd;//客戶連接有數(shù)據(jù)到達if( ( sockfd == connfd ) && ( events[i].events & EPOLLIN ) ){memset( share_mem + idx*BUFFER_SIZE, '\0', BUFFER_SIZE );//讀取到對應的讀緩存中,讀緩存是共享內(nèi)存的一段,它開始于 idx*BUFFER_SIZE 處,長度為 BUFFER_SIZE 字節(jié)。故每個客戶連接的讀緩存是共享的。ret = recv( connfd, share_mem + idx*BUFFER_SIZE, BUFFER_SIZE-1, 0 );if( ret < 0 ){if( errno != EAGAIN ){stop_child = true;}}else if( ret == 0 ){stop_child = true;}else{//成功讀取客戶數(shù)據(jù)后通知主進程(通過管道)來處理send( pipefd, ( char* )&idx, sizeof( idx ), 0 );}}//主進程通知本進程(通過管道)將第client個客戶額數(shù)據(jù)發(fā)送到本進程負責的客戶端else if( ( sockfd == pipefd ) && ( events[i].events & EPOLLIN ) ){int client = 0;//接收主進程發(fā)送來的數(shù)據(jù),即有客戶數(shù)據(jù)到達的連接編號ret = recv( sockfd, ( char* )&client, sizeof( client ), 0 );if( ret < 0 ){if( errno != EAGAIN ){stop_child = true;}}else if( ret == 0 ){stop_child = true;}else{send( connfd, share_mem + client * BUFFER_SIZE, BUFFER_SIZE, 0 );}}else{continue;}}}close( connfd );close( pipefd );close( child_epollfd );return 0;
}int main( int argc, char* argv[] )
{if( argc <= 2 ){printf( "usage: %s ip_address port_number\n", basename( argv[0] ) );return 1;}const char* ip = argv[1];int port = atoi( argv[2] );int ret = 0;struct sockaddr_in address;bzero( &address, sizeof( address ) );address.sin_family = AF_INET;inet_pton( AF_INET, ip, &address.sin_addr );address.sin_port = htons( port );listenfd = socket( PF_INET, SOCK_STREAM, 0 );assert( listenfd >= 0 );ret = bind( listenfd, ( struct sockaddr* )&address, sizeof( address ) );assert( ret != -1 );ret = listen( listenfd, 5 );assert( ret != -1 );user_count = 0;users = new client_data [ USER_LIMIT+1 ];sub_process = new int [ PROCESS_LIMIT ];for( int i = 0; i < PROCESS_LIMIT; ++i ){sub_process[i] = -1;}epoll_event events[ MAX_EVENT_NUMBER ];epollfd = epoll_create( 5 );assert( epollfd != -1 );addfd( epollfd, listenfd );ret = socketpair( PF_UNIX, SOCK_STREAM, 0, sig_pipefd );assert( ret != -1 );setnonblocking( sig_pipefd[1] );addfd( epollfd, sig_pipefd[0] );addsig( SIGCHLD, sig_handler );addsig( SIGTERM, sig_handler );addsig( SIGINT, sig_handler );addsig( SIGPIPE, SIG_IGN );bool stop_server = false;bool terminate = false;//創(chuàng)建共享內(nèi)存,作為所有客戶 socket 連接的讀緩存shmfd = shm_open( shm_name, O_CREAT | O_RDWR, 0666 );assert( shmfd != -1 );ret = ftruncate( shmfd, USER_LIMIT * BUFFER_SIZE ); assert( ret != -1 );share_mem = (char*)mmap( NULL, USER_LIMIT * BUFFER_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shmfd, 0 );assert( share_mem != MAP_FAILED );close( shmfd );while( !stop_server ){int number = epoll_wait( epollfd, events, MAX_EVENT_NUMBER, -1 );if ( ( number < 0 ) && ( errno != EINTR ) ){printf( "epoll failure\n" );break;}for ( int i = 0; i < number; i++ ){int sockfd = events[i].data.fd;if( sockfd == listenfd )        //新連接到來{struct sockaddr_in client_address;socklen_t client_addrlength = sizeof( client_address );int connfd = accept( listenfd, ( struct sockaddr* )&client_address, &client_addrlength );if ( connfd < 0 ){printf( "errno is: %d\n", errno );continue;}if( user_count >= USER_LIMIT ){const char* info = "too many users\n";printf( "%s", info );send( connfd, info, strlen( info ), 0 );close( connfd );continue;}//保存新連接相關(guān)數(shù)據(jù)users[user_count].address = client_address;users[user_count].connfd = connfd;//創(chuàng)建與子進程管道ret = socketpair( PF_UNIX, SOCK_STREAM, 0, users[user_count].pipefd );assert( ret != -1 );pid_t pid = fork();if( pid < 0 ){close( connfd );continue;}else if( pid == 0 ){close( epollfd );close( listenfd );close( users[user_count].pipefd[0] );close( sig_pipefd[0] );close( sig_pipefd[1] );run_child( user_count, users, share_mem );munmap( (void*)share_mem,  USER_LIMIT * BUFFER_SIZE );exit( 0 );}else{close( connfd );close( users[user_count].pipefd[1] );addfd( epollfd, users[user_count].pipefd[0] );users[user_count].pid = pid;//記錄新客戶連接在數(shù)組 users中的索引值,建立進程pid 和該索引值間的映射sub_process[pid] = user_count;user_count++;}}//處理信號else if( ( sockfd == sig_pipefd[0] ) && ( events[i].events & EPOLLIN ) ){int sig;char signals[1024];ret = recv( sig_pipefd[0], signals, sizeof( signals ), 0 );if( ret == -1 ){continue;}else if( ret == 0 ){continue;}else{for( int i = 0; i < ret; ++i ){switch( signals[i] ){case SIGCHLD:   //子進程退出,表示某個客戶端關(guān)閉了連接{pid_t pid;int stat;while ( ( pid = waitpid( -1, &stat, WNOHANG ) ) > 0 ){//子進程pid取得被關(guān)閉連接編號int del_user = sub_process[pid];sub_process[pid] = -1;if( ( del_user < 0 ) || ( del_user > USER_LIMIT ) ){printf( "the deleted user was not change\n" );continue;}//清除關(guān)閉連接的相關(guān)數(shù)據(jù)epoll_ctl( epollfd, EPOLL_CTL_DEL, users[del_user].pipefd[0], 0 );close( users[del_user].pipefd[0] );users[del_user] = users[--user_count];sub_process[users[del_user].pid] = del_user;printf( "child %d exit, now we have %d users\n", del_user, user_count ); }if( terminate && user_count == 0 ){stop_server = true;}break;}case SIGTERM:case SIGINT:{   //結(jié)束服務器程序printf( "kill all the clild now\n" );//addsig( SIGTERM, SIG_IGN );//addsig( SIGINT, SIG_IGN );if( user_count == 0 ){stop_server = true;break;}for( int i = 0; i < user_count; ++i ){int pid = users[i].pid;kill( pid, SIGTERM );}terminate = true;break;}default:{break;}}}}}else if( events[i].events & EPOLLIN )       //某個子進程向父進程寫了數(shù)據(jù){int child = 0;ret = recv( sockfd, ( char* )&child, sizeof( child ), 0 );      //讀取管道數(shù)據(jù),child 變量記錄哪個客戶連接有數(shù)據(jù)到達printf( "read data from child accross pipe\n" );if( ret == -1 ){continue;}else if( ret == 0 ){continue;}else{   //向除負責處理第 chold 個客戶連接的子進程外的 其他子進程發(fā)送消息,通知它們有客戶要寫數(shù)據(jù)for( int j = 0; j < user_count; ++j ){if( users[j].pipefd[0] != sockfd ){printf( "send data to child accross pipe\n" );send( users[j].pipefd[0], ( char* )&child, sizeof( child ), 0 );}}}}}}del_resource();return 0;
}

3. 消息隊列

消息隊列在兩個進程間傳遞 二進制塊數(shù)據(jù) 的方式。

每個數(shù)據(jù)塊都有一個類型,接收方可以根據(jù)類型來有選擇的接收數(shù)據(jù)。
而不像管道 和 命名管道那樣必須先進先出的接收數(shù)據(jù)。

Linux消息隊列 API 定義在 sys/msg.h 	頭文件中,
包括 4 個系統(tǒng)調(diào)用:
msgget、msgsnd、msgrcv、msgctl。

3.1 msgget 系統(tǒng)調(diào)用

//創(chuàng)建一個消息隊列或者獲取一個消息隊列
#include <sys/msg.h>
int msgget (key_t key, int msgflg);
key:表用來標識一個全局唯一的消息隊列。
msgflg:使用和含義與 semget 的參數(shù) sem_flags參數(shù)相同。指定一組標志。它低端 9 個比特是信號量的權(quán)限,其格式和含義都與系統(tǒng)調(diào)用的 open、model參數(shù)相同。此外,它還可以和 IPC_CREAT 標志做按位 “或”運算以創(chuàng)建新的信號量集。確保創(chuàng)建一組新的、唯一的信號量集:聯(lián)合使用 IPC_CREAT 和 IPC_EXCL.此時,若創(chuàng)建的信號量集已存在,則 semget 返回錯誤 并設置 errno 為 EEXIST.返回:成功:返回正整數(shù)值,是消息隊列的標識符失敗:-1,并設置 errno擴展:若msgget用于創(chuàng)建消息隊列,則與之關(guān)聯(lián)的內(nèi)核數(shù)據(jù)結(jié)構(gòu) msqid_ds 將被創(chuàng)建并初始化:struct 	msqid_ds{struct ipc_perm msg_perm;	//消息隊列的操作權(quán)限time_t msg_stime;			//最后一次調(diào)用 msgsnd 的時間time_t msg_rtime;			//最后一次調(diào)用 msgrcv 的時間time_t msg_ctime;			//最后一次被修改的時間unsigned long __msg_cbytes;	//消息隊列中已有的字節(jié)數(shù)msqqnum_t msg_qnum;			//消息隊列中已有的消息數(shù)msglen_t msg_qbytes;		//消息隊列允許最大字節(jié)數(shù)pid_t msg_lspid;			//最后執(zhí)行 msgsnd 的進程 PIDpid_t msg_lrpid;			//最后執(zhí)行 msgrcv 的進程 PID}		

3.2 msgsnd 系統(tǒng)調(diào)用

//把一條消息添加到消息隊列
#include <sys/msg.h>
int msgsnd(int msqid, const void * msg_ptr, size_t msg_sz, int msgflg);
msqid:消息隊列標識符。即 msgget 調(diào)用返回值。
msg_ptr:指向一個準備發(fā)送的消息,必須被定義為如下類型:struct msgbuf{long mtype;			//消息類型,必須是一個正整數(shù)。char mtext[512];	//消息數(shù)據(jù),}
msg_sz:消息的數(shù)據(jù)部分(mtext)的長度??梢詾?0,表示沒有消息數(shù)據(jù)。
msgflg:控制 msgsnd 的行為。支持 IPC_NOWAIT 標志,即以非阻塞方式發(fā)送消息。此時 msgsnd 將立即返回并設置 errno 為 EAGAIN.默認情況下,發(fā)送消息時若消息隊列滿了,則 msgsnd 將阻塞。處于阻塞狀態(tài)的 msgsnd 調(diào)用可能被如下兩種情況所中斷:1.消息隊列被移除。此時 msgsnd 將立即返回并設置 errno 為 EIDRM。2.程序接收到信號。此時 msgsnd 將立即返回并設置 errno 為 EINTR。返回:成功:0.成功將修改內(nèi)核數(shù)據(jù) msqid_ds 的部分字段,如下:msg_qnum 加1msg_lspid 設置為調(diào)用進程的 PIDmsg_stime 設置為當前時間失敗:-1,errno

3.3 msgrcv 系統(tǒng)調(diào)用

//從消息隊列中獲取消息
#include <sys/msg.h>
int msgrcv(int msqid, void * msg_ptr,size_t msg_sz, long int msgtype, int msgflg);
msqid:消息隊列標識符
msg_ptr:用于存儲接收的消息
msg_sz:消息數(shù)據(jù)部分長度。
msgtype:指定消息類型。msgtype = 0:	讀取消息隊列中第一個消息。msgtype > 0:	讀取消息隊列中第一個類型為 msgtype 的消息(除非指定了標志 MSG_EXCEPT)msgtype < 0:	讀取消息隊列中第一個類型值比 msgtype 的絕對值小的消息。	
msgflg:控制 msgrcv 的行為,可以是如下標志的按位或:IPC_NOWAIT:	如果消息隊列中沒有消息,則 msgrcv 立即返回并設置 errno 為 ENOMSG.MSG_EXCEPT:	若 msgtype > 0 ,則接收消息隊列中第一個非 msgtype 類型的消息。MSG_NOERROR:如果消息數(shù)據(jù)部分長度超過 msg_sz ,就將它截斷。返回:成功:0.成功將修改 內(nèi)核數(shù)據(jù)結(jié)構(gòu) msqid_ds 中部分字段:msg_qnum 減 1.msg_lrqid 設置為調(diào)用進程 pid。msg_rtime 設置為當前時間。失敗 :-1,errno
擴展:處于阻塞狀態(tài)的 msgrcv 可能被如下兩種異常中斷:1.消息隊列被移除。 此時 msgrcv 將立即返回,并設置 errno 為 EIDRM.2.程序接收到信號。 此時 msgrcv 將立即返回,并設置 errno 為 EINTR.

3.4 msgctl 系統(tǒng)調(diào)用

//控制消息隊列某些屬性
#include <sys/msg.h>
int msgctl(int msqid, int command, struct msqid_ds * buf);
msqid:共享內(nèi)存標識符。
command:指定要執(zhí)行的命令。詳見下表 13-4。
返回:成功:取決于 command 參數(shù),詳見下表 13-4。失敗:-1,errno。		

在這里插入圖片描述

4. IPC 命令

在這里插入圖片描述

5.進程間傳遞文件描述符

fork 后,父進程中打開的文件描述符在子進程中仍然保持打開。

傳遞文件描述符是要在接收進程中創(chuàng)建一個新的文件描述符,并且該文件描述符和發(fā)送進程中被傳遞的文件描述符指向內(nèi)核中相同文件表項

示例:

#include <sys/socket.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>static const int CONTROL_LEN = CMSG_LEN( sizeof(int) );/// @brief 發(fā)送文件描述符 
/// @param fd 傳遞信息的 unix 域 socket
/// @param fd_to_send 待發(fā)送額文件描述符
void send_fd( int fd, int fd_to_send )
{struct iovec iov[1];struct msghdr msg;      char buf[0];iov[0].iov_base = buf;iov[0].iov_len = 1;msg.msg_name    = NULL;     //消息的協(xié)議地址  協(xié)議地址和套接口信息//在非連接的UDP中,發(fā)送者要指定對方地址端口,接受方用于的到數(shù)據(jù)來源,如果不需要的話可以設置為NULL//(在TCP或者連接的UDP中,一般設置為NULL)msg.msg_namelen = 0;        ///*  地址的長度  */msg.msg_iov     = iov;     /*  多io緩沖區(qū)的地址  */ msg.msg_iovlen = 1;        /*  緩沖區(qū)的個數(shù)  */ cmsghdr cm;cm.cmsg_len = CONTROL_LEN;  /*  包含該頭部的數(shù)據(jù)長度  */ cm.cmsg_level = SOL_SOCKET; /*  具體的協(xié)議標識  */ cm.cmsg_type = SCM_RIGHTS;  /*  協(xié)議中的類型  */ *(int *)CMSG_DATA( &cm ) = fd_to_send;msg.msg_control = &cm;      //設置輔助數(shù)據(jù)msg.msg_controllen = CONTROL_LEN;  /*  輔助數(shù)據(jù)的長度  */   sendmsg( fd, &msg, 0 );//只用于套接口,不能用于普通的I/O讀寫,參數(shù)sockfd則是指明要讀寫的套接口
}/// @brief 接收文件描述符
/// @param fd  
/// @return 
int recv_fd( int fd )
{struct iovec iov[1];struct msghdr msg;char buf[0];iov[0].iov_base = buf;  //指定用戶空間緩存區(qū)地址iov[0].iov_len = 1;     //指定 緩沖區(qū)長度為 1msg.msg_name    = NULL;msg.msg_namelen = 0;msg.msg_iov     = iov;msg.msg_iovlen = 1;cmsghdr cm;msg.msg_control = &cm;msg.msg_controllen = CONTROL_LEN;recvmsg( fd, &msg, 0 );int fd_to_read = *(int *)CMSG_DATA( &cm );return fd_to_read;
}int main()
{int pipefd[2];int fd_to_pass = 0;int ret = socketpair( PF_UNIX, SOCK_DGRAM, 0, pipefd ); //創(chuàng)建進程通信文件描述符assert( ret != -1 );pid_t pid = fork();assert( pid >= 0 );if ( pid == 0 ){close( pipefd[0] );//子進程關(guān)閉一端fd_to_pass = open( "test.txt", O_RDWR, 0666 );  //子進程打開文件描述符send_fd( pipefd[1], ( fd_to_pass > 0 ) ? fd_to_pass : 0 );//子進程向本地socket一端寫數(shù)據(jù),并攜帶待傳遞的文件描述符close( fd_to_pass );//子進程關(guān)閉打開的文件描述符exit( 0 );}close( pipefd[1] );//主進程關(guān)閉寫端(因為子進程往這個方向?qū)?#xff09;fd_to_pass = recv_fd( pipefd[0] );  //主進程接收文件描述符char buf[1024];memset( buf, '\0', 1024 );read( fd_to_pass, buf, 1024 );  //主進程讀取接收的文件描述符中的數(shù)據(jù)printf( "I got fd %d and data %s\n", fd_to_pass, buf );close( fd_to_pass );    //主進程關(guān)閉 接收的文件描述符。
}
http://www.risenshineclean.com/news/46063.html

相關(guān)文章:

  • 網(wǎng)站大屏輪播圖效果怎么做公眾號排名優(yōu)化軟件
  • 怎樣做永久網(wǎng)站二維碼北京seo公司有哪些
  • 網(wǎng)站做線上銷售蘇州百度關(guān)鍵詞優(yōu)化
  • wordpress兼職sem seo
  • 精品網(wǎng)站建設平臺如何自己免費制作網(wǎng)站
  • 哈爾濱網(wǎng)站建設外包公司申請自媒體平臺注冊
  • 網(wǎng)站建設銷售問答品牌營銷是什么
  • ps 怎么做網(wǎng)站杭州網(wǎng)站定制
  • win7 iis創(chuàng)建網(wǎng)站網(wǎng)絡推廣營銷網(wǎng)
  • 重慶網(wǎng)站建設吧營銷型網(wǎng)站
  • 自己制作的網(wǎng)站怎么做分頁今日時事新聞
  • 寧波網(wǎng)站建設的企業(yè)太原今日頭條
  • 百度推廣做網(wǎng)站什么價位網(wǎng)站開發(fā)流程的8個步驟
  • asp網(wǎng)站vps搬家2024年重大新聞簡短
  • 武漢做網(wǎng)站找哪家好世界球隊最新排名榜
  • 杭州未來科技網(wǎng)站建設高端建站
  • 企業(yè)信用網(wǎng)查詢網(wǎng)站優(yōu)化推廣培訓
  • 建甌網(wǎng)站制作百度網(wǎng)站怎么提升排名
  • 自己做產(chǎn)品品牌網(wǎng)站谷歌官網(wǎng)網(wǎng)址
  • 杭州富陽區(qū)網(wǎng)站建設公司seo排名軟件有用嗎
  • 鄭州響應式網(wǎng)站建設如何找外包的銷售團隊
  • 網(wǎng)絡工作室頭像seo積分優(yōu)化
  • 用flash做的網(wǎng)站展示品牌策劃方案模板
  • 網(wǎng)站二級域名 權(quán)重 盧松松學seo網(wǎng)絡推廣
  • 國內(nèi)醫(yī)療美容網(wǎng)站建設培訓機構(gòu)不退費最有效方式
  • 電子書推送網(wǎng)站怎么做會計培訓班
  • 政府采購網(wǎng)站的建設情況bing收錄提交
  • 個人博客網(wǎng)站開發(fā)的原因鄭州seo排名優(yōu)化公司
  • flash 做ppt的模板下載網(wǎng)站北京seo招聘信息
  • wordpress 命令插件東莞seo靠譜