當(dāng)前位置:首頁 > 嵌入式培訓(xùn) > 嵌入式學(xué)習(xí) > 講師博文 > linux系統(tǒng)中管道的介紹和線程同步代碼示例
傳統(tǒng)的進(jìn)程間通信其中有無名管道(PIPE)、有名管道(FIFO)和信號(hào)(Signal)。咱們今天就說說linux中基于POSIX的有名管道(FIFO)和無名管道(PIPE)。
1. 描述:
管道提供一個(gè)單向的進(jìn)程間通訊通道。一個(gè)管道,有一個(gè)讀端和一個(gè)寫端。寫到寫端的數(shù)據(jù)能從這個(gè)管道的讀端讀出。
無名管道,在linux中可以通過pipe2函數(shù)來創(chuàng)建一個(gè)無名管道,執(zhí)行這個(gè)函數(shù)后,會(huì)返回兩個(gè)文件描述符,一個(gè)指向管道的讀端,一個(gè)指向管道的寫端。無名管道用于有親緣關(guān)系的進(jìn)程之間通信。通信方式類似半雙工通信方式。
有名管道(先進(jìn)先出的縮寫),在linux文件系統(tǒng)內(nèi)有一個(gè)名字(區(qū)別無名管道),可以通過mkfifo函數(shù)來創(chuàng)建一個(gè)有名管道。任何進(jìn)程都可以通過open函數(shù)來打開一個(gè)有名管道文件,前提是這個(gè)進(jìn)程有打開權(quán)限。讀端使用open函數(shù)打開的標(biāo)識(shí)是O_RDONLY;寫端在open函數(shù)中使用O_WRONLY來打開。用于任何進(jìn)程之間的通信。
注意:
0)盡管一個(gè)有名管道在文件系統(tǒng)中有一個(gè)名字,但是對(duì)于隱藏設(shè)備,管道的I/O操作不起作用。
1)有名管道和無名管道的不同就在他們的創(chuàng)建和打開的方式上。一旦創(chuàng)建和打開任務(wù)已經(jīng)完成,那么有名管道和無名管道的I/O操作意義完全相同。
2)管道提供的通訊通道是字節(jié)流:沒有消息邊界的概念。
3)不能使用lseek函數(shù)來操作管道。
4)在使用fork創(chuàng)建子進(jìn)程的時(shí)候,要注意適當(dāng)合理的使用close關(guān)閉子進(jìn)程從父進(jìn)程繼承拷貝的多余的讀端或者是寫端的文件描述符。
5)管道中的數(shù)據(jù)從讀端讀走了,就從管道中消失了。
2. 規(guī)則:
1. 讀(讀端寫端都存在):如果一個(gè)進(jìn)程讀一個(gè)空管道,read函數(shù)會(huì)阻塞,直到管道中有可用數(shù)據(jù)。
2. 寫(讀端寫端都存在):如果一個(gè)進(jìn)程想寫一個(gè)已滿管道,write函數(shù)會(huì)阻塞,直到管道中有足夠的數(shù)據(jù)被讀出來讓write函數(shù)完成寫操作。
如果想讓讀寫不阻塞,可以參考fcntl函數(shù)。使用F_SETFL和O_NONBLOCK來完成。
3. 讀(寫端全部關(guān)閉):如果指向?qū)懚说乃形募枋龇急魂P(guān)閉,read函數(shù)對(duì)該管道讀的嘗試結(jié)果會(huì)返回0;
4. 寫(讀端全部關(guān)閉):如果指向讀端的所有文件描述符都被關(guān)閉,write函數(shù)對(duì)該管道的寫操作會(huì)產(chǎn)生SIGPIPE信號(hào),并發(fā)送給調(diào)用write函數(shù)的進(jìn)程;如果調(diào)用write的進(jìn)程忽略了SIFPIPE信號(hào),那write會(huì)帶著EPIPE的錯(cuò)誤而失敗。
管道中的數(shù)據(jù)可讀性只有一次。也就是說數(shù)據(jù)在管道中閱后即焚。管道中的數(shù)據(jù)讀了就沒有了。
(1) 無名管道:int pipe(int pipefd[2]);
參數(shù):
//pipefd 獲得操作管道的文件描述符 ,讀:pipefd[0],寫:pipefd[1]。
返回值: 成功返回0,失敗返回-1
(2)創(chuàng)建有名管道文件
int mkfifo(const char *pathname, mode_t mode);
@pathname 管道文件名
@mode 指定文件的權(quán)限
返回值:
成功返回0,失敗返回-1
下面以兩個(gè)線程中使用有名管道的同步通信為例,展示相關(guān)代碼的使用:
#include
#include
//strlen memset
#include
//mkfifo open
#include
#include
//open
#include
//read
#include
//errno
#include
//pthrea_mutex_unlock
#include
//sem_init sem_wait sem_post
#include
#define N 1024//緩存buf的大小
sem_t read_sem;//讀信號(hào)資源
sem_t write_sem;//寫信號(hào)資源
void *read_fifo(void* args){//讀管道線程的線程主體
int ret = -1;
int fd_r = -1;
char buf[N] = {0};
ssize_t num = 0;
ret = mkfifo((char*)args,0666);
if(ret < 0 && errno != EEXIST){
perror("read_fifo mkfifo failed");
return (char*)args;
}
fd_r = open((char*)args,O_RDONLY|O_NONBLOCK,0666);/*非阻塞方式打開文件獲取文件描述符*/
if(fd_r < 0){
perror("read_fifo open failed");
return (char*)args;
}
while(1)
{
sem_wait(&read_sem);/*讀管道線程獲取讀管道的資源,實(shí)現(xiàn)和寫線程的同步*/
memset(buf,0,sizeof(buf));/*清空緩沖區(qū)*/
num = read(fd_r,buf,sizeof(buf));
buf[N-1] = '\0';/*輸出前的防止越界操作*/
if(num > 0){
printf("%d read:%s\n",num,buf);
}
if(strncmp(buf,"q",1) == 0){/*對(duì)退出字符串的捕捉*/
close(fd_r);
sem_post(&write_sem);
break;
}
sem_post(&write_sem);/*釋放寫操作資源*/
}
close(fd_r);
return (char*)args;
}
void *write_fifo(void* args){
int ret = -1;
int fd_w = -1;
char buf[N] = {0};
ssize_t num = 0;
ret = mkfifo((char*)args,0666);
if(ret < 0 && errno != EEXIST){//排除已經(jīng)創(chuàng)建管道的錯(cuò)誤退出情況
perror("read_fifo mkfifo failed");
return (char*)args;
}
fd_w = open((char*)args,O_WRONLY|O_NONBLOCK,0666);
if(fd_w < 0){
perror("read_fifo open failed");
return (char*)args;
}
while(1)
{
sem_wait(&write_sem);
printf("#:");/*輸入提示符*/
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf) - 1] = '\0';/*對(duì)fgets獲取到終端的’\n’字符的消除*/
num = write(fd_w,buf,strlen(buf));
if(strncmp(buf,"q",1) == 0){
close(fd_w);
sem_post(&read_sem);
break;
}
sem_post(&read_sem);
}
close(fd_w);
return (char*)args;
}
int main(int argc, const char *argv[])
{
int ret_p = -1;
pthread_t tid[2] = {0};
sem_init(&read_sem,0,0);/*程序啟動(dòng)時(shí),管道沒有資源,故讀的資源要初始化為0*/
sem_init(&write_sem,0,1);/*程序啟動(dòng)時(shí),管道首先需要一個(gè)寫操作,故寫資源為1*/
if(argc < 2){/*程序運(yùn)行時(shí),緊跟其后的輸入?yún)?shù)判斷*/
perror("Input wrong format");
puts("Excuting_code_name + new_fifoname");
exit(EXIT_SUCCESS);
}
ret_p = pthread_create(&tid[0],NULL,read_fifo,(char*)argv[1]);/*把在程序后的參數(shù)傳遞給相關(guān)進(jìn)程作為有名管道文件的文件名*/
if(ret_p < 0){
perror("read_fifo pthread_create failed");
exit(EXIT_FAILURE);
}
#if 1
ret_p = pthread_create(&tid[1],NULL,write_fifo,(char*)argv[1]);
if(ret_p < 0){
perror("write_fifo pthread_create failed");
exit(EXIT_FAILURE);
}
#endif
pthread_join(tid[0],NULL);/*線程的阻塞回收,亦可以防止主進(jìn)程結(jié)束*/
pthread_join(tid[1],NULL);
return 0;
}