目錄
1.進程組和會話
2.守護進程
2.1守護進程daemon概念?
2.2創建守護進程
3.線程
3.1線程的概念
3.2線程內核三級映射
3.3線程共享
3.4線程優缺點
4.線程控制原語
4.1獲取線程id
4.2創建線程
4.3循環創建N個子線
4.4子線程傳參地址,錯誤示例
4.5線程退出
4.6線程回收
4.6.1回收示例1
4.6.2回收示例2
4.7線程分離
4.8殺死(取消)線程
4.8.1終止線程方法
1.進程組和會話
- 進程聚集成為進程組,多個進程組聚集成為會話。
- ps ajx //查看 進程組id ?和 會話id
- 創建會話 setsid()
?pid_t setsid(void);?
- 組長進程不能作為新會話的首進程,因此 fork() 后,終止父進程,子進程調用 setsid() 創建會話,以自己的進程pid, 為會話id 和 進程組 id。
2.守護進程
2.1守護進程daemon概念?
- Linux后臺的一些服務進程,沒有控制終端,不能直接和用戶交互。不受用戶登錄、注銷的影響,一直在后臺運行。周期性的執行某種任務,或者等待某一事件的發生。 一般采用以d 結尾的命名方式。
2.2創建守護進程
int main(int argc, char *argv[])
{pid_t pid;int ret, fd;pid = fork();?? ??? ?// 創建子進程if (pid > 0)exit(0);?? ??? ?// 終止父進程pid = setsid();?? ??? ?// 子進程創建新會話.if (pid == -1)sys_err("setsid err");ret = chdir("/home/itcast/bj_40"); ?// 改變工作目錄if (ret == -1)sys_err("chdir err");umask(0);?? ??? ??? ?// 改變文件訪問權限掩碼,沒有屏蔽任何權限close(STDIN_FILENO);// 關閉標準輸入文件描述符fd = open("/dev/null", O_RDWR); ? // fd ---> 0if (fd == -1)sys_err("open err");dup2(fd, STDOUT_FILENO);dup2(fd, STDERR_FILENO);while (1);?? ??? ??? ?// 模擬守護進程業務return 0;
}
3.線程
3.1線程的概念
- Linux 系統中,線程 LWP 稱之為:輕量級的進程。
- 進程:有獨立的進程地址空間, 有獨立的 pcb。 —— 最小資源分配單位。
- 線程:有獨立的pcb,沒有獨立的進程地址空間。(與其他線程共享) —— 最小執行單位。
- 一個創建了線程的進程,本身也淪落 為線程。
- LWP 號: cpu 劃分時間片依據。 ?—— 線程 最小執行單位。
- 查看LWP號命令: ps -Lf 進程pid?
3.2線程內核三級映射
- 三級映射。—— 解釋了,為什么線程沒有獨立的進程地址空間。
3.3線程共享
- 獨享:棧空間(用戶棧、內核棧)errnum?
- 共享:./text ?./ordata ?./data ?./bss ?heap堆 ----> 共享全局變量
3.4線程優缺點
- 優點:
? ? - 并發性強。 ?
? ? - 開銷小。
? ? - 數據通信方便。
- 缺點:
? ? - 庫函數,穩定性差。
? ? - 調試、編寫困難
? ? - 對信號支持差。
- 結論:既能使用進程開發,也能使用線程開發的程序,首選 線程。
4.線程控制原語
4.1獲取線程id
#include <pthread.h>
pthread_t pthread_self(void); ??// 獲取線程id, 在進程內部標識線程身份。
返回值:線程id
4.2創建線程
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
?????????????????? void *(*start_routine) (void *), void *arg);參1: 傳出參數,新子線的 線程id
參2: 線程屬性。默認傳 NULL, 表使用默認屬性。
參3: 子線程回調函數。pthread_create 調用成功,該函數會被自動調用起來。
參4: 參3 的參數。返回值:
????成功:0
????失敗:直接返回錯誤號!
- 線程中處理出錯, 只能使用 strerror() , 不能使用 perror() ,舉例如下:
#include <string.h>char *strerror(int errnum);
fprintf(stderr, "xxx error:%s\n", strerror(錯誤號));
- 創建子線程
// 子線程主函數
void *tfn(void *arg)
{printf("tfn : pid = %d, pthread_id = %lu\n", getpid(), pthread_self());return NULL;
}int main(int argc, char *argv[])
{pthread_t tid;// 創建子線程int ret = pthread_create(&tid, NULL, tfn, NULL);if (ret != 0)fprintf(stderr, "pthread_create err:%s\n", strerror(ret));printf("main : pid = %d, pthread_id = %lu\n", getpid(), pthread_self());sleep(1);????????// 給子線程執行時間return 0;????????// 釋放進程地址空間
}
4.3循環創建N個子線
- 每個子線程打印,自己是第幾個被創建出來。
void *tfn(void *arg)
{int i = (int)(long)arg;printf("I'm %dth thread: pid= %d, tid= %lu\n", i+1, getpid(), pthread_self());return NULL;
}int main(int argc, char *argv[])
{int i, ret;pthread_t tid;for (i = 0; i < 5; i++) {ret = pthread_create(&tid, NULL, tfn, (void *)(long)i);if (ret != 0)fprintf(stderr, "pthread_create err:%s\n", strerror(ret));}//sleep(i);usleep(10000);printf("I'm main thread: pid= %d, tid= %lu\n", getpid(), pthread_self());return 0;
}
4.4子線程傳參地址,錯誤示例
4.5線程退出
void pthread_exit(void *retval);
參:代表線程的退出值。 無退出值,NULL
- 結論:
? ? - return:返回到調用者那里。
? ? - exit(): 退出當前進程。
? ? - pthread_exit(): 退出當前線程。
4.6線程回收
// 阻塞 回收線程。
int pthread_join(pthread_t thread, void **retval);
參1:待回收的線程id
參2:傳出參數。回收的那個線程的 退出值。
?? ?進程中:main返回值:return 0、 exit(1) ?---> int。 回收進程退出值 wait(int *)
? ? 線程中:線程返回值:pthread_exit --> void *。 回收線程退出值 pthread_join(void **)
返回值:
?? ?成功:0
?? ?失敗:直接返回錯誤號!
4.6.1回收示例1
// 子線程主題函數
void *tfn(void *arg)
{sleep(5);//return (void *)74;pthread_exit((void *)"hello");
}int main(int argc, char *argv[])
{pthread_t tid;//int *retval; ? ?// 用來存儲子進程退出值char *retval; ? ?// 用來存儲子進程退出值// 創建子線程int ret = pthread_create(&tid, NULL, tfn, NULL);if (ret != 0)fprintf(stderr, "pthread_create err:%s\n", strerror(ret));printf("----------------1\n");// 回收子線程退出值ret = pthread_join(tid, (void **)&retval);if (ret != 0)fprintf(stderr, "pthread_join err:%s\n", strerror(ret));printf("child thread exit with %s\n", (char *)retval);pthread_exit((void *)0);?? ??? ?// 退出主線程
}
4.6.2回收示例2
struct thrd {int var;char str[256];
};// 子線程主題函數
void *tfn(void *arg)
{struct thrd *tval = (struct thrd *)arg;?? ??? ??? ?//malloc()tval->var = 100;strcpy(tval->str, "hello thread");pthread_exit((void *)tval);// return (void *)tval;?? ??? ??? ??? ?// 也可以
}int main(int argc, char *argv[])
{pthread_t tid;struct thrd arg, *retval;// 創建子線程int ret = pthread_create(&tid, NULL, tfn, (void *)&arg);if (ret != 0)fprintf(stderr, "pthread_create err:%s\n", strerror(ret));// 回收子線程退出值ret = pthread_join(tid, (void **)&retval);if (ret != 0)fprintf(stderr, "pthread_join err:%s\n", strerror(ret));printf("child exit with: var = %d, str= %s\n", retval->var, retval->str);// free();pthread_exit((void *)0);?? ??? ?// 退出主線程
}
4.7線程分離
- 與進程類似,線程結束時,也有 “僵尸線程” 產生。消耗系統資源。
int pthread_detach(pthread_t thread); ?// 設置線程為分離態
參:待設置為分離的線程id
- 設置為分離態的線程,在終止時,會自動清理 pcb 內核殘留。
- 對于已經分離的線程,使用 pthread_join() 不能正常回收。不能獲取線程退出值。
4.8殺死(取消)線程
int pthread_cancel(pthread_t thread);
參:待殺死的線程id
1. 被 pthread_cancel() 殺死的線程,在使用 pthread_join() 回收,得到的退出值 -1。?
2. pthread_cancel() 殺死線程必須要到達一個 “取消點” (保存點), 才能生效。否則無法殺死線程。
? ? - 應該在被cancel的線程中,調用 pthread_testcancel() 函數來添加 “取消點” (保存點)
4.8.1終止線程方法
1. return
2. pthread_exit()
3. pthread_cancel() ? 需要 “保存點”。 —— 進內核,即可得到。
線程進程控制原語比對:
| 線程控制原語? ? ? ? ?| 進程控制原語|
| -------------------------?| -----------------?|
| pthread_create()? ? ?| fork()? ? ? ? ? ?? |
| pthread_self()? ? ? ? ?| getpid()? ? ? ? ? |
| pthread_exit()? ? ? ? ?| exit()? ? ? ? ? ? ? |
| pthread_join()? ? ? ? ?| wait/waitpid()?|
| pthread_cancel()? ??| kill()? ? ? ? ? ? ? ? |
| pthread_detach()? ? |? ? ? ? ? ? ? ? ? ? ? ?|
?