日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

嵌入式Linux多任务编程 进程 管道 命名管道

發(fā)布時間:2023/12/13 linux 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 嵌入式Linux多任务编程 进程 管道 命名管道 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

進程

  • 進程是一個可并發(fā)執(zhí)行的具有獨立功能的程序關(guān)于某個數(shù)據(jù)集合的一次執(zhí)行過程,也是操作系統(tǒng)執(zhí)行資源分配和保護的基本單位。
  • 程序的一次執(zhí)行就是一個進程
  • 一個程序可以派生多個進程
  • 多個不同程序運行的時候,也會有多個相對應(yīng)的進程與其相互對應(yīng)
  • 進程是動態(tài)的,有始有終,有自己的生命周期,有進程狀態(tài)的變化

程序與進程的區(qū)別

  • 程序是靜止的,無生命的,進程是活動的
  • 程序是可以脫離機器長期存在,而進程是執(zhí)行了的程序
  • 程序不具備并發(fā)性,不占據(jù)系統(tǒng)的資源,進程則相反,具備并發(fā)性、會占據(jù)內(nèi)存空間,并且會受到其他進程的制約和影響
  • 一個程序?qū)?yīng)很多的進程

進程的狀態(tài)

  • 運行狀態(tài)
  • 就緒狀態(tài) 缺少cpu
  • 等待狀態(tài) 缺少IO資源,等待

相關(guān)命令

  • 使用命令 ps -ax 查看進程的相關(guān)狀態(tài);比如進程號 、 進程的狀態(tài)、

進程樹的形成

  • 計算機啟動之后,BIOS從磁盤引導(dǎo)扇區(qū)加載系統(tǒng)引導(dǎo)程序,它將Linux系統(tǒng)裝入內(nèi)存,并且跳到內(nèi)核處開始執(zhí)行,Linux內(nèi)核就執(zhí)行初始化工作:初始化硬件、初始化內(nèi)部數(shù)據(jù)結(jié)構(gòu)、建立進程0
  • 進程0創(chuàng)建進程1,進程1是以后所有創(chuàng)建進程的祖先,它負責(zé)初始化所有的用戶進程。進程1創(chuàng)建shell,shell進程顯示提示符,等待命令的輸入
  • init進程ID為1,通常是init進程,在自檢過程結(jié)束的時候由內(nèi)核調(diào)用
  • init進程不會終止
  • init進程是一個普通的用戶進程(與交換進程不同,他不是內(nèi)核中的系統(tǒng)進程,但是它以超級用戶特權(quán)執(zhí)行)

進程的創(chuàng)建

  • 任何進程的創(chuàng)建,都是基于現(xiàn)有的進程
  • 進程的創(chuàng)建可以使用fork 和 exec
  • fork 視為新的進程分配響應(yīng)的數(shù)據(jù)結(jié)構(gòu),并且將父進程的相應(yīng)上下文信息復(fù)制過來
  • exec 將可執(zhí)行文件的正文和數(shù)據(jù)轉(zhuǎn)入內(nèi)存替代先前的內(nèi)容(從父進程復(fù)制過來的數(shù)據(jù)),并開始執(zhí)行正文段,將當前進程替換為一個新的進程

進程的終止

  • 使用exit()
  • exit釋放除了task_struct以外的所有的上下文,父進程收到子進程的終結(jié)信號之后,釋放子進程的task_struct
  • vfork調(diào)用之后,將立即調(diào)用exec,這樣就不需要拷貝父進程的所有的頁表,因此比fork快
  • pid_t fork(void);
  • pid_t vfor(void);
  • 頭文件 unistd.h

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

  • 當fork函數(shù)調(diào)用成功時,對父進程返回子進程的PID,對子進程返回0
  • 調(diào)用失敗的時候,父進程返回-1,并沒有子進程的創(chuàng)建
  • fork函數(shù)返回 兩個參數(shù),分別給父進程和子進程
pid_t new_pid; new_pid = fork(); switch(new_pid){case -1://Error!break;case 0://Child processbreak;default://parent processbreak; }

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

  • exec是用來執(zhí)行一個可執(zhí)行文件來代替當前進程的執(zhí)行鏡像
  • int execl(const char * path,const char * arg, ...)
  • int execlp(const char * file,const char * arg, ...)
  • int execle(const char *path, const char * arg,...,const char *envp[])
  • ?int execv(const char *path,const char * argv[])
  • int execve(const char * filename,char * const argv[],char * const envp[])
  • int execvp(const char *file,char * const argv[])

函數(shù)的原型

  • int execve(const char * filename,char * const argv[],char * const envp[])
  • filename? ?執(zhí)行的文件
  • argv? ? ? ? ? 傳遞給文件的參數(shù)
  • envp? ? ? ? ?傳遞給文件的環(huán)境變量
  • 當參數(shù)path所指定的文件替換原進程的執(zhí)行鏡像之后,文件的path開始執(zhí)行,參數(shù)argv和envp便傳遞給進程
  • l 表示list
  • v表示vector
  • e 可以傳遞新進程環(huán)境變量 execle execve
  • p 可執(zhí)行文件查找方式為文件的名字 execlp execvp
char* envp[] = {"PATH = /tmp","User = lei","STATUS = testing", nullptr};char* argv_execv[] = {"echo","executed by execv", nullptr};char* argv_execvp[] = {"echo","executed by execvp", nullptr};char* argv_execve[] = {"env", nullptr};if (execl("/bin/echo","echo","executed by execl", nullptr)<0)perror("Err on execl");if (execlp("echo","echo","executed by execlp", nullptr)<0)perror("Err on execlp");if(execle("/user/bin/env","env", nullptr,envp)<0)perror("Err on execle");if (execv("/bin/echo",argv_execv)<0)perror("Err on execv");if (execvp("echo",argv_execvp))perror("Err on execvp");if(execve("/user/bin/ebv",argv_execve,envp)<0)perror("Err on execve");

函數(shù)

  • ?頭文件 <sys/types.h> <unistd.h>
  • pid_t getpid(void);返回調(diào)用進程的進程ID
  • pid_t getppid(void);但會調(diào)用進程的父進程ID
  • uid_t getuid(void);返回調(diào)用進程的實際用戶ID
  • uid_t geteuid(void);返回進程的有效用戶ID
  • gid_t getgid(void);返回進程的實際組ID
  • gid_t getegit(void);調(diào)用進程的有效組ID
#include <iostream> #include <sys/types.h> #include <unistd.h>int main(){pid_t cld_pid;cld_pid = fork();if (cld_pid == 0){std::cout << "This is child process.\n" << std::endl;printf("My PID(child) is %d\n",getpid());printf("My child PID is %d\n",cld_pid );}else{std::cout << "This is parent process.\n" << std::endl;printf("My PID(parent) is %d\n",getpid());printf("My child PID is %d\n",cld_pid );}return 0; }

?exit系統(tǒng)調(diào)用

  • _exit 直接結(jié)束進程,清除其內(nèi)存使用空間,并且清除其在內(nèi)核中的數(shù)據(jù)結(jié)構(gòu)
  • exit 函數(shù)在調(diào)用之前會檢查文件的打開情況,把文件的緩存區(qū)的內(nèi)容寫會文件中,比如調(diào)用printf()函數(shù)

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

  • wait函數(shù) 用于使父進程阻塞,直到一個進程結(jié)束或者該進程收到一個指定信號為止
  • 調(diào)用wait或者waitpid的進程可能會:
  • 阻塞:如果其所有的進程還在運行
  • 帶子進程的終止狀態(tài)立即返回(如果一個子進程已經(jīng)終止,正等待父進程存取其終止狀態(tài))
  • 出錯立即返回(如果沒有任何子進程)
  • pid_t wait(int *status);? status返回子進程退出時的狀態(tài)信息
  • pid_t waitpid(pid_t pid,int *status,int options);//等待指定的進程id
  • 頭文件 <sys/types.h> 和 <sys/wait.h>
  • 兩個函數(shù)返回數(shù)值:如果成功返回子進程的ID號,如果出現(xiàn)問題,返回-1

wait和waitpid函數(shù)的區(qū)別

  • 在一個子進程終止之前,wait使其調(diào)用者阻塞,而waitpid有一個選項,可以不阻塞調(diào)用者
  • waitpid并不等待第一個終止的子進程,他有若干個選擇項,可以控制他所等待的特定進程
  • 實際上,wait函數(shù)是waitpid的一個特例

守護進程

  • 創(chuàng)建子進程,然后父進程退出
  • 在子進程中創(chuàng)建新的會話 setsid();
  • 改變目錄為根目錄 chdir("/");
  • 重設(shè)文件的權(quán)限掩碼umask(0);
  • 關(guān)閉文件描述符 for(int i = 0;i < MAXFILE; i++){close(i);}
  • 守護進程的工作 while(1){}
#include <iostream> #include <unistd.h> #include <ftw.h> #include <fcntl.h>int main(){pid_t pc;char *buf = "This is a Daemon\n";int len = strlen(buf);pc = fork();if (pc < 0){printf("Error fork!\n");exit(-1);} else if (pc > 0){exit(0);}setsid();//在子進程中創(chuàng)建新的會話chdir("/");//改變目錄為根目錄umask(0);//重設(shè)文件的權(quán)限掩碼for (int i = 0; i < 65535; ++i) {close(i);}int fd;while (1){if (fd = open("/tmp/daemon.log",O_CREAT|O_WRONLY|O_APPEND,0600) < 0){perror("open");exit(1);}write(fd,buf,len+1);close(fd);sleep(10);}return 0; }

sleep函數(shù)

  • 函數(shù)使用sleep用來指定進程掛起來的指定的秒數(shù)。該函數(shù)的調(diào)用格式如下
  • unsigned int sleep(unsigned int seconds);
  • 頭文件 unistd.h

進程間通信

  • 數(shù)據(jù)傳輸:一個進程將 數(shù)據(jù) 發(fā)送給另外一個進程,數(shù)據(jù)大小一般是一個字節(jié)到幾兆字節(jié)之間
  • 共享數(shù)據(jù):多個進程之間操作共享數(shù)據(jù),一個進程對共享數(shù)據(jù)的修改,另外的進程可以看到
  • 通知事件:一個進程向另外一個進程發(fā)送消息,通知它發(fā)生了某件事情。(如:子進程終止,通知父進程)
  • 資源共享:多個進程之間共享相同的資源,需要內(nèi)核提供 鎖 和 同步機制
  • 進程控制:有些進程完全控制另一個進程的執(zhí)行(如Debug),這個時候,進程會攔截另外一個進程所有的陷入(陷入內(nèi)核)和異常,并且及時知道他的狀態(tài)改變
  • 進程間通信 (IPC)
  • Linux進程間通信 包括Unix進程間通信、Socket進程間通信、POSIX進程間通信、System V進程間通信
  • Unix進程間通信 -> 管道 FIFO 信號
  • System V進程間通信 ->?System V消息隊列??System V信號燈??System V共享內(nèi)存
  • POSIX進程間通信 -> posix消息隊列?posix信號燈??posix共享內(nèi)存

現(xiàn)在的linux使用的進程間通信方式

  • 管道(pipe) 和 有名管道(FIFO)
  • 信號(signal):比較復(fù)雜的通信方式,用于通知其余進程,信號可以用于進程之間通信外,信號還可以發(fā)送信號給進程本身。
  • 消息隊列:消息的鏈接表,包括posix消息隊列和system V消息隊列;有權(quán)限的進程可以讀寫消息隊列;克服了信號承載信息量少,管道只能承載無格式字節(jié)流以及緩沖區(qū)大小受限等缺陷
  • 共享內(nèi)存:多個進程訪問同一塊內(nèi)存空間,是最快的IPC方式,往往結(jié)合信號量來實現(xiàn)進程間同步以及互斥
  • 信號量 (使用不多)
  • 套接字(socket)? (網(wǎng)絡(luò))

管道和有名管道的區(qū)別

  • 管道:具有親緣關(guān)系的進程間通信,需要通信雙方的進程有共同的祖先進程
  • 有名管道:允許無親緣關(guān)系的管道之間通信,在文件系統(tǒng)中有對應(yīng)的文件名字;有名管道通過命令 mkfifo或系統(tǒng)調(diào)用mkfifo來創(chuàng)建

具體使用

管道

  • linux命令允許重定向,重定向就是管道 例如 ls > 1.txt
  • 管道是單向的、先進先出的,無結(jié)構(gòu)的、固定大小的字節(jié)流,他把一個進程的標準輸出和另外一個進程的標準輸入連接在一起
  • 寫進程在管道的末尾輸入數(shù)據(jù),讀進程在管道的首端讀取數(shù)據(jù)。數(shù)據(jù)被讀出后會從管道中移除,其他進程對此不可見了
  • 管道提供了簡單的流控制機制。進程試圖讀取空的管道時,在有數(shù)據(jù)填充管道之前會處于阻塞狀態(tài);同理,管道數(shù)據(jù)已滿時,未有讀進程讀取數(shù)據(jù),寫進程往管道里面寫入數(shù)據(jù)會處于阻塞狀態(tài)
  • 管道用于進程之間通信
  • 使用系統(tǒng)調(diào)用pipe() 函數(shù),創(chuàng)建一個簡單的管道,只需接受一個參數(shù),也就是一個包括兩個整數(shù)的數(shù)組。
  • 如果系統(tǒng)調(diào)用成功,此數(shù)組將包括管道使用的兩個文件描述符
  • 創(chuàng)建一個管道之后,一般情況下進程將會產(chǎn)生一個新的進程
  • 系統(tǒng)調(diào)用pip()? 原型:int pipe(int fd[2])
  • 返回值:成功返回0 ;失敗返回 -1
  • errno = EMFILE? 沒有空閑的文件描述符
  • errno = ENFILE? 系統(tǒng)文件表已滿
  • errno = EFAULT? fd數(shù)組無效

代碼

#include <stdio.h> #include <unistd.h>int main(void) {int pipe_fd[2];if(pipe(pipe_fd) < 0){printf("pipe create error!\n");return -1;} else{printf("pipe create success!\n");}close(pipe_fd[0]);close(pipe_fd[1]);return 0; }
  • 先創(chuàng)建一個管道 再創(chuàng)建一個子進程?

父子進程之間通信? 父進程寫入;子進程讀入數(shù)據(jù)

  • 當讀到10的時候,結(jié)束進程
#include <stdio.h> #include <unistd.h> #include <sys/wait.h>int main(void) {int pipe_fd[2];pid_t pid;char buf[2]={0};//在創(chuàng)建子進程之前創(chuàng)建管道if(pipe(pipe_fd) < 0){printf("pipe create error!\n");_exit(-1);} else{printf("pipe create success!\n");printf("pipe_fd[0] = %d,pipe_fd[1] = %d",pipe_fd[0],pipe_fd[1]);}pid = fork();//創(chuàng)建子進程//子進程執(zhí)行的代碼if (pid == 0){close(pipe_fd[1]);while (1){if (read(pipe_fd[0],buf,1) > 0){ // if (buf[0] % 3 == 0 && buf[0] != 0)printf("%d\n",buf[0]);if (buf[0] == 10)_exit(0);}}close(pipe_fd[0]);} else{//父進程執(zhí)行的代碼close(pipe_fd[0]);for (buf[0]=0;buf[0]<=10;buf[0]++) {write(pipe_fd[1],buf,1);}close(pipe_fd[1]);wait(nullptr);}return 0; }

上面這個例子 感覺有問題?

#include <stdio.h> #include <unistd.h> #include <wait.h> #include <cstring>int main(){int pipe_fd[2];if (pipe(pipe_fd) < 0){printf("pipe create error!\n");_exit(-1);return -1;} else{printf("pipe create success!\n");printf("pipe_fd[0] = %d,pipe_fd[1] = %d",pipe_fd[0],pipe_fd[1]);}pid_t pid = fork(); // char buf[2] = {0};char buf[100] ={"Hello world"};int len = strlen(buf);if (pid == 0){close(pipe_fd[1]);while(1){if (read(pipe_fd[0],buf,len) > 0){printf("%s\n",buf);}}close(pipe_fd[0]);} else{//父進程執(zhí)行此段代碼close(pipe_fd[0]);for (int i = 0;i<10;i++){write(pipe_fd[1], buf, len);}close(pipe_fd[1]);wait(nullptr);}return 0; }

?簡單修改如下

?

  • ?通過打開兩根管道,實現(xiàn)一個雙向的管道,但是需要在子進程中正確的設(shè)置文件的描述符號
  • 必須在fork之前使用pipe(),否則子進程將不會繼承文件的描述符
  • 使用半雙工管道的時候,任何關(guān)聯(lián)的進程都需要共享一個相關(guān)的祖先進程。因為管道存儲在系統(tǒng)內(nèi)核中,不繼承祖先將無法尋址,有名管道就不會出現(xiàn)這個問題

代碼

#include <stdio.h> #include <unistd.h> #include <sys/wait.h>int main(void) {int pipe_fd[2];int pipe_fd1[2];pid_t pid;char buf_r[100]={0};char * p_wbuf = nullptr;int r_num;//在創(chuàng)建子進程之前創(chuàng)建管道if(pipe(pipe_fd) < 0){printf("pipe create error!\n");_exit(-1);}if(pipe(pipe_fd1) < 0){printf("pipe create error!\n");_exit(-1);}printf("pipe create success!\n"); // printf("pipe_fd[0] = %d,pipe_fd[1] = %d",pipe_fd[0],pipe_fd[1]); // printf("pipe_fd1[0] = %d,pipe_fd1[1] = %d",pipe_fd1[0],pipe_fd1[1]);pid = fork();//創(chuàng)建子進程//子進程執(zhí)行的代碼if (pid == 0){printf("******子進程******\n");//read pipeclose(pipe_fd[1]);r_num = read(pipe_fd[0],buf_r,100);if (r_num > 0){printf("children r_num = %d, pipe is: %s\n",r_num,buf_r);}close(pipe_fd[0]);//write pipeclose(pipe_fd1[0]);if (write(pipe_fd1[1],"ByeBye!",7)!=-1){printf("children process write ByeBye! success!\n");}close(pipe_fd1[1]);printf("children process exit!\n");_exit(0);} else if (pid > 0){printf("******父進程******\n");//write pipeclose(pipe_fd[0]);if (write(pipe_fd[1],"Hello!",6) !=-1){printf("father process write Hello! success!\n");}close(pipe_fd[1]);//read processclose(pipe_fd1[1]);r_num = read(pipe_fd1[0],buf_r,100);if (r_num > 0){printf("father r_num = %d, pipe is: %s\n",r_num,buf_r);}close(pipe_fd1[0]);wait(nullptr);printf("father process exit!\n");_exit(0);}return 0; }
  • ?父進程 給 子進程 傳輸 Hello!;接收輸出子進程的 ByeBye!
  • 子進程 給 父進程 傳輸?ByeBye!;接收輸出父進程的 Hello!
  • 子進程退出,然后父進程退出
  • r_num 為讀取的數(shù)據(jù)長度

命名管道

  • 作為一個特殊的設(shè)備文件而存在
  • 適用于無血緣關(guān)系的進程之間通信
  • 即使進程之間不再需要管道,但是命名管道仍然繼續(xù)保存在文件系統(tǒng)中,便于以后使用
  • 使用函數(shù) int mkfifo(const char * pathname,mode_t mode)
  • 函數(shù)說明:mkfifo()會依參數(shù)pathname 建立特殊的FIFO 文件, 該文件必須不存在, 而參數(shù)mode 為該文件的權(quán)限 (mode%~umask), 因此umask 值也會影響到FIFO 文件的權(quán)限. Mkfifo()建立的FIFO 文件其他進程都可以用讀寫一般文件的方式存取. 當使用open()來打開FIFO 文件時, O_NONBLOCK 旗標會有影響:
  • 頭文件:#include <sys/types.h> ??#include <sys/stat.h>
  • 1. 當使用O_NONBLOCK 旗標時, 打開FIFO 文件來讀取的操作會立刻返回, 但是若還沒有其他進程打開FIFO 文件來讀取, 則寫入的操作會返回ENXIO 錯誤代碼.
  • 2. 沒有使用O_NONBLOCK 旗標時, 打開FIFO 來讀取的操作會等到其他進程打開FIFO 文件來寫入才正常返回. 同樣地, 打開FIFO 文件來寫入的操作會等到其他進程打開FIFO 文件來讀取后才正常返回.
  • 返回值:若成功則返回0, 否則返回-1, 錯誤原因存于errno 中
  • 使用ls -l查看管道文件 顯示p開頭
  • 普通文件 是一個 -?

錯誤代碼:

  • 1、EACCESS 參數(shù)pathname 所指定的目錄路徑無可執(zhí)行的權(quán)限
  • 2、EEXIST 參數(shù)pathname 所指定的文件已存在.
  • 3、ENAMETOOLONG 參數(shù)pathname 的路徑名稱太長.
  • 4、ENOENT 參數(shù)pathname 包含的目錄不存在
  • 5、ENOSPC 文件系統(tǒng)的剩余空間不足
  • 6、ENOTDIR 參數(shù)pathname 路徑中的目錄存在但卻非真正的目錄.
  • 7、EROFS 參數(shù)pathname 指定的文件存在于只讀文件系統(tǒng)內(nèi).

注意事項

  • 打開FIFO,使用非阻塞標志(O_NONBLOCK)產(chǎn)生以下的影響
  • 如果未說明O_NONBLOCK,只讀打開會要阻塞某個寫進程打開此FIFO;類似,只寫打開要阻塞某個讀進程打開此FIFO;讀寫互斥
  • 如果指定了O_NONBLOCK,則只讀打開立即返回,但是如果沒有進程為了讀而打開一個FIFO,那么只寫打開將出錯返回,其errno是ENXIO
  • 類似管道,若寫一個尚無進程為讀而打開的FIFO,則產(chǎn)生信號為SIGPIPE;若某個FIFO最后一個寫進程關(guān)閉了該FIFO,則將為該FIFO的讀進程產(chǎn)生一個文件結(jié)束標志

參考鏈接

  • Linux進程描述符task_struct結(jié)構(gòu)體詳解--Linux進程的管理與調(diào)度(一)
  • Linux 系統(tǒng)啟動過程
  • Linux系統(tǒng)啟動流程詳解
  • fork和exec的區(qū)別
  • Linux中fork,vfork和clone詳解(區(qū)別與聯(lián)系)
  • C語言vfork()函數(shù):建立新的進程
  • linux系統(tǒng)編程之進程(五):exec系列函數(shù)(execl,execlp,execle,execv,execvp)使用
  • C語言waitpid()函數(shù):中斷(結(jié)束)進程函數(shù)(等待子進程中斷或
  • linux守護進程詳解及創(chuàng)建,daemon()使用
  • linux系統(tǒng)的7種運行級別
  • SGX的內(nèi)部組件概述(一)找一個好的工作,為自己活
  • C語言mkfifo()函數(shù):建立具名管道
  • linux驅(qū)動重要頭文件!!!
  • errno頭文件

總結(jié)

以上是生活随笔為你收集整理的嵌入式Linux多任务编程 进程 管道 命名管道的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。