Linux系统编程常见函数 (进程/线程)
Lunix系統編程函數多且復雜,本文記錄一下Linux系統編程最最常用的哪些函數以及這些函數的常見用法。當然本文不可能囊括所有,所以在編程時候最好的學習方法還是查看manpage,多看多寫,熟能生巧。
而且這里只是總結一下函數的最常用的用法和參數,相關知識還是要看書看視頻學習的。
博主學習Linux編程不久,水平有限文章有錯誤還請各位不吝指出。廢話不多說,開始咯。
這篇主要歸納進程和線程相關的函數,文件和目錄操作看上一篇:https://www.cnblogs.com/clno1/p/12935626.html。
進程相關:
fork函數:
pid_t fork(void); 作用是創建一個子進程。
失敗返回-1;成功返回:① 父進程返回子進程的 ID(非負) ②子進程返回 0 (亦即我們用fork返回值判斷當前是父/子進程)
父子相同處: 全局變量、.data、.text、棧、堆、環境變量、用戶 ID、宿主目錄、進程工作目錄、信號處理方式...
父子不同處: 1.進程 ID 2.fork 返回值 3.父進程 ID 4.進程運行時間 5.鬧鐘(定時器) 6.未決信號集
父子進程的copyonwrite機制:https://www.pianshen.com/article/4305691855/
getpid函數:
pid_t getpid(void); 作用是獲取當前進程 ID(返回值)
getppid函數:
pid_t getppid(void); 獲取當前進程的父進程 ID(返回值)
getuid 函數:
獲取當前進程實際用戶 ID uid_t getuid(void);
獲取當前進程有效用戶 ID uid_t geteuid(void);
getgid 函數
獲取當前進程使用用戶組 ID gid_t getgid(void);
獲取當前進程有效用戶組 ID gid_t getegid(void);
孤兒進程與講師進程,以及回收子進程:
https://www.cnblogs.com/clno1/p/12937547.html
守護進程相關:
https://www.cnblogs.com/clno1/p/12941308.html
進程間通信:
在操作系統歷史中出現了很多IPC,如文件、管道、信號、共享內存、消息隊列、套接字、命名管道等。但是現今常用的進程間通信方式有:① 管道 (使用最簡單) ,② 信號 (開銷最小),③ 共享映射區 (無血緣關系),④ 本地套接字 (最穩定
匿名管道PIPE:
int pipe(int pipefd[2]); 函數作用創建匿名管道。成功:0;失敗:-1,設置 errno;
函數調用成功返回 r/w 兩個文件描述符。無需 open,但需手動 close。規定:fd[0] → r; fd[1] → w,就像 0對應標準輸入,1 對應標準輸出一樣。向管道文件讀寫數據其實是在讀寫內核緩沖區。
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<string.h>
4 #include<unistd.h>
5 #include<errno.h>
6 #include<pthread.h>
7
8 void sys_err(const char *str) {
9 perror(str);
10 exit(1);
11 }
12
13 int main(int argc,char *argv[])
14 {
15 int fd[2];
16 int ret;
17 pid_t pid;
18
19 ret=pipe(fd); //創建管道
20 if (ret==-1) sys_err("pipe error");
21
22
23 pid=fork(); //創建子進程
24
25 //父子進程合作完成ls wc -l
26 if (pid==-1) //失敗
27 sys_err("fork error");
28 else if (pid>0) { //父進程完成wc -l
29 close(fd[1]); //父進程讀,關閉寫端
30 dup2(fd[0],STDIN_FILENO); //有讀端且為空,pipe會阻塞到讀到數據
31 execlp("wc","wc","-l",NULL);
32 sys_err("exclp wc error"); //一旦執行了execlp就結束了,沒法走到這里,所以這里可以用來判錯
33 } else if (pid==0) { //子進程完成ls
34 close(fd[0]); //子進程寫,關閉讀端
35 dup2(fd[1],STDOUT_FILENO);
36 execlp("ls","ls",NULL);
37 sys_err("exclp ls error");
38 }
39 return 0;
40 }
PIPE完成ls wc -l
命名管道FIFO:
匿名管道(pipe)只能用于“有血緣關系”的進程間。但通過 FIFO,不相關的進程也能交換數據。FIFO 是 Linux 基礎文件類型中的一種。但,FIFO 文件在磁盤上沒有數據塊,僅僅用來標識內核中一條通道。各
進程可以打開這個文件進行 read/write,實際上是在讀寫內核通道,這樣就實現了進程間通信。
創建方式:1. 命令:mkfifo 管道名 2. 庫函數:int mkfifo(const char *pathname, mode_t mode); 成功:0; 失敗:-1
一旦使用 mkfifo 創建了一個 FIFO,就可以使用 open 打開它,常見的文件 I/O 函數都可用于 fifo。如:close、read、write、unlink 等
共享映射區MMap:
原理是將一個磁盤文件映射到內存地址空間緩沖區,于是當從緩沖區中取數據,就相當于讀文件中的相應字節。于此類似,將數據存入緩沖區,則相應的字節就自動寫入文件。
mmap 函數
void *mmap(void *adrr, size_t length, int prot, int flags, int fd, off_t offset); 函數作用是建立映射文件。
返回:成功:返回創建的映射區首地址; 失敗:MAP_FAILED 宏
addr: 建立映射區的首地址,由 Linux 內核指定。使用時,直接傳遞 NULL
length: 欲創建映射區的大小
prot: 映射區權限 PROT_READ、PROT_WRITE、PROT_READ|PROT_WRITE
flags: 標志位參數(常用于設定更新物理區域、設置共享、創建匿名映射區)
MAP_SHARED: 會將映射區所做的操作反映到物理設備(磁盤)上。
MAP_PRIVATE: 映射區所做的修改不會反映到物理設備。
fd: 用來建立映射區的文件描述符
offset: 映射文件的偏移(4k 的整數倍)
父子等有血緣關系的進程之間也可以通過 mmap 建立的映射區來完成數據通信。但相應的要在創建映射區的時候指定對應的標志位參數 flags:
MAP_PRIVATE: (私有映射) 父子進程各自獨占映射區; MAP_SHARED: (共享映射) 父子進程共享映射區;
munmap 函數
int munmap(void *addr, size_t length); 函數作用是釋放剛剛建立映射的內存緩沖區。
返回值: 成功:0; 失敗:-1 errno
信號:
信號重要且相對復雜,開新篇總結:https://www.cnblogs.com/clno1/p/12941316.html
線程相關:
線程(ight weight process)稱為輕量級的進程,本質仍是進程(在 Linux 環境下),一個區別在于:進程,獨立地址空間,擁有 PCB 。線程,有獨立的 PCB,但沒有獨立的地址空間(共享)。
線程共享資源 1.文件描述符表2.每種信號的處理方式3.當前工作目錄4.用戶 ID 和組 ID5.內存地址空間 (.text/.data/.bss/heap/共享庫)
線程非共享資源 1.線程 id2.處理器現場和棧指針(內核棧)3.獨立的棧空間(用戶空間棧)4.errno 變量5.信號屏蔽字6.調度優先級
pthread_create 函數
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); 創建一個新線程。 其作用,對應進程中 fork() 函數。
返回值:成功:0; 失敗:錯誤號 -----Linux 環境下,所有線程特點,失敗均直接返回錯誤號。
參數:pthread_t:當前 Linux 中可理解為:typedef unsigned long int pthread_t;
參數 1:傳出參數,保存系統為我們分配好的線程 ID
參數 2:通常傳 NULL,表示使用線程默認屬性。若想使用具體屬性也可以修改該參數。
參數 3:函數指針,指向線程主函數(線程體),該函數運行結束,則線程結束。
參數 4:線程主函數執行期間所使用的參數。 (即參數3和4描述了線程的函數)
pthread_self 函數
pthread_t pthread_self(void); 獲取線程 ID。其作用對應進程中 getpid() 函數。 返回值:成功:0; 失敗:無!
線程 ID:pthread_t 類型,本質:在 Linux 下為無符號整數(%lu),其他系統中可能是結構體實現
線程 ID 是進程內部,識別標志。(兩個進程間,線程 ID 允許相同)
注意:不應使用全局變量 pthread_t tid,在子線程中通過 pthread_create 傳出參數來獲取線程 ID,而應使用pthread_self。
pthread_exit 函數
void pthread_exit(void *retval); 將單個線程退出 參數:retval 表示線程退出狀態,通常傳 NULL
pthread_join 函數
int pthread_join(pthread_t thread, void **retval); 阻塞等待線程退出,獲取線程退出狀態 其作用,對應進程中 waitpid() 函數。 成功:0;失敗:錯誤號
參數:thread:線程 ID (【注意】:不是指針);retval:存儲線程結束狀態。
pthread_detach 函數
int pthread_detach(pthread_t thread); 實現線程分離 成功:0;失敗:錯誤號
線程分離狀態:指定該狀態,線程主動與主控線程斷開關系。線程結束后,其退出狀態不由其他線程獲取,而直接自己自動釋放。網絡、多線程服務器常用。
一般情況下,線程終止后,其終止狀態一直保留到其它線程調用 pthread_join 獲取它的狀態為止。但是線程也可以被置為 detach 狀態, 這樣的線程一旦終止就立刻回收它占用的所有資源,而不保留終止狀態。
不能對一個已經處于 detach 狀態的線程調用 pthread_join,這樣的調用將返回 EINVAL 錯誤。也就是說,如果已經對一個線程調用了 pthread_detach 就不能再調用 pthread_join 了。
也可使用 pthread_create 函數參 2(線程屬性)來設置線程分離。
pthread_cancel 函數
int pthread_cancel(pthread_t thread); 殺死(取消)線程 其作用,對應進程中 kill() 函數。 成功:0;失敗:錯誤號
值得注意的是:線程的取消并不是實時的,而有一定的延時。需要等待線程到達某個取消點(檢查點)。
取消點:是線程檢查是否被取消,并按請求進行動作的一個位置。通常是一些系統調用 creat,open,pause,close,read,write..... 執行命令 man 7 pthreads 可以查看具備這些取消點的系統調用列表。
可粗略認為一個系統調用(進入內核)即為一個取消點。如線程中沒有取消點,可以通過調用 pthread_testcancel函數自行設置一個取消點。
總結:終止某個線程而不終止整個進程,有三種方法:
1. 從線程主函數 return。這種方法對主控線程不適用,從 main 函數 return 相當于調用 exit。
2. 一個線程可以調用 pthread_cancel 終止同一進程中的另一個線程。
3. 線程可以調用 pthread_exit 終止自己。
線程同步:https://www.cnblogs.com/clno1/p/12942972.html
總結
以上是生活随笔為你收集整理的Linux系统编程常见函数 (进程/线程)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 利用Messager信使服务在局域网内发
- 下一篇: 司空见惯的司空是指什么东西(司空见惯的“