Linux高并发服务器开发第十九天(线程进程)
- 软件开发
- 2025-08-23 07:57:01

目录
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 ---> 0 if (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() | |
Linux高并发服务器开发第十九天(线程进程)由讯客互联软件开发栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“Linux高并发服务器开发第十九天(线程进程)”
上一篇
安装MySQL9.1.0-winx64.msi的报错解决办法
下一篇
dify安装