目录
进程组和会话
①进程组:一个或多个进程的集合。
当父进程,创建子进程的时候,默认子进程与父进程属于同一进程组。进程组ID第一个进程ID(组长进程)。所以,组长进程标识:其进程组ID其进程ID
可以使用kill -SIGKILL -进程组ID(负的)来将整个进程组内的进程全部杀死。
组长进程可以创建一个进程组,创建该进程组中的进程,然后终止。只要进程组中有一个进程存在,进程组就存在,与组长进程是否终止无关。
进程组生存期:进程组创建到最后一个进程离开(终止或转移到另一个进程组)。
一个进程可以为自己或子进程设置进程组ID②会话:多个进程组的集合。
创建一个会话需要注意以下6点注意事项:
1.创建会话的调用进程不能是进程组组长,该进程变成新会话首进程(session header)
2.该进程成为一个新进程组的组长进程。
3.需有root权限 (ubuntu不需要)
4.新会话丢弃原有的控制终端,该会话没有控制终端 无法和用户完成交互
5.该调用进程是组长进程,则出错返回
6.建立新会话时,先调用fork, 父进程终止,子进程调用setsid
setsid、getsid函数
getsid - get session ID
#include <unistd.h>
pid_t getsid(pid_t pid);
On success, a session ID is returned. On error, (pid_t) -1 will be returned, and errno is set appropriatelysetsid - creates a session and sets the process group ID
#include <unistd.h>
pid_t setsid(void);
On success, the (new) session ID of the calling process is returned. On error, (pid_t) -1 is returned, and errno is set to indicate the error.创建一个会话,并以自己的 ID 设置进程组 ID,同时也是新会话的 ID.成功返回调用进程的会话 ID,失败返回-1,设置 error
创建会话程序
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <pthread.h>
#include <sys/mman.h>
#include <errno.h>
void sys_error(const char* string1){
perror(string1);
exit(1);
}
int main(int argc,char* argv[]){
pid_t pid;
pid = fork();
if(pid < 0 ){
sys_error("fork error");
}
if(pid == 0 ){
//子进程
printf("child process PID = %d\n",getpid());//获取子进程id
printf("Group ID of child = %d\n",getpgid(0));//获取组id
printf("Session ID of child = %d\n",getsid(0));//获取子进程的会话id
sleep(10);
setsid();//设置为会话id,成为组长,进程组id就是会话id等于子进程id
printf("-------------------\n");
printf("child process PID = %d\n",getpid());
printf("Group ID of child = %d\n",getpgid(0));
printf("Session ID of child = %d\n",getsid(0));//获取子进程的会话id
sleep(20);
exit(0);
}
return 0;
}
/*运行结果
haitu@ubuntu:/opt/modbus_test/linux_test/signal/signal2$ ./creat_sid
child process PID = 16466
Group ID of child = 16465
Session ID of child = 15749
haitu@ubuntu:/opt/modbus_test/linux_test/signal/signal2$ -------------------
child process PID = 16466
Group ID of child = 16466
Session ID of child = 16466
haitu@ubuntu:/opt/modbus_test/linux_test/signal/signal2$
*/
守护进程
Linux Daemon(守护进程)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。它不需要用户输入就能运行而且提供某种服务,不是对整个系统就是对某个用户程序提供服务。Linux系统的大多数服务器就是通过守护进程实现的。一般采用以d结尾的名字。例如系统日志进程syslogd、 web服务器httpd和数据库服务器mysqld等。
- 创建守护进程的一般步骤
守护进程创建步骤:
- fork 子进程,让父进程终止。
- 子进程调用 setsid() 创建新会话
- 通常根据需要,改变工作目录位置 chdir(), 防止目录被卸载。
- 通常根据需要,重设 umask 文件权限掩码,影响新文件的创建权限。 022 – 755 0345 -
– 432 r—wx-w- 422- 通常根据需要,关闭/重定向 文件描述符 (fd==0/1/2重定向给/dev/null,空洞)
- 守护进程 业务逻辑。while()
例子程序dameon.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <pthread.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
void sys_error(const char* string1){
perror(string1);
exit(1);
}
int main(int argc,char* argv[]){
pid_t pid;
int ret,fd;
pid = fork();
if(pid == 0){//子进程
pid = setsid();//设置为会话id
if (pid == -1){
sys_error("setsid error");
exit(0);
}
ret = chdir("/opt");//改变工作目录位置
if(ret == -1){
sys_error("chdir error");
}
umask(0022);//设置文件访问权限掩码umask
//关闭或重定向文件描述符0,1,2
close(STDIN_FILENO);//关闭文件描述符0
fd = open("/dev/null",O_RDWR);//打开空洞设备。用于重定向。
if(fd == -1){
sys_error("open error");
}
//进行重定向
dup2(fd,STDOUT_FILENO);//将文件描述符标准输出流给重定向
dup2(fd,STDERR_FILENO);//将标准错误流重定向
//守护进程执行内容,业务逻辑
while(1){
//执行的内容
}
}else if(pid > 0){//父进程
exit(0);//结束父进程
}
return 0;
}
运行出来后,在
ps ajx
中就可以看到程序运行,并且用户登入注销并不会影响该进程的执行。要想关闭该守护进程就必须要使用kill
命令。
线程的概念
LWP:light weight process 轻量级的进程,本质仍是进程(在Linux环境下)
进程:独立地址空间,拥有PCB
线程:有独立的PCB,但没有独立的地址空间(共享)
区别:在于是否共享地址空间。 独居(进程);合租(线程)。
Linux下:
线程:最小的执行单位
进程:最小分配资源单位,可看成是只有一个线程的进程。
- 实现原理
轻量级进程(light-weight process),也有PCB,创建线程使用的底层函数和进程一样,都是clone
从内核里看进程和线程是一样的,都有各自不同的PCB,但是PCB中指向内存资源的三级页表是相同的
进程可以蜕变成线程(进程创建了线程,那么该进程也就会沦为线程。)
线程可看做寄存器和栈的集合
在linux下,线程最是小的执行单位;进程是最小的分配资源单位
察看LWP号:ps –Lf pid 查看指定线程的lwp号,也就是线程号,注意和线程id区分开来。
三级页表:(虚拟地址的映射)
pcb里面的页目录首地址—> 页目录 —> 页表 —> 物理页 —> 内存单元 —> 物理地址
- 线程资源
线程共享资源
1.文件描述符表
2.每种信号的处理方式
3.当前工作目录
4.用户ID和组ID
**5.内存地址空间 (.text/.data/.bss/heap/共享库) 共享全局变量
线程非共享资源
1.线程id
2.处理器现场和栈指针(内核栈)
3.独立的栈空间(用户空间栈)
4.errno变量
5.信号屏蔽字 (可以用于signal指定线程使用,但一般不使用)
6.调度优先级
线程控制原语
pthread_self函数
pthread_create函数
pthread_exit函数
pthread_join函数
pthread_detach函数
pthread_cancel函数
pthread_self
获取线程ID。其作用对应进程中 getpid() 函数。
pthread_t pthread_self(void); 返回值:成功:0; 失败:无!
线程ID:pthread_t类型,本质:在Linux下为无符号整数(%lu),其他系统中可能是结构体实现
线程ID是进程内部,识别标志。(两个进程间,线程ID允许相同)
注意:不应使用全局变量 pthread_t tid,在子线程中通过pthread_create传出参数来获取线程ID,而应使用pthread_self。
pthread_create
创建一个新线程。 其作用,对应进程中fork() 函数。
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
返回值:成功:0; 失败:错误号 -----Linux环境下,所有线程特点,失败均直接返回错误号。
参数:
pthread_t:当前Linux中可理解为:typedef unsigned long int pthread_t;
参数1:传出参数,保存系统为我们分配好的线程ID
参数2:通常传NULL,表示使用线程默认属性。若想使用具体属性也可以修改该参数。
参数3:函数指针,指向线程主函数(线程体),该函数运行结束,则线程结束。
参数4:线程主函数执行期间所使用的参数。
创建子线程
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <pthread.h>
#include <sys/mman.h>
#include <errno.h>
void sys_error(const char* string1){
perror(string1);
exit(1);
}
void* function1(void* arg){//线程执行函数
printf("1234--%lu,%d\n",pthread_self(),*(int *)arg);//打印新线程得tid和传输进来得数据
return NULL;
}
int main(int argc,char* argv[]){
pthread_t tid;
int a = 909090;//用于传输得数据
int ret;//用于判断pthread是否创建成功
int* asd = &a;
ret = pthread_create(&tid,NULL,function1,(void*)asd);//asd为传递进去得数据
if(ret != 0){
sys_error("pthread error");
}
printf("tid1 = %lu\n",tid);//这个是新添加得线程id
tid = pthread_self();
printf("tid2 = %lu\n",tid);//这个是主进程变为线程后得线程id
sleep(1);//防止主函数执行完,没有执行线程
return 0;
}
/*执行结果
haitu@ubuntu:/opt/modbus_test/linux_test/session/pthread_test$ ./pthread
tid1 = 140577440052992
tid2 = 140577448384256
1234--140577440052992,909090
*/
循环创建多个子线程
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <pthread.h>
#include <sys/mman.h>
#include <errno.h>
void sys_error(const char* string1){
perror(string1);
exit(1);
}
void* function1(void* arg){
printf("1234---%lu,%d\n",pthread_self(),(int)arg);
return NULL;
}
int main(int argc,char* argv[]){
pthread_t tid;
for(int i = 0;i<5;i++){
int ret = pthread_create(&tid,NULL,function1,(void *)i);
if(ret != 0){
sys_error("pthread error");
}
}
printf("tid1 = %lu\n",tid);
tid = pthread_self();
printf("tid2 = %lu\n",tid);
sleep(2);
return 0;
}
/*运行结果
haitu@ubuntu:/opt/modbus_test/linux_test/session/pthread_test$ ./pthread2
1234---140030760503040,0
1234---140030743717632,2
1234---140030752110336,1
1234---140030735324928,3
tid1 = 140030726932224
tid2 = 140030768834304
1234---140030726932224,4
*/
在
pthread
线程中,全局变量是共享的。在线程中全局变量更改,之后在主线程中该变量也会随之改变的。
pthread_exit
pthread_exit - terminate calling thread
对应进程的exit函数
void pthread_exit(void *retval);
retval
:退出值。 无退出值时,NULL
exit()
; 退出当前进程。
return
: 返回到调用者那里去。并不会退出线程。
pthread_exit()
: 退出当前线程。
/*对上面的循环创建多个子线程进行更改,那么就可以实现不使用sleep,主线程死亡后退出,不会影响其他的线程*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <pthread.h>
#include <sys/mman.h>
#include <errno.h>
void sys_error(const char* string1){
perror(string1);
exit(1);
}
void* tfn(void* arg){
sleep((int)arg);
//if ((int)arg == 2){
// pthread_exit(NULL);//退出第二个线程
//}
printf("---i am %dth thread--pid == %d,tid == %lu\n",(int)arg,getpid(),pthread_self());
return NULL;
}
int main(int argc,char* argv[]){
int ret;//用于判断是否创建了线程
int i;//用于循环创建线程
pthread_t tid;//用于create线程的传出参数
for(i = 1;i<5;i++){
ret = pthread_create(&tid,NULL,tfn,(void*)(i));
if(ret != 0){
sys_error("pthread_create error");
}
}
//sleep(i);
pthread_exit(NULL);
//return 0;
}
/*运行结果
haitu@ubuntu:/opt/modbus_test/linux_test/session/pthread_test$ ./pthrd_exit
---i am 1th thread--pid == 34065,tid == 140209677682432
---i am 2th thread--pid == 34065,tid == 140209669289728
---i am 3th thread--pid == 34065,tid == 140209660897024
---i am 4th thread--pid == 34065,tid == 140209652504320
*/
pthread_join
int pthread_join(pthread_t thread, void **retval);
阻塞 回收线程。相当于进程的wait
和waitpid
函数。作用:
pthread_join - join with a terminated thread
thread
: 待回收的线程 id
retval
:传出参数。 回收的那个线程的退出值。线程异常借助,值为 -1。
返回值:成功:0
失败:
errno
回收一个子线程
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <pthread.h>
#include <sys/mman.h>
#include <errno.h>
void sys_error(const char* string1){
perror(string1);
exit(1);
}
void* tfn(void* arg){
sleep((int)arg);
printf("---i am %dth thread--pid == %d,tid == %lu\n",(int)arg,getpid(),pthread_self());
return (void*)(1);//返回参数
}
int main(int argc,char* argv[]){
int ret;//用于判断是否创建了线程
int i = 0;//用于循环创建线程
pthread_t tid;//用于create线程的传出参数
int* a;
ret = pthread_create(&tid,NULL,tfn,(void*)(i));
if(ret != 0){
sys_error("pthread_create error");
}
pthread_join(tid,(void**)&a);
//sleep(i);
printf("a==%d\n",a);//打印回收线程的返回参数
pthread_exit(NULL);
}
/*运行结果
---i am 0th thread--pid == 37824,tid == 140075081254656
a==1
*/
回收多个子线程
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <pthread.h>
#include <sys/mman.h>
#include <errno.h>
struct trd {
int val;
char str[256];
};
void sys_error(const char* string1){
perror(string1);
exit(1);
}
void* tfn(void* arg){
struct trd *tval;
tval = malloc(sizeof(tval));
tval->val = 1200;
tval->val += (int *)arg;
strcpy(tval->str, "902901");
return (void*)tval;
}
int main(int argc,char* argv[]){
int ret;//用于判断是否创建了线程
int i;//用于循环创建线程
//pthread_t tid;//用于create线程的传出参数
pthread_t tid[4];
struct trd *retval;
for(i = 1;i<5;i++){
ret = pthread_create(&tid[i],NULL,tfn,(void*)i);
if(ret != 0){
sys_error("pthread_create error");
}
}
//sleep(i);
for (int j = 1;j<5;j++){
pthread_join(tid[j],(void**)&retval);
printf("1--%d- 2 -%s---\n",retval->val,retval->str);
}
pthread_exit(NULL);
}
/*运行结果
ubuntu@ubuntu:~/LearnCPP/pthread_test$ ./pthrd_join2
1--4801- 2 -902901---
1--4802- 2 -902901---
1--4803- 2 -902901---
1--4804- 2 -902901---
*/
pthread_cancel函数
int pthread_cancel(pthread_t thread);
杀死一个线程。 需要到达取消点(保存点)
thread: 待杀死的线程 id
返回值:成功:0
失败:errno
pthread_cancel 工作的必要条件是进入内核,如果线程执行函数真的奇葩到没有进入内核,
则 pthread_cancel 不能杀死线程,子线程没有到达取消点, 那么 pthread_cancel 无效。此时需要手动设置取消点,就是 pthread_testcancel()
成功被 pthread_cancel() 杀死的线程,返回 -1.使用 pthead_join 回收。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <pthread.h>
#include <sys/mman.h>
#include <errno.h>
void sys_error(const char* string1){
perror(string1);
exit(1);
}
void* tfn(void* arg){
while(1){
sleep((int)arg);
printf("---i am %dth thread--pid == %d,tid == %lu\n",(int)arg,getpid(),pthread_self()); //printf有调用内核
//pthread_testcancel();//取消点,使得线程进入内核,是否收到了终止
//pthread_cancel 工作的必要条件是进入内核,如果 tfn 真的奇葩到没有进入内核,则 pthread_cancel 不能杀死线程,此时需要手动设置取消点
}
return (void*)(1);
//pthread_exit((void *)222)
}
int main(int argc,char* argv[]){
int ret;//用于判断是否创建了线程
int i = 1;
pthread_t tid;//用于create线程的传出参数
int* a;
ret = pthread_create(&tid,NULL,tfn,(void*)(i));
if(ret != 0){
sys_error("pthread_create error");
}
sleep(5);//延时5s
ret = pthread_cancel(tid);//取消掉该线程
if(ret != 0){
sys_error("pthread_cancel error");
}
pthread_join(tid,(void**)&a);
//sleep(i);
printf("a==%d\n",a);
while(1);
pthread_exit(NULL);
}
/*执行结果
ubuntu@ubuntu:~/LearnCPP/pthread_test$ ./pthrd_cancel
---i am 1th thread--pid == 3782,tid == 140564196775680
---i am 1th thread--pid == 3782,tid == 140564196775680
---i am 1th thread--pid == 3782,tid == 140564196775680
---i am 1th thread--pid == 3782,tid == 140564196775680
a==-1
^C
*/
//回收后显示的数值是-1,表示的是线程非正常终止。
pthread_detach
pthread_detach - detach a thread
#include <pthread.h>
int pthread_detach(pthread_t thread);
thread:要分离的线程
On success, pthread_detach() returns 0; on error, it returns an error number.
对线程分离后,就不用手动的进行线程回收。它的资源会被系统自动的回收
而不再需要在其它线程中对其进行 pthread_join() 操作。
线程分离的作用:当主线程与新线程无关时,主线程无需等待新线程的结束。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <pthread.h>
#include <sys/mman.h>
#include <errno.h>
void sys_error(const char* string1){
perror(string1);
exit(1);
}
void* tfn(void* arg){
sleep((int)arg);
printf("---i am %dth thread--pid == %d,tid == %lu\n",(int)arg,getpid(),pthread_self());
return (void*)(1);
}
int main(int argc,char* argv[]){
int ret;//用于判断是否创建了线程
int i = 0;//用于循环创建线程
pthread_t tid;//用于create线程的传出参数
int* a;
ret = pthread_create(&tid,NULL,tfn,(void*)(i));
if(ret != 0){
sys_error("pthread_create error");
}
ret = pthread_detach(tid);//线程分离
if(ret != 0){
sys_error("pthread_detach error");
//fprintf(stderr,"pthread_detach error:%s\n",strerror(ret));
exit(1);
}
sleep(1);
//线程分离后本身是不用在进行线程回收
//这里只是进行错误分析
ret = pthread_join(tid,(void**)&a);
if(ret != 0){
sys_error("pthread_join error");
//fprintf(stderr,"pthread_join error: %s\n",strerror(ret));
exit(1);
}
//sleep(i);
printf("a==%d\n",a);
pthread_exit(NULL);
}
/*执行结果
//这个执行后没有显示错误的原因
ubuntu@ubuntu:~/LearnCPP/pthread_test/detach$ ./detach
---i am 0th thread--pid == 4296,tid == 140640814401280
pthread_join error: Success
//将sys_error("pthread_join error");注释掉,打开
//fprintf(stderr,"pthread_join %s\n",strerror(ret));执行后就会出现错误原因
ubuntu@ubuntu:~/LearnCPP/pthread_test/detach$ ./detach
---i am 0th thread--pid == 4348,tid == 140688971962112
pthread_join error: Invalid argument
//无效参数:tid,因为被线程分离了
*/
//所以以后所有线程中的检查错误的语句使用
//fprintf(stderr,"什么错误:%s\n",strerror(ret));
进程和线程控制原语对比
线程控制原语 | 进程控制原语 |
---|---|
pthread_create() | fork() |
pthread_self() | getpid() |
pthread_exit() | exit()/return |
pthread_join() | wait()/waitpid() |
pthread_cancel() | kill() |
pthread_detach() |
线程属性
线程属性初始化
#include <pthread.h>
typedef struct
{
int etachstate; //线程的分离状态
int schedpolicy; //线程调度策略(线程优先级)
structsched_param schedparam; //线程的调度参数
int inheritsched; //线程的继承性
int scope; //线程的作用域
size_t guardsize; //线程栈末尾的警戒缓冲区大小(栈溢出时可以多溢出的大小)
int stackaddr_set; //线程的栈设置
void* stackaddr; //线程栈的位置
size_t stacksize; //线程栈的大小
}pthread_attr_t;
//早期的线程属性定义,与目前线程属性在内核之间定义的主要元素差别不大。
int pthread_attr_init(pthread_attr_t *attr); //初始化线程属性
return: On success, these functions return 0; on error, they return a nonzero error number.
int pthread_attr_destroy(pthread_attr_t *attr); //销毁线程属性所占用的资源
return: On success, these functions return 0; on error, they return a nonzero error number.
设置线程分离状态属性
功能:pthread_attr_setdetachstate, pthread_attr_getdetachstate - set/get detach state attribute in thread attributes object
#include <pthread.h>
//线程分离状态函数
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
pthread_attr_t *attr:被已初始化的线程属性
int detachstate:可选为
PTHREAD_CREATE_DETACHED(分离线程)
PTHREAD _CREATE_JOINABLE(非分离线程)
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <pthread.h>
#include <sys/mman.h>
#include <errno.h>
void* tfn(void* arg){
sleep((int)arg);
printf("---i am %dth thread--pid == %d,tid == %lu\n",(int)arg,getpid(),pthread_self());
return (void*)(1);
}
int main(int argc,char* argv[]){
int ret;//用于判断是否创建了线程
int i = 1;//用于循环创建线程
pthread_t tid;//用于create线程的传出参数
pthread_attr_t attr;
int* a;
ret = pthread_attr_init(&attr);//初始化attr结构体
if(ret != 0){
fprintf(stderr,"pthread_attr_init %s\n",strerror(ret));
exit(1);
}
ret = pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);//设置分离属性
if(ret != 0){
fprintf(stderr,"pthread_attr_setdetachstate %s\n",strerror(ret));
exit(1);
}
ret = pthread_create(&tid,&attr,tfn,(void*)(i));//创建线程分离的子线程
if(ret != 0){
fprintf(stderr,"pthread_attr_setdetachstate %s\n",strerror(ret));
}
//ret = pthread_detach(tid);//线程分离
ret = pthread_attr_destroy(&attr);//销毁attr属性结构体
if(ret != 0){
fprintf(stderr,"pthread_attr_destroy error:%s\n",strerror(ret));
exit(1);
}
sleep(2);
ret = pthread_join(tid,(void**)&a);
if(ret != 0){
fprintf(stderr,"pthread_join %s\n",strerror(ret));
exit(1);
}
printf("a==%d\n",a);
pthread_exit(NULL);
}
/*执行结果
ubuntu@ubuntu:~/LearnCPP/pthread_test/detach$ ./detach2
---i am 1th thread--pid == 4704,tid == 140404107732736
pthread_join Invalid argument
//说明完成了分离,不用在使用join回收
*/
/*
这里要注意的一点是,如果设置一个线程为分离线程,而这个线程运行又非常快,它很可能在pthread_create函数返回之前就终止了,它终止以后就可能将线程号和系统资源移交给其他的线程使用,这样调用pthread_create的线程就得到了错误的线程号。要避免这种情况可以采取一定的同步措施,最简单的方法之一是可以在被创建的线程里调用pthread_cond_timedwait函数,让这个线程等待一会儿,留出足够的时间让函数pthread_create返回。设置一段等待时间,是在多线程编程里常用的方法。但是注意不要使用诸如wait()之类的函数,它们是使整个进程睡眠,并不能解决线程同步的问题。
*/
设置线程的栈大小和栈地址
#include <pthread.h>
int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize);
int pthread_attr_getstack(pthread_attr_t *attr, void **stackaddr, size_t *stacksize);
attr 指向一个线程属性的指针
stackaddr 返回获取的栈地址
stacksize 返回获取的栈大小
返回值:若是成功返回0,否则返回错误的编号
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <pthread.h>
#include <sys/mman.h>
#include <errno.h>
#define SIZE 0x10000
void* tfn(void* arg){
sleep((int)arg);
printf("---i am %dth thread--pid == %d,tid == %lu\n",(int)arg,getpid(),pthread_self());
return (void*)(1);
}
int main(int argc,char* argv[]){
int ret;//用于判断是否创建了线程
int i = 1;//用于循环创建线程
pthread_t tid;//用于create线程的传出参数
size_t stacksize;
void *stackaddr;
pthread_attr_t attr;
int* a;
ret = pthread_attr_init(&attr);//初始化attr结构体
if(ret != 0){
fprintf(stderr,"pthread_attr_init %s\n",strerror(ret));
exit(1);
}
ret = pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);//设置分离属性
if(ret != 0){
fprintf(stderr,"pthread_attr_setdetachstate %s\n",strerror(ret));
exit(1);
}
/* 在堆上申请内存,指定线程栈的起始地址和大小*/
stackaddr = malloc(SIZE);//为栈分配空间、返回首地址
if (stackaddr == NULL)
{
perror("malloc");
exit(1);
}
/* 设置栈大小和地址 大小为上面的宏,位置为上面设置的*/
stacksize = SIZE;//设置占空间大小
pthread_attr_setstack(&attr, stackaddr, stacksize); //线程属性 栈地址 栈大小
ret = pthread_create(&tid,&attr,tfn,(void*)(i));//创建线程分离的子线程
if(ret != 0){
fprintf(stderr,"pthread_attr_setdetachstate %s\n",strerror(ret));
}
pthread_attr_getstack(&attr, &stackaddr, &stacksize);//获取栈信息
printf("stackadd=%p\n", stackaddr); //打印栈的地址
printf("stacksize=%x\n", (int)stacksize); //打印栈的大小
//ret = pthread_detach(tid);//线程分离
ret = pthread_attr_destroy(&attr);//销毁attr属性结构体
if(ret != 0){
fprintf(stderr,"pthread_attr_destroy error:%s\n",strerror(ret));
exit(1);
}
sleep(2);
pthread_exit(NULL);
}
/*执行结果
ubuntu@ubuntu:~/LearnCPP/pthread_test/detach$ ./stackTest
stackadd=0x556d1ee0e2a0
stacksize=10000
---i am 1th thread--pid == 158914,tid == 93927157913984
*/
线程使用注意事项
主线程退出其他线程不退出,主线程应调用pthread_exit
避免僵尸线程
pthread_join
pthread_detach
pthread_create指定分离属性
被join线程可能在join函数返回前就释放完自己的所有内存资源,所以不应当返回被回收线程栈中的值;
malloc和mmap申请的内存可以被其他线程释放
应避免在多线程模型中调用fork除非,马上exec,子进程中只有调用fork的线程存在,其他线程在子进程中均pthread_exit
信号的复杂语义很难和多线程共存,应避免在多线程引入信号机制