第三十七章 POSIX线程(一)
POSIX线程库相关介绍
与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都有“pthread_”开头
要使用这些函数库,都需要加入头文件“<pthread.h>”, 链接的时候需要链接“-lpthread”
pthread_create
功能:
创建一个线程
原型:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
参数:
thread : 返回线程ID
attr : 设置线程属性,为NULL时表示使用默认属性
start_routine : 是个函数地址,线程启动后要执行的函数
arg : 传给线程启动函数的参数
返回值:
成功 :0
失败 : 返回错误码
pthread_exit
功能:
线程终止
原型:
void pthread_exit(void *retval);
参数:
retval :retval不要指向一个局部变量
返回值:
无返回值,跟进程一样,线程结束的时候无法返回到它的调用者(自身)
pthread_join
功能:
等待线程结束
原型:
int pthread_join(pthread_t thread, void **retval);
参数:
thread :线程ID
retval : 它指向一个指针,指向线程的返回值
返回值:
成功 : 0
失败 : 返回错误码
pthread_detach
功能:
分离线程
原型:
int pthread_detach(pthread_t thread);
参数:
thread :线程ID
返回值:
成功 : 0
失败 : 返回错误码
pthread_self
功能:
返回线程的ID
原型:
int pthread_self(void);
返回值:
总是成功,返回调用此函数的线程ID
pthread_cancle
功能:
取消一个执行中的线程
原型:
int pthread_cancel(pthread_t thread);
参数:
thread : 线程ID
返回值:
成功 : 0
失败 : 返回错误码
错误检查
- 传统的一些函数是,成功返回0, 失败返回-1,并且对全局变量errno赋值以指示错误
- pthread函数出错时不会设置全局变量errno(而大部分其它POSIX函数会这样做)。而是将错误代码通过返回值返回
- pthread同样也提供了线程内的errno变量,以支持其它使用errno的代码。对于pthread函数的错误设置,建议通过返回值判定,因为读取返回值要比读取线程内的errno变量的开销更小
进程线程对比
| 属性 | 进程 | 线程 |
|---|---|---|
| ID | pid_t | pthread_t |
| 创建 | fork | pthread_create |
| 等待 | waitpid | pthread_join |
| 僵尸 | waitpid | pthread_join、pthread_detach |
| 退出(自杀) | exit,return | pthread_exit,return |
| 他杀 | kill | pthread_cancel |
用线程实现回射客户端、服务器端
pserver.c
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>
#define ERR_EXIT(m)\
do\
{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)
void do_service(int conn)
{
char recvbuf[1024] = {0};
while(1)
{
memset(recvbuf,0,sizeof(recvbuf));
int ret = read(conn,recvbuf,sizeof(recvbuf));
if(ret == 0)
{
printf("client close\n");
break;
}
else if(ret == -1)
ERR_EXIT("read");
fputs(recvbuf,stdout);
write(conn,recvbuf,ret);
}
}
void* thread_routine(void *arg)
{
pthread_detach(pthread_self());
// 使用取地址的方式获取
//int conn = *((int*)arg);
// 使用强制转换的方式获取
// int conn = (int)arg;
// 使用取地址的方式获取,释放空间
int conn = *((int*)arg);
free(arg);
do_service(conn);
printf("exit thread %lu ...\n",pthread_self());
return NULL;
}
int main(void)
{
int listenfd;
if((listenfd = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0)
ERR_EXIT("socket");
struct sockaddr_in serv_addr;
memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(5188);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
// serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
// inet_aton("127.0.0.1",&serv_addr.sin_addr);
int on = 1;
if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR,&on,sizeof(on)) < 0)
ERR_EXIT("setsockopt");
if(bind(listenfd, (struct sockaddr*)(&serv_addr), sizeof(serv_addr)) < 0)
ERR_EXIT("bind");
if(listen(listenfd, SOMAXCONN) < 0)
ERR_EXIT("listen");
struct sockaddr_in cli_addr;
socklen_t cli_len = sizeof(cli_addr);
int conn;
while(1)
{
if((conn = accept(listenfd, (struct sockaddr *)(&cli_addr), &cli_len)) < 0)
ERR_EXIT("accept");
printf("ip : %s port : %d\n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port));
pthread_t tid;
int ret;
// 使用这种方式存在一定的问题,因为是&conn,如果accept返回后,thread_routine还没有来得及处理上一个conn,conn将被改变,导致上一次连接无法处理
// 最好不要使用指针传递,应使用值传递;
//if( (ret = pthread_create(&tid, NULL, thread_routine, (void*)&conn)) != 0 )
//使用这种方式,将int类型装换成无类型指针,int是4个字节,指针也是4个字节;但是这种做法是不可移植的,不同的操作系统,指针所占的字节数不一样
// if( (ret = pthread_create(&tid, NULL, thread_routine, (void*)conn)) != 0 )
//申请一块单独的内存放conn,取出后释放掉
int *p = malloc(sizeof(int));
*p = conn;
if( (ret = pthread_create(&tid, NULL, thread_routine, p)) != 0 )
{
fprintf(stderr, "pthread_create : %s\n", strerror(ret));
exit(EXIT_FAILURE);
}
}
return 0;
}
pclient.c
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#define ERR_EXIT(m)\
do\
{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)
void handler(int sig)
{
printf("recv a sig :%d\n",sig);
exit(EXIT_SUCCESS);
}
int main(void)
{
int sock;
// socket(PF_INET,SOCK_STREAM,0);
if((sock = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0)
ERR_EXIT("socket");
struct sockaddr_in serv_addr;
memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(5188);
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
if(connect(sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr)) < 0 )
ERR_EXIT("connect");
pid_t pid;
pid = fork();
if(pid < 0)
ERR_EXIT("fork");
if(pid == 0)
{
char recvbuf[1024] = {0};
while(1)
{
memset(recvbuf,0,sizeof(recvbuf));
int ret = read(sock,recvbuf,sizeof(recvbuf));
if (ret == -1)
ERR_EXIT("read");
else if(ret == 0)
{
printf("peer close\n");
break;
}
fputs(recvbuf,stdout);
}
close(sock);
kill(getppid(),SIGUSR1);
}
else
{
signal(SIGUSR1,handler);
char sendbuf[1024] = {0};
while(fgets(sendbuf,sizeof(sendbuf),stdin) != NULL)
{
write(sock,sendbuf,strlen(sendbuf));
memset(sendbuf,0,sizeof(sendbuf));
}
close(sock);
}
return 0;
}
第三十七章 POSIX线程(一)的更多相关文章
- “全栈2019”Java多线程第三十七章:如何让等待的线程无法被中断
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- 程序员编程艺术第三十六~三十七章、搜索智能提示suggestion,附近点搜索
第三十六~三十七章.搜索智能提示suggestion,附近地点搜索 作者:July.致谢:caopengcs.胡果果.时间:二零一三年九月七日. 题记 写博的近三年,整理了太多太多的笔试面试题,如微软 ...
- Gradle 1.12用户指南翻译——第三十七章. OSGi 插件
本文由CSDN博客万一博主翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...
- “全栈2019”Java第三十七章:类与字段
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- 第三十八章 POSIX线程(二)
线程属性 初始化与销毁属性 int pthread_attr_init(pthread_attr_t *attr); int pthread_attr_destroy(pthread_attr_t * ...
- 第三十七章 springboot+docker(手动部署)
一.下载centos镜像 docker pull hub.c.163.com/library/centos:latest docker tag containId centos:7 docker ru ...
- 三十七、Linux 线程——线程清理和控制函数、进程和线程启动方式比较、线程的状态转换
37.1 线程清理和控制函数 #include <pthread.h> void pthread_cleanup_push(void (* rtn)(void *), void *arg) ...
- 【第三十七章】 springboot+docker(手动部署)
一.下载centos镜像 docker pull hub.c.163.com/library/centos:latest docker tag containId centos:7 docker ru ...
- SpringBoot | 第三十七章:集成Jasypt实现配置项加密
前言 近期在进行项目安全方面评审时,质量管理部门有提出需要对配置文件中的敏高文件进行加密处理,避免了信息泄露问题.想想前段时间某公司上传github时,把相应的生产数据库明文密码也一并上传了,导致了相 ...
随机推荐
- 蓝松短视频SDK支持AE模板, 可做类似微商视频, 小柿饼的效果等
AE模板: 是指设计师用Adobe After Effect做好各种视频动画,比如炫酷视频,文艺/搞笑的场景,相册效果等,根据我们的指导文件导出.蓝松SDK会解析导出的文件,自动还原成AE设计时的动画 ...
- CentOS7下mongodb忘记密码后重置密码
新装mongodb后,结果一段时间没有用,密码给忘记了,只能重置密码了. 步骤如下: 1.找到mongodb的配置文件 通过ps -ef|grep mongod找到mongodb的配置文件mongod ...
- 从xxe-lab来深入学习xxe漏洞
这几天,想复习一下xxe的知识,于是把以前的一个靶场拿过来玩玩,顺便审计一下代码2333,靶场地址:https://github.com/c0ny1/xxe-lab 首先先练习的是php-xxe: 我 ...
- xtrabackup 备份+还原
1.安装下载地址:https://www.percona.com/downloads/Percona-XtraBackup-LATEST/ 2.备份# innobackupex --defaults- ...
- .NET Core开发的iNeuOS物联网平台部署树霉派,从网关到云端整体解决方案。助力2019中国.NET峰会。
2019 中国.NET 开发者峰会正式启动 目 录 1. 概述... 2 2. 树莓派硬件配置... 2 3. 软件信息... 3 4. Raspb ...
- Web安全之注入点构造
在测试过程中,经常需要自己本地构造注入点来进行SQL测试,这边分享一下,不同环境下构造SQL注入的代码. PHP+MYSQL版 <?php $con = mysql_connect(" ...
- vi/vim编辑器使用方法详解
vi编辑器是所有Unix及Linux系统下标准的编辑器,他就相当于windows系统中的记事本一样,它的强大不逊色于任何最新的文本编辑器.他是我们使用Linux系统不能缺少的工具.由于对Unix及li ...
- [Luogu2737] [USACO4.1]麦香牛块Beef McNuggets
题目描述 农夫布朗的奶牛们正在进行斗争,因为它们听说麦当劳正在考虑引进一种新产品:麦香牛块.奶牛们正在想尽一切办法让这种可怕的设想泡汤.奶牛们进行斗争的策略之一是“劣质的包装”.“看,”奶牛们说,“如 ...
- NServiceBus+Saga开发分布式应用
前言 当你在处理异步消息时,每个单独的消息处理程序都是一个单独的handler,每个handler之间互不影响.这时如果一个消息依赖另一个消息的状态呢? 这时业务逻辑怎么处理? ...
- java命令行导出、导入sql文件
@IocBean public class SqlCommandModel{ //用户名 @Inject("java:$conf.get('jdbc.username')") pr ...