
進(jìn)程是指正在運(yùn)行的程序,它擁有獨(dú)立的內(nèi)存空間和系統(tǒng)資源,不同進(jìn)程之間的數(shù)據(jù)不共享。進(jìn)程是資源分配的基本單位。
線程是進(jìn)程內(nèi)的執(zhí)行單元,它與同一進(jìn)程內(nèi)的其他線程共享進(jìn)程的內(nèi)存空間和系統(tǒng)資源。線程是調(diào)度的基本單位。
在Linux中啟動(dòng)一個(gè)進(jìn)程有多種方法:
#include?
/**
?*?@brief?執(zhí)行系統(tǒng)命令調(diào)用命令處理器來執(zhí)行命令
?*
?*?Detailed?function?description
?*
?*?@param[in]?command:?包含被請(qǐng)求變量名稱的?C?字符串
?*
?*?@return?如果發(fā)生錯(cuò)誤,則返回值為?-1,否則返回命令的狀態(tài)。
?*/
int?system(const?char?*command);
例子:通過system函數(shù)啟動(dòng)一個(gè)進(jìn)程,列出當(dāng)前目錄下的文件及文件夾。
#include?
#include?
int?main(void)
{
????system("ls");
????printf("ls?end\n");
????return?0;
}
#include?
#include?
/**
?*?@brief?fork系統(tǒng)調(diào)用用于創(chuàng)建一個(gè)子進(jìn)程
?*
?*?Detailed?function?description
?*
?*?@param[in]
?*
?*?@return?如果發(fā)生錯(cuò)誤,則返回值為?-1,否則返回命令的狀態(tài)。
?*/
pid_t?fork(void);
例子:通過fork函數(shù)啟動(dòng)子進(jìn)程
#include?
#include?
#include?
#include?
int?main(void)?
{
????pid_t?res?=?fork();
????///
????if?(res?==?0)?
????{
????????printf("res?=?%d,?I?am?child?process.?pid?=?%d\n",?res,?getpid());
????????exit(EXIT_SUCCESS);???///
????}?
????///
????else?if?(res?>?0)?
????{
????????printf("res?=?%d,?I?am?parent?process.?pid?=?%d\n",?res,?getpid());
????????int?child_status?=?0;
????????pid_t?child_pid?=?wait(&child_status);???///
????????printf("Child?process(pid?=?%d)?has?been?terminated,?child_status?=?%d\n",?child_pid,?child_status);
????}?
????///
????else?
????{
????????printf("Fork?failed.\n");
????????exit(EXIT_FAILURE);
????}
????return?0;
}
編譯、運(yùn)行:

我們使用了fork()系統(tǒng)調(diào)用來創(chuàng)建一個(gè)新進(jìn)程。如果fork()返回值為0,則說明當(dāng)前進(jìn)程是子進(jìn)程;如果返回值大于0,則說明當(dāng)前進(jìn)程是父進(jìn)程。在父進(jìn)程中,我們使用wait()系統(tǒng)調(diào)用來等待子進(jìn)程結(jié)束。當(dāng)子進(jìn)程結(jié)束后,父進(jìn)程會(huì)繼續(xù)執(zhí)行。
#include?
/**
?*?@brief?啟動(dòng)新進(jìn)程,新進(jìn)程會(huì)覆蓋舊進(jìn)程
?*
?*?Detailed?function?description
?*
?*?@param[in]?path:?所執(zhí)行文件的路徑
?*?@param[in]?file:?所執(zhí)行文件的名稱
?*?@param[in]?arg:?傳入的參數(shù)列表,以NULL作為結(jié)束
?*?@param[in]?envp:?傳入的環(huán)境變量
?*
?*?@return?如果發(fā)生錯(cuò)誤,則返回值為?-1,否則返回命令的狀態(tài)。
?*/
int?execl(const?char?*path,?const?char?*arg,?...);
int?execlp(const?char?*file,?const?char?*arg,?...);
int?execle(const?char?*path,?const?char?*arg,?...,?char?*const?envp[]);
int?execv(const?char?*path,?char?*const?argv[]);
int?execvp(const?char?*file,?char?*const?argv[]);
int?execve(const?char?*path,?char?*const?argv[],?char?*const?envp[]);
例子:通過execl()函數(shù)的參數(shù)列表調(diào)用了ls命令程序
#include?
#include?
int?main(void)
{
????execl("/bin/ls",?"ls",?"-la",?NULL);
????printf("ls?end\n");
????return?0;
}
execl()函數(shù)的參數(shù)列表調(diào)用了ls命令程序,與在終端上運(yùn)行”ls -la”產(chǎn)生的結(jié)果是一樣的。
在Linux中終止一個(gè)進(jìn)程有多種方法:
進(jìn)程間通信是指在不同進(jìn)程之間傳播或交換信息的一種機(jī)制。每個(gè)進(jìn)程各自有不同的用戶地址空間,任何一個(gè)進(jìn)程的全局變量在另一個(gè)進(jìn)程中都看不到,所以進(jìn)程之間要交換數(shù)據(jù)必須通過內(nèi)核,在內(nèi)核中開辟一塊緩沖區(qū),進(jìn)程A把數(shù)據(jù)從用戶空間拷到內(nèi)核緩沖區(qū),進(jìn)程B再?gòu)膬?nèi)核緩沖區(qū)把數(shù)據(jù)讀走,內(nèi)核提供的這種機(jī)制稱為進(jìn)程間通信。
進(jìn)程間通信的目的:
Linux IPC(Inter-process Comminication, 進(jìn)程間通信)的方式:

內(nèi)核中的一個(gè)優(yōu)先級(jí)隊(duì)列,多個(gè)進(jìn)程通過訪問同一個(gè)隊(duì)列,進(jìn)行添加結(jié)點(diǎn)或者獲取結(jié)點(diǎn)實(shí)現(xiàn)通信。
POSIX消息隊(duì)列頭文件:
#include????????????/*?For?O_*?constants?*/
#include?????????/*?For?mode?constants?*/
#include?
編譯鏈接需要加上
-lrt鏈接。
消息隊(duì)列API接口:
/**
?*?@brief?創(chuàng)建消息隊(duì)列實(shí)例
?*
?*?Detailed?function?description
?*
?*?@param[in]?name:?消息隊(duì)列名稱
?*?@param[in]?oflag:根據(jù)傳入標(biāo)識(shí)來創(chuàng)建或者打開一個(gè)已創(chuàng)建的消息隊(duì)列
????????????????????-?O_CREAT:?創(chuàng)建一個(gè)消息隊(duì)列
????????????????????-?O_EXCL:?檢查消息隊(duì)列是否存在,一般與O_CREAT一起使用
????????????????????-?O_CREAT|O_EXCL:?消息隊(duì)列不存在則創(chuàng)建,已存在返回NULL
????????????????????-?O_NONBLOCK:?非阻塞模式打開,消息隊(duì)列不存在返回NULL
????????????????????-?O_RDONLY:?只讀模式打開
????????????????????-?O_WRONLY:?只寫模式打開
????????????????????-?O_RDWR:?讀寫模式打開
?*?@param[in]?mode:訪問權(quán)限
?*?@param[in]?attr:消息隊(duì)列屬性地址
?*
?*?@return?成功返回消息隊(duì)列描述符,失敗返回-1,錯(cuò)誤碼存于error中
?*/
mqd_t?mq_open(const?char?*name,?int?oflag,??mode_t?mode,?struct?mq_attr?*attr);
/**
?*?@brief?無限阻塞方式接收消息
?*
?*?Detailed?function?description
?*
?*?@param[in]?mqdes:?消息隊(duì)列描述符
?*?@param[in]?msg_ptr:消息體緩沖區(qū)地址
?*?@param[in]?msg_len:消息體長(zhǎng)度,長(zhǎng)度必須大于等于消息屬性設(shè)定的最大值
?*?@param[in]?msg_prio:消息優(yōu)先級(jí)
?*
?*?@return?成功返回消息長(zhǎng)度,失敗返回-1,錯(cuò)誤碼存于error中
?*/
mqd_t?mq_receive(mqd_t?mqdes,?char?*msg_ptr,?size_t?msg_len,?unsigned?*msg_prio);
/**
?*?@brief?指定超時(shí)時(shí)間阻塞方式接收消息
?*
?*?Detailed?function?description
?*
?*?@param[in]?mqdes:?消息隊(duì)列描述符
?*?@param[in]?msg_ptr:消息體緩沖區(qū)地址
?*?@param[in]?msg_len:消息體長(zhǎng)度,長(zhǎng)度必須大于等于消息屬性設(shè)定的最大值
?*?@param[in]?msg_prio:消息優(yōu)先級(jí)
?*?@param[in]?abs_timeout:超時(shí)時(shí)間
?*
?*?@return?成功返回消息長(zhǎng)度,失敗返回-1,錯(cuò)誤碼存于error中
?*/
mqd_t?mq_timedreceive(mqd_t?mqdes,?char?*msg_ptr,?size_t?msg_len,?unsigned?*msg_prio,?const?struct?timespec?*abs_timeout);
/**
?*?@brief?無限阻塞方式發(fā)送消息
?*
?*?Detailed?function?description
?*
?*?@param[in]?mqdes:?消息隊(duì)列描述符
?*?@param[in]?msg_ptr:待發(fā)送消息體緩沖區(qū)地址
?*?@param[in]?msg_len:消息體長(zhǎng)度
?*?@param[in]?msg_prio:消息優(yōu)先級(jí)
?*
?*?@return?成功返回0,失敗返回-1
?*/
mqd_t?mq_send(mqd_t?mqdes,?const?char?*msg_ptr,?size_t?msg_len,?unsigned?msg_prio);
/**
?*?@brief?指定超時(shí)時(shí)間阻塞方式發(fā)送消息
?*
?*?Detailed?function?description
?*
?*?@param[in]?mqdes:?消息隊(duì)列描述符
?*?@param[in]?msg_ptr:待發(fā)送消息體緩沖區(qū)地址
?*?@param[in]?msg_len:消息體長(zhǎng)度
?*?@param[in]?msg_prio:消息優(yōu)先級(jí)
?*?@param[in]?abs_timeout:超時(shí)時(shí)間
?*
?*?@return?成功返回0,失敗返回-1
?*/
mqd_t?mq_timedsend(mqd_t?mqdes,?const?char?*msg_ptr,?size_t?msg_len,?unsigned?msg_prio,?const?struct?timespec?*abs_timeout);
/**
?*?@brief?關(guān)閉消息隊(duì)列
?*
?*?Detailed?function?description
?*
?*?@param[in]?mqdes:?消息隊(duì)列描述符
?*
?*?@return?成功返回0,失敗返回-1
?*/
mqd_t?mq_close(mqd_t?mqdes);
/**
?*?@brief?分離消息隊(duì)列
?*
?*?Detailed?function?description
?*
?*?@param[in]?name:?消息隊(duì)列名稱
?*
?*?@return?成功返回0,失敗返回-1
?*/
mqd_t?mq_unlink(const?char?*name);
消息隊(duì)列基本API接口使用例子:發(fā)送進(jìn)程給接收進(jìn)程發(fā)送測(cè)試數(shù)據(jù)。
send.c:
#include?
#include?
#include?
#include?
#include????????????/*?For?O_*?constants?*/
#include?????????/*?For?mode?constants?*/
#include?
#define?MQ_MSG_MAX_SIZE????512??///
#define?MQ_MSG_MAX_ITEM????5??///
static?mqd_t?s_mq;
typedef?struct?_msg_data
{
????char?buf[128];
????int?cnt;
}msg_data_t;
void?send_data(void)
{
????static?int?cnt?=?0;
????msg_data_t?send_data?=?{0};
????cnt++;
????strcpy(send_data.buf,?"hello");
????send_data.cnt?=?cnt;
????int?ret?=?mq_send(s_mq,?(char*)&send_data,?sizeof(send_data),?0);
????if?(ret?0)
????{
????????perror("mq_send?error");
????????return;
????}
????printf("send?msg?=?%s,?cnt?=?%d\n",?send_data.buf,?send_data.cnt);
}
int?main(void)
{
????int?ret?=?0;
????struct?mq_attr?attr;
????///
????memset(&attr,?0,?sizeof(attr));
????attr.mq_maxmsg?=?MQ_MSG_MAX_ITEM;
????attr.mq_msgsize?=?MQ_MSG_MAX_SIZE;
????attr.mq_flags?=?0;
????s_mq?=?mq_open("/mq",?O_CREAT|O_RDWR,?0777,?&attr);
?if(-1?==?s_mq)
????{
????????perror("mq_open?error");
????????return?-1;
????}
????for?(size_t?i?=?0;?i?10;?i++)
????{
????????send_data();
????????sleep(1);
????}
????mq_close(s_mq);
????return?0;
}
recv.c:
#include?
#include?
#include?
#include?
#include????????????/*?For?O_*?constants?*/
#include?????????/*?For?mode?constants?*/
#include?
#define?MQ_MSG_MAX_SIZE????512??///
#define?MQ_MSG_MAX_ITEM????5??///
static?mqd_t?s_mq;
typedef?struct?_msg_data
{
????char?buf[128];
????int?cnt;
}msg_data_t;
int?main(void)
{
????int?ret?=?0;
????struct?mq_attr?attr;
????char?recv_msg[MQ_MSG_MAX_SIZE]?=?{0};
????msg_data_t?recv_data?=?{0};
????int?prio?=?0;
????ssize_t?len?=?0;
????s_mq?=?mq_open("/mq",?O_RDONLY);
?if(-1?==?s_mq)
????{
????????perror("mq_open?error");
????????return?-1;
????}
????while?(1)
????{
????????if((len?=?mq_receive(s_mq,?(char*)&recv_data,?MQ_MSG_MAX_SIZE,?&prio))?==?-1)
????????{
????????????perror("mq_receive?error");
????????????return?-1;
????????}
????????printf("recv_msg?=?%s,?cnt?=?%d\n",?recv_data.buf,?recv_data.cnt);
????????sleep(1);
????}
????mq_close(s_mq);
????mq_unlink("/mq");
????return?0;
}
編譯、運(yùn)行:
gcc?send.c?-o?send_process?-lrt
gcc?recv.c?-o?recv_process?-lrt

消息隊(duì)列的讀取和寫入的過程,會(huì)有發(fā)生用戶態(tài)與內(nèi)核態(tài)之間的消息拷貝過程。而共享內(nèi)存的方式則沒有這個(gè)拷貝過程,進(jìn)程間通信速度較快。
在物理內(nèi)存上開辟一塊內(nèi)存空間,多個(gè)進(jìn)程可以將同一塊物理內(nèi)存空間映射到自己的虛擬地址空間,通過自己的虛擬地址直接訪問這塊空間,通過這種方式實(shí)現(xiàn)數(shù)據(jù)共享。

POSIX共享內(nèi)存頭文件:
#include?
#include?
#include?
共享內(nèi)存API接口:
/**
?*?@brief?創(chuàng)建共享內(nèi)存實(shí)例
?*
?*?Detailed?function?description
?*
?*?@param[in]?name:?要打開或創(chuàng)建的共享內(nèi)存文件名
?*?@param[in]?oflag:打開的文件操作屬性
????????????????????-?O_CREAT:?創(chuàng)建一個(gè)共享內(nèi)存文件
????????????????????-?O_EXCL:?檢查共享內(nèi)存是否存在,一般與O_CREAT一起使用
????????????????????-?O_CREAT|O_EXCL:?共享內(nèi)存不存在則創(chuàng)建,已存在返回NULL
????????????????????-?O_NONBLOCK:?非阻塞模式打開,共享內(nèi)存不存在返回NULL
????????????????????-?O_RDONLY:?只讀模式打開
????????????????????-?O_WRONLY:?只寫模式打開
????????????????????-?O_RDWR:?讀寫模式打開
?*?@param[in]?mode:文件共享模式,例如?0777
?*
?*?@return?成功返回共享內(nèi)存描述符,失敗返回-1,錯(cuò)誤碼存于error中
?*/
int?shm_open(const?char?*name,?int?oflag,?mode_t?mode);
/**
?*?@brief?刪除共享內(nèi)存
?*
?*?Detailed?function?description
?*
?*?@param[in]?name:?創(chuàng)建的共享內(nèi)存文件名
?*
?*?@return?成功返回0,失敗返回-1
?*/
int?shm_unlink(const?char?*name);
/**
?*?@brief?將打開的文件映射到內(nèi)存
?*
?*?Detailed?function?description
?*
?*?@param[in]?addr:?要將文件映射到的內(nèi)存地址,一般應(yīng)該傳遞NULL來由Linux內(nèi)核指定
?*?@param[in]?length:?要映射的文件數(shù)據(jù)長(zhǎng)度
?*?@param[in]?prot:?映射的內(nèi)存區(qū)域的操作權(quán)限(保護(hù)屬性),包括PROT_READ、PROT_WRITE、PROT_READ|PROT_WRITE
?*?@param[in]?flags:?標(biāo)志位參數(shù),包括:MAP_SHARED、MAP_PRIVATE與MAP_ANONYMOUS。
?*?@param[in]?fd:??用來建立映射區(qū)的文件描述符,用?shm_open打開或者open打開的文件
?*?@param[in]?offset:?映射文件相對(duì)于文件頭的偏移位置,應(yīng)該按4096字節(jié)對(duì)齊
?*
?*?@return?成功返回0,失敗返回-1
?*/
void?*mmap(void?*addr,?size_t?length,?int?prot,?int?flags,?int?fd,?off_t?offset);
?
/**
?*?@brief?取消內(nèi)存映射
?*
?*?Detailed?function?description
?*
?*?@param[in]?addr:?由mmap成功返回的地址
?*?@param[in]?length:?要取消的內(nèi)存長(zhǎng)度
?*
?*?@return?成功返回0,失敗返回-1
?*/
int?munmap(void?*addr,?size_t?length);
/**
?*?@brief?將參數(shù)fd指定的文件大小改為參數(shù)length指定的大小
?*
?*?Detailed?function?description
?*
?*?@param[in]?fd:?已打開的文件描述符,以寫入模式打開的文件
?*?@param[in]?length:?要設(shè)置的長(zhǎng)度
?*
?*?@return?成功返回0,失敗返回-1
?*/
int?ftruncate(int?fd,off_t?length);
/**
?*?@brief?獲取文件相關(guān)的信息,將獲取到的信息放入到statbuf結(jié)構(gòu)體中
?*
?*?Detailed?function?description
?*
?*?@param[in]?fd:?已打開的文件描述符
?*?@param[out]?statbuf:?文件的信息
?*
?*?@return?成功返回0,失敗返回-1
?*/
int?fstat(int?fd,?struct?stat?*statbuf);
共享內(nèi)存基本API接口使用例子:發(fā)送進(jìn)程給接收進(jìn)程發(fā)送測(cè)試數(shù)據(jù)。
send.c:
#include?
#include?
#include?
#include?
#include????????????/*?For?O_*?constants?*/
#include?????????/*?For?mode?constants?*/
#include?
#define?SHM_NAME?"/shm"
int?main(void)
{
????int?ret?=?0;
????///
????int?shm_fd?=?shm_open(SHM_NAME,?O_RDWR?|?O_CREAT,?0666);
????if?(shm_fd?==?-1)?
????{
????????printf("shm_open?error\n");
????}
????///
????ftruncate(shm_fd?,?8?*?1024);
????///
????struct?stat?filestat?=?{0};
????fstat(shm_fd,?&filestat);
????printf("st_size?=?%ld\n",filestat.st_size);
????///
????char?*shm_ptr?=?(char*)mmap(NULL,?filestat.st_size,?PROT_READ|PROT_WRITE,?MAP_SHARED,?shm_fd,?0);
????close(shm_fd);
????///
????char?buf[]?=?"hello?world";
????memmove(shm_ptr,buf,?sizeof(buf));
????printf("pid?%d,?%s\n",getpid(),?shm_ptr);
????///
????munmap(shm_ptr,?filestat.st_size);
????return?0;
}
recv.c:
#include?
#include?
#include?
#include?
#include????????????/*?For?O_*?constants?*/
#include?????????/*?For?mode?constants?*/
#include?
#define?SHM_NAME?"/shm"
int?main(void)
{
????///
????int?shm_fd?=?shm_open(SHM_NAME,?O_RDWR?|?O_CREAT,?0666);
????if?(shm_fd?==?-1)?
????{
????????printf("shm_open?failed\n");
????????exit(EXIT_FAILURE);
????}
????///
????ftruncate(shm_fd?,?8192);
????///
????struct?stat?filestat;
????fstat(shm_fd,?&filestat);
????printf("st_size?=?%ld\n",filestat.st_size);
????///
????char?*shm_ptr?=?(char*)mmap(NULL,?filestat.st_size,?PROT_READ|PROT_WRITE,?MAP_SHARED,?shm_fd,?0);
????close(shm_fd);
????
????///
????printf("pid?=?%d,?%s\n",?getpid(),?shm_ptr);
????munmap(shm_ptr,?filestat.st_size);
????shm_unlink(SHM_NAME);
????return?0;
}
編譯、運(yùn)行:
gcc?send.c?-o?send_process?-lrt
gcc?recv.c?-o?recv_process?-lrt

對(duì)具有多個(gè)處理核系統(tǒng)消息傳遞的性能要優(yōu)于共享內(nèi)存。共享內(nèi)存會(huì)有高速緩存一致性問題,這是由共享數(shù)據(jù)在多個(gè)高速緩存之間遷移而引起的。隨著系統(tǒng)的處理核的數(shù)量的日益增加,可能導(dǎo)致消息傳遞作為 IPC 的首選機(jī)制。
UNIX域套接字與傳統(tǒng)基于TCP/IP協(xié)議棧的socket不同,unix domain socket以文件系統(tǒng)作為地址空間,不需經(jīng)過TCP/IP的頭部封裝、報(bào)文ack確認(rèn)、路由選擇、數(shù)據(jù)校驗(yàn)與重傳過程,因此傳輸速率上也不會(huì)受網(wǎng)卡帶寬的限制。
unix domain socket在進(jìn)程間通信同樣是基于“客戶端—服務(wù)器”(C-S)模式。
UNIX域套接字基本API接口使用例子:基于UNIX域套接字客戶端進(jìn)程向服務(wù)端進(jìn)程發(fā)送測(cè)試數(shù)據(jù)。
server.c:
#include?
#include?
#include?
#include?
#include????????????/*?For?O_*?constants?*/
#include?????????/*?For?mode?constants?*/
#include?
#include?
#include?
#include?
#define?SERVER_PATH?"/tmp/server"?
int?main(void)
{
?///
?int?server_fd?=?socket(AF_LOCAL,?SOCK_STREAM,?0);
?if(server_fd?0)
?{
??printf("socket?error\n");
??exit(EXIT_FAILURE);
?}
?///
?unlink(SERVER_PATH);
?struct?sockaddr_un?server_addr;
?memset((char*)&server_addr,?0,?sizeof(server_addr));
?server_addr.sun_family?=?AF_LOCAL;
?strncpy(server_addr.sun_path,?SERVER_PATH,?sizeof(server_addr.sun_path)-1);
?if(bind(server_fd,?(struct?sockaddr*)&server_addr,?sizeof(server_addr))?0)
?{
??printf("bind?error\n");
????????close(server_fd);
??exit(EXIT_FAILURE);
?}
?///
?if(listen(server_fd,?10)?0)?
?{
??printf("listen?error\n");
????????close(server_fd);
??exit(EXIT_FAILURE);
?}
????///
?int?addr_len?=?sizeof(struct?sockaddr);
????struct?sockaddr_un?client_addr;
?int?client_fd?=?accept(server_fd,?(struct?sockaddr*)&client_addr,?(socklen_t?*)&addr_len);
?if(client_fd?0)
?{
??printf("accept?error\n");
??close(server_fd);
??unlink(SERVER_PATH);
??exit(1);?
?}
?else
?{
??printf("connected?client:?%s\n",?client_addr.sun_path);
?}
?while(1)
?{
????????char?buf[128]?=?{0};
????????int?recv_len?=?read(client_fd,?buf,?sizeof(buf));?
????????if(recv_len?<=?0)
????????{
????????????printf("recv?error!\n");
????????????close(client_fd);
????????????exit(EXIT_FAILURE);
????????}
????????printf("recv?:?%s\n",?buf);
?}
????unlink(SERVER_PATH);
?close(server_fd);
????close(client_fd);
????return?0;
}
client.c:
#include?
#include?
#include?
#include?
#include????????????/*?For?O_*?constants?*/
#include?????????/*?For?mode?constants?*/
#include?
#include?
#include?
#include?
#define?SERVER_PATH?"/tmp/server"
#define?CLIENT_PATH?"/tmp/client"
int?main(void)
{
?///
?int?client_fd?=?socket(AF_UNIX,?SOCK_STREAM,?0);
?if(client_fd?0)
?{
??printf("socket?error\n");
??exit(EXIT_FAILURE);
?}
?///
????struct?sockaddr_un?client_addr;
?memset((char*)&client_addr,?0,?sizeof(client_addr));
?client_addr.sun_family?=?AF_UNIX;
?strncpy(client_addr.sun_path,?CLIENT_PATH,?sizeof(client_addr.sun_path)-1);
?unlink(CLIENT_PATH);
?if(bind(client_fd,?(struct?sockaddr*)&client_addr,?sizeof(client_addr))?0)
?{
??printf("bind?error\n");
??close(client_fd);
??exit(1);?
?}
?///
????struct?sockaddr_un?server_addr;
?server_addr.sun_family??=?AF_UNIX;
?strncpy(server_addr.sun_path,?SERVER_PATH,?sizeof(server_addr.sun_path)-1);
?int?ret?=?connect(client_fd,?(struct?sockaddr*)&server_addr,?sizeof(server_addr));?
?if(ret?0)
?{
??printf("connect?error\n");
??close(client_fd);
??unlink(CLIENT_PATH);
??exit(1);??
?}?
?printf("connect?to?server:?%s\n",?server_addr.sun_path);
?while(1)
?{
????????char?buf[128]?=?{0};
????????if?(scanf("%s",?buf))
????????{
????????????int?send_len?=?write(client_fd,?buf,?strlen(buf));
????????????if?(send_len?<=?0)
????????????{
????????????????printf("write?error!\n");
????????????????close(client_fd);
????????????????exit(EXIT_FAILURE);??
????????????}
????????????else
????????????{
????????????????printf("send?success!?send:?%s,?send_len:?%d\n",?buf,?send_len);
????????????}
????????}?
?}
????unlink(SERVER_PATH);
????close(client_fd);
????return?0;
}
編譯、運(yùn)行:
gcc?server.c?-o?server_process
gcc?client.c?-o?client_process

類socket的其它進(jìn)程間通信方式:
實(shí)用 | nanomsg通信庫的簡(jiǎn)單使用分享
mqtt應(yīng)用于進(jìn)程間通信
在內(nèi)核中開辟一塊緩沖區(qū);若多個(gè)進(jìn)程拿到同一個(gè)管道(緩沖區(qū))的操作句柄,就可以訪問同一個(gè)緩沖區(qū),就可以進(jìn)行通信。涉及到兩次用戶態(tài)與內(nèi)核態(tài)之間的數(shù)據(jù)拷貝。
內(nèi)核中的緩沖區(qū)是沒有具體的標(biāo)識(shí)符的,匿名管道只能用于具有親緣關(guān)系的進(jìn)程間通信。

調(diào)用pipe接口可以創(chuàng)建一個(gè)匿名管道,并返回了兩個(gè)描述符,一個(gè)是管道的讀取端描述符 fd[0],另一個(gè)是管道的寫入端描述符 fd[1]。
管道是一個(gè)半雙工通信(可以選擇方向的單向傳輸)
匿名管道基本API接口使用例子:父進(jìn)程通過管道發(fā)送測(cè)試數(shù)據(jù)給子進(jìn)程。
#include?
#include?
#include?
#include?
int?main()
{
????///
?int?pipefd[2]?=?{-1};
???int?ret?=?pipe(pipefd);
????if?(ret?0)
????{
???????printf("pipe?error\n");
???????exit(EXIT_FAILURE);
????}
????int?read_fd?=?pipefd[0];???///
????int?write_fd?=?pipefd[1];??///
????///
????pid_t?pid?=?fork();
????if?(pid?==?0)
????{
????????///
??char?buf[128]?=?{0};
??read(read_fd,?buf,?sizeof(buf));
????????printf("child?recv?data?from?father:?%s",?buf);
?}
????else?if?(pid?>?0)
???{
??///
????????char?*ptr?=?"hello88888888\n";
????????write(write_fd,?ptr,?strlen(ptr));
????}
?return?0;
}
編譯、運(yùn)行:

如果需要雙向通信,則應(yīng)該創(chuàng)建兩個(gè)管道。
命名管道也是內(nèi)核中的一塊緩沖區(qū),并且這個(gè)緩沖區(qū)具有標(biāo)識(shí)符;這個(gè)標(biāo)識(shí)符是一個(gè)可見于文件系統(tǒng)的管道文件,能夠被其他進(jìn)程找到并打開管道文件,則可以獲取管道的操作句柄,所以該命名管道可用于同一主機(jī)上的任意進(jìn)程間通信。
創(chuàng)建命名管道的接口:
int?mkfifo(const?char?*pathname,?mode_t?mode);
命名管道基本API接口使用例子:一個(gè)進(jìn)程往管道中寫入測(cè)試數(shù)據(jù),另一個(gè)進(jìn)程從管道中讀取數(shù)據(jù)。
fifo_wr.c:
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#define?FIFO_PATH??"./fifo_file"
typedef?struct?_msg_data
{
????char?buf[128];
????int?cnt;
}msg_data_t;
void?send_data(int?fd)
{
????static?int?cnt?=?0;
????msg_data_t?send_data?=?{0};
????cnt++;
????strcpy(send_data.buf,?"hello");
????send_data.cnt?=?cnt;
????write(fd,?&send_data,?sizeof(send_data));
????printf("send?msg?=?%s,?cnt?=?%d\n",?send_data.buf,?send_data.cnt);
}
int?main(void)
{
????///
????int?ret?=?mkfifo(FIFO_PATH,?0664);
????if?(ret?0?&&?errno?!=?EEXIST)
????{
????????printf("mkfifo?error\n");
????????exit(EXIT_FAILURE);
????}
????///
????int?fd?=?open(FIFO_PATH,?O_WRONLY);
????if?(fd?0)
????{
????????printf("open?fifo?error\n");
????????exit(EXIT_FAILURE);
????}
????printf("open?fifo?success\n");
????
????///
????for?(size_t?i?=?0;?i?10;?i++)
????{
????????send_data(fd);
????????sleep(1);
????}
????
????close(fd);
????return?0;
}
fifo_rd.c:
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#define?FIFO_PATH??"./fifo_file"
typedef?struct?_msg_data
{
????char?buf[128];
????int?cnt;
}msg_data_t;
int?main(void)
{
????umask(0);
????///
????int?ret?=?mkfifo(FIFO_PATH,0664?);
????if?(ret?0?&&?errno?!=?EEXIST)
????{
????????printf("mkfifo?error\n");
????????exit(EXIT_FAILURE);
????}
????///
????int?fd?=?open(FIFO_PATH,?O_RDONLY);
????if?(fd?0)
????{
????????printf("open?error\n");
????????exit(EXIT_FAILURE);
????}
????printf("open?fifo?success\n");
????while(1)
????{
????????msg_data_t?read_data?=?{0};
????????///
????????int?ret?=?read(fd,?&read_data,?sizeof(read_data));
????????if?(ret?0)
????????{
????????????printf("read?error\n");
????????????exit(EXIT_FAILURE);
????????}
????????else?if?(ret?==?0)
????????{
????????????printf("all?write?closed\nd");
????????????exit(EXIT_FAILURE);
????????}
????????printf("read_data?=?%s,?cnt?=?%d\n",?read_data.buf,?read_data.cnt);
????????sleep(1);
????}
????close(fd);
????return?0;
}
編譯、運(yùn)行:
gcc?fifo_wr.c?-o?fifo_wr
gcc?fifo_rd.c?-o?fifo_rd

信號(hào)量(Seamphore)是進(jìn)程和線程間同步的一種機(jī)制。
信號(hào)量本質(zhì)是一個(gè)非負(fù)的整型變量。增加一個(gè)可用資源執(zhí)行加一,也稱為V操作;獲取一個(gè)資源資源后執(zhí)行減一,也稱為P操作。
信號(hào)量根據(jù)信號(hào)值不同可分為兩類:
信號(hào)量根據(jù)作用對(duì)象不同可分為兩類:
POSIX信號(hào)量頭文件:
#include?
編譯鏈接需要加-lpthread參數(shù)。
信號(hào)量API接口:
/**
?*?@brief?創(chuàng)建信號(hào)量
?*
?*?Detailed?function?description
?*
?*?@param[in]?name:?信號(hào)量名稱
?*?@param[in]?mode:?訪問權(quán)限
?*?@param[in]?value:?信號(hào)量初始值
?*
?*?@return?成功時(shí)返回指向信號(hào)量的指針,出錯(cuò)時(shí)為SEM_FAILED
?*/
sem_t?*sem_open(const?char?*name,int?oflag,?mode_t?mode,?unsigned?int?value);
/**
?*?@brief?初始化信號(hào)量
?*
?*?Detailed?function?description
?*
?*?@param[in]?sem:?信號(hào)量實(shí)例地址
?*?@param[in]?pshared:?信號(hào)量作用域,分為進(jìn)程內(nèi)作用域PTHREAD_PROCESS_PRIVATE和跨進(jìn)程作用域PTHREAD_PROCESS_SHARED
?*?@param[in]?value:?信號(hào)量初始值
?*
?*?@return?成功返回0,失敗返回-1
?*/
int?sem_init(sem_t?*sem,?int?pshared,?unsigned?int?value);
/**
?*?@brief?獲取信號(hào)量
?*
?*?Detailed?function?description
?*
?*?@param[in]?sem:?信號(hào)量實(shí)例地址
?*?@param[out]?sval:?保存返回信號(hào)值地址
?*
?*?@return?成功返回0,失敗返回-1
?*/
int?sem_getvalue(sem_t?*sem,?int?*sval);
/**
?*?@brief?阻塞方式等待信號(hào)量
?*
?*?Detailed?function?description
?*
?*?@param[in]?sem:?信號(hào)量實(shí)例地址
?*
?*?@return?成功返回0,失敗返回-1
?*/
int?sem_wait(sem_t?*sem);
/**
?*?@brief?指定超時(shí)時(shí)間阻塞方式等待信號(hào)量
?*
?*?Detailed?function?description
?*
?*?@param[in]?sem:?信號(hào)量實(shí)例地址
?*?@param[in]?sem:?超時(shí)時(shí)間,單位為時(shí)鐘節(jié)拍
?*
?*?@return?成功返回0,失敗返回-1
?*/
int?sem_timedwait(sem_t?*sem,?const?struct?timespec?*abs_timeout);
/**
?*?@brief?非阻塞方式等待信號(hào)量
?*
?*?Detailed?function?description
?*
?*?@param[in]?sem:?信號(hào)量實(shí)例地址
?*
?*?@return?成功返回0,失敗返回-1
?*/
int?sem_trywait(sem_t?*sem);
/**
?*?@brief?產(chǎn)生信號(hào)量
?*
?*?Detailed?function?description
?*
?*?@param[in]?sem:?信號(hào)量實(shí)例地址
?*
?*?@return?成功返回0,失敗返回-1
?*/
int?sem_post(sem_t?*sem);
/**
?*?@brief?銷毀信號(hào)量
?*
?*?Detailed?function?description
?*
?*?@param[in]?sem:?信號(hào)量實(shí)例地址
?*
?*?@return?成功返回0,失敗返回-1
?*/
int?sem_destroy(sem_t?*sem);
/**
?*?@brief?關(guān)閉信號(hào)量
?*
?*?Detailed?function?description
?*
?*?@param[in]?sem:?信號(hào)量實(shí)例地址
?*
?*?@return?成功返回0,失敗返回-1
?*/
int?sem_close(sem_t?*sem);
/**
?*?@brief?分離信號(hào)量
?*
?*?Detailed?function?description
?*
?*?@param[in]?name:?信號(hào)量名稱
?*
?*?@return?成功返回0,失敗返回-1
?*/
int?sem_unlink(const?char?*name);
信號(hào)量基本API接口使用例子:父子進(jìn)程間通信
#include?
#include?
#include?
#include?
#include?
#define?SEM_NAME?"sem"
int?main?(void)
{
????int?sem_val?=?0;
????///
????sem_t?*sem?=?sem_open(SEM_NAME,?O_CREAT,?0666,?1);
????if?(NULL?==?sem)
????{
???????printf("sem_open?error\n");
???????exit(EXIT_FAILURE);?
????}
????///
????pid_t?pid?=?fork();
????if?(pid?==?-1)?
????{
????????printf("fork?error\n");
????????sem_close(sem);
????????sem_unlink(SEM_NAME);
????????exit(EXIT_FAILURE);
????}
????else?if(pid?==?0)?
????{
????????///
????????for?(size_t?i?=?0;?i?5;?i++)
????????{
????????????sem_wait(sem);
????????????if?(sem_getvalue(sem,?&sem_val)?!=?-1)?
????????????{
????????????????printf("child?process?P?operation,?sem_val?=?%d\n",?sem_val);
????????????????sleep(1);
????????????}
????????}
????????_exit(1);
????}
????else?if?(pid?>?0)
????{
????????///
????????for?(size_t?i?=?0;?i?5;?i++)
????????{
????????????sem_post(sem);
????????????if?(sem_getvalue(sem,?&sem_val)?!=?-1)?
????????????{
????????????????printf("prarent?process?V?operation,?sem_val?=?%d\n",?sem_val);
????????????????sleep(2);
????????????}
????????}
????}
????///
????sem_close(sem);
????if?(sem_unlink(SEM_NAME)?!=?-1)?
????{
????????printf("sem_unlink?success\n");
????}
????return?0;
}
編譯、運(yùn)行:

操作系統(tǒng)根據(jù)不同的場(chǎng)景提供了不同的方式,消息隊(duì)列、共享內(nèi)存、UNIX域套接字、管道、信號(hào)量。
消息隊(duì)列: 內(nèi)核中的一個(gè)優(yōu)先級(jí)隊(duì)列,多個(gè)進(jìn)程通過訪問同一個(gè)隊(duì)列,在隊(duì)列當(dāng)中添加或者獲取節(jié)點(diǎn)來實(shí)現(xiàn)進(jìn)程間通信。
共享內(nèi)存: 本質(zhì)是一塊物理內(nèi)存,多個(gè)進(jìn)程將同一塊物理內(nèi)存映射到自己的虛擬地址空間中,再通過頁表映射到物理地址達(dá)到進(jìn)程間通信,它是最快的進(jìn)程間通信方式,相較其他通信方式少了兩步數(shù)據(jù)拷貝操作。
UNIX域套接字: 與TCP/IP套接字使用方式相同,但UNIX域套接字以文件系統(tǒng)作為地址空間,不需經(jīng)過TCP/IP的頭部封裝、報(bào)文ack確認(rèn)、路由選擇、數(shù)據(jù)校驗(yàn)與重傳過程,因此傳輸速率上也不會(huì)受網(wǎng)卡帶寬的限制。
管道: 內(nèi)核中的一塊緩沖區(qū),分為匿名管道和命名管道。匿名管道只能用于具有親緣關(guān)系的進(jìn)程間;而命名管道可用于同一主機(jī)上任意進(jìn)程間通信。
信號(hào)量: 本質(zhì)是內(nèi)核中的一個(gè)計(jì)數(shù)器,主要實(shí)現(xiàn)進(jìn)程間的同步與互斥,對(duì)資源進(jìn)行計(jì)數(shù),有兩種操作,分別是在訪問資源之前進(jìn)行的p操作,還有產(chǎn)生資源之后的v操作。
相關(guān)資料:
由于微信公眾號(hào)近期改變了推送規(guī)則,為了防止找不到,可以星標(biāo)置頂,這樣每次推送的文章才會(huì)出現(xiàn)在您的訂閱列表里。
猜你喜歡:
實(shí)用 | 分享幾個(gè)非常實(shí)用的開源項(xiàng)目
柔性數(shù)組在實(shí)際項(xiàng)目中的應(yīng)用?
干貨 | protobuf-c之嵌入式平臺(tái)使用
C語言、嵌入式重點(diǎn)知識(shí):回調(diào)函數(shù)
實(shí)用 | 10分鐘教你搭建一個(gè)嵌入式web服務(wù)器