写在前面:
我的Linux的学习之路非常坎坷。第一次学习Linux是在大一下的开学没多久,结果因为不会安装VMware就无疾而终了,可以说是没开始就失败了。第二次学习Linux是在大一下快放暑假(那个时候刚刚过完考试周),我没什么事做就又重拾Linux,不服输的我选择再战Linux,这一次学习还算顺利,虽然中间有些小插曲但是不影响整体学习进度, 我看着B站上的视频一点点学习Linux,基本上把Linux的基础指令学完了。学完之后我又遇到问题了,视频基本上到这就结束了,而我却不知道下一步该学什么,于是就没怎么碰Linux,结果没过多长时间我就把学的Linux指令忘的一干二净。现在是我第三次学习Linux,我决定重新开始学Linux,同时为了让自己学习的效果更好,我选择以写blog的形式逼迫自己每天把学习到的Linux知识整理下来。这也就是我写这个系列blog的原因。
waitpid
和kill
函数的参数中用到。kill -SIGKILL -进程组id
杀掉整个进程组多个进程组的集合
创建会话的注意事项
pid_t getpid(pid_t pid)
errno
pid_t setsid()
errno
daemon
进程。通常运行与操作系统后台,脱离控制终端。一般不与用户直接交互。周期性的等待某个事件发生或者周期性执行某一动作。setsid()
函数创建一个新的 session
,并成为session leader
.fork
子进程,让父进程终止。setsid()
创建新会话 chdir()
, 防止目录被卸载。022
)源代码:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<fcntl.h>
#include<sys/stat.h>
int main()
{
pid_t pid;
pid=fork();
if(pid>0)
{
printf("i am parent,my session id is %d\n",getsid(getpid()));
exit(1);
}
printf("i am child,my session id is %d\n",getsid(getpid()));
int res=setsid();
if(res<0)
{
perror("getsid error");
exit(1);
}
printf("i am child, my session id has changed to %d\n",res);
umask(0002);
close(STDIN_FILENO);
int fd=open("/dev/null",O_RDWR);
dup2(fd,STDOUT_FILENO);
dup2(fd,STDERR_FILENO);
while(1);
}
效果:
这个daemon不会受用户登录注销影响,想要终止必须使用
kill
命令
进程:有独立的 进程地址空间。有独立的pcb。 分配资源的最小单位。
线程:有独立的pcb。没有独立的进程地址空间。 最小单位的执行。
ps -Lf 线程id
——> 线程号LWP,CPU执行的最小单位
ps -Lf 进程号
——> 查看进程的线程
线程共享: ./text./data ./rodataa ./bsss heap
—> 共享【全局变量】(errno
)
线程非共享:栈空间(内核栈、用户栈)
三级映射
- 轻量级线程,存在pcb,创建线程使用的底层函数和进程一样,都是clone
- 从内核看进程和线程一样,都有各自不同的pcb,但是pcb中指向内存资源的三级页表是相同的
- 进程可以变成线程
- 线程可以看成寄存器和栈的集合
- 线程是最小的执行单位,进程是最小的资源分配的单位
pthread_self()
函数pthread_t pthread_self();
pthread_create()
函数int pthread_create(pthread_t* tid,const pthread_attr_t* attr,void* (*start_rountn)(void*),void* arg);
tid
:传出参数,表示新创建的子线程idattr
:线程属性,一般传NULL
表示默认属性。start_rountn
:子线程回调函数。创建成功,pthread
返回时,该函数自动被调用。arg
:回调函数的参数,没有的话,传NULL
.ret
这里要注意,线程里错误检查和其他地方有些不一样。线程出错是只有
errno
。
所以:fprintf(stderr,"XXXX error:%s\n",strerror(ret)
源代码:
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>
#include<stdlib.h>
void fun(void * arg)
{
int i=(int) arg;
sleep(i);
printf("i am %dth pthread,my pid is %d,my tid is %lu\n",i+1,getpid(),pthread_self());
return NULL;
}
int main()
{
pthread_t tid;
for(int i=0;i<5;i++)
{
int res=pthread_create(&tid,NULL,fun,(void*)i);
if(res!=0)
{
fprintf(stderr,"pthread create error:%s\n",strerror(res));
exit(1);
}
}
sleep(5);
printf("i am main,pid=%d,tid=%lu\n",getpid(),pthread_self());
return 0;
}
效果:
编译时会出现类型强转的警告,指针4字节转int的8字节,不过不存在精度损失,忽略就行。
pthread_exit()
NULL
exit();
:退出当前进程return
:返回到函数调用者那里pthread_exit()
:退出当前线程为了更好的说明三种方式的区别,我们还是用上面创建五个子线程的例子,只不过我们要退出第三个子线程。我们分别用这三种方式来试试。
源代码:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>
#include<errno.h>
//the first way,use exit.
void fun(void* arg)
{
int i=(int) arg;
sleep(i);
if(i==2)
exit(1);
printf("i am %dth pthread,my pid=%d,tid=%lu\n",i+1,getpid(),pthread_self());
return NULL;
}
/* the second way,use pthread_exit().
void fun(void* arg)
{
int i=(int) arg;
sleep(i);
if(2==i)
pthread_exit(1);
printf("i am %dth pthread,my pid=%d,tid=%lu\n",i+1,getpid(),pthread_self());
return NULL;
}
*/
/* the third way,use return.
void fun(void* arg)
{
int i=(int) arg;
sleep(i);
if(2==i)
return NULL;
printf("i am %dth pthread,my pid=%d,tid=%lu\n",i+1,getpid(),pthread_self());
return NULL;
}
*/
int main()
{
pthread_t tid;
for(int i=0;i<5;i++)
{
int res=pthread_create(&tid,NULL,fun,(void*)i);
if(res!=0)
{
fprintf(stderr,"pthread_create error %s\n",strerror(errno));
exit(1);
}
}
sleep(5);
printf("i am main,my pid is %d,tid=%lu\n",getpid(),pthread_self());
return 0;
}
效果:
源代码:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>
#include<errno.h>
/* //the first way,use exit.
void fun(void* arg)
{
int i=(int) arg;
sleep(i);
if(i==2)
exit(1);
printf("i am %dth pthread,my pid=%d,tid=%lu\n",i+1,getpid(),pthread_self());
return NULL;
}
*/
//the second way,use pthread_exit().
void fun(void* arg)
{
int i=(int) arg;
sleep(i);
if(2==i)
pthread_exit(1);
printf("i am %dth pthread,my pid=%d,tid=%lu\n",i+1,getpid(),pthread_self());
return NULL;
}
/* the third way,use return.
void fun(void* arg)
{
int i=(int) arg;
sleep(i);
if(2==i)
return NULL;
printf("i am %dth pthread,my pid=%d,tid=%lu\n",i+1,getpid(),pthread_self());
return NULL;
}
*/
int main()
{
pthread_t tid;
for(int i=0;i<5;i++)
{
int res=pthread_create(&tid,NULL,fun,(void*)i);
if(res!=0)
{
fprintf(stderr,"pthread_create error %s\n",strerror(errno));
exit(1);
}
}
sleep(5);
printf("i am main,my pid is %d,tid=%lu\n",getpid(),pthread_self());
return 0;
}
效果:
源代码:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>
#include<errno.h>
/* //the first way,use exit.
void fun(void* arg)
{
int i=(int) arg;
sleep(i);
if(i==2)
exit(1);
printf("i am %dth pthread,my pid=%d,tid=%lu\n",i+1,getpid(),pthread_self());
return NULL;
}
*/
/* //the second way,use pthread_exit().
void fun(void* arg)
{
int i=(int) arg;
sleep(i);
if(2==i)
pthread_exit(1);
printf("i am %dth pthread,my pid=%d,tid=%lu\n",i+1,getpid(),pthread_self());
return NULL;
}
*/
//the third way,use return.
void fun(void* arg)
{
int i=(int) arg;
sleep(i);
if(2==i)
return NULL;
printf("i am %dth pthread,my pid=%d,tid=%lu\n",i+1,getpid(),pthread_self());
return NULL;
}
int main()
{
pthread_t tid;
for(int i=0;i<5;i++)
{
int res=pthread_create(&tid,NULL,fun,(void*)i);
if(res!=0)
{
fprintf(stderr,"pthread_create error %s\n",strerror(errno));
exit(1);
}
}
sleep(5);
printf("i am main,my pid is %d,tid=%lu\n",getpid(),pthread_self());
return 0;
}
效果:
int pthread_join(pthread_t tid,void** retval);
tid
:待回收的子线程idretval
:传出参数,回收线程的退出值。源代码:
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>
#include<stdlib.h>
struct stu
{
int age;
char mesg[100];
};
void* fun(void* arg)
{
struct stu* t;
t=malloc(sizeof t);
t->age=19;
strcpy(t->mesg,"hellow linux\n");
return (void*)t;
}
int main()
{
pthread_t tid;
pthread_create(&tid,NULL,fun,NULL);
struct stu *temp;
pthread_join(tid,(void**)&temp);
printf("child pthread exit with age=%d,mesg=%s\n",temp->age,temp->mesg);
return 0;
}
效果:
我们利用pthread_join
循环回收创建的5子线程
源代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<errno.h>
void* fun(void* arg)
{
int i=(int) arg;
sleep(i);
printf("i am %dth pthread\n",i+1);
return NULL;
}
int main()
{
pthread_t tid[5];
for(int i=0;i<5;i++)
{
int ret=pthread_create(&tid[i],NULL,fun,(void*) i);
if(0!=ret)
{
fprintf(stderr,"pthread_create error:%s\n",strerror(ret));
exit(0);
}
}
for(int i=0;i<5;i++)
{
pthread_join(tid[i],NULL);
printf("i am main pthread,i join %dth pthread\n",i+1);
}
return 0;
}
效果:
int pthread_cancel(pthread_t tid);
tid
待杀死的线程源代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
void* fun(void* arg)
{
while(1)
{
printf("i am child pthread\n"); //this is a normal case
sleep(1);
//pthread_testcancel(); //this is to set cancel point
}
return (void*) 666;
}
int main()
{
pthread_t tid;
void* res=NULL;
pthread_create(&tid,NULL,fun,NULL);
sleep(3);
pthread_cancel(tid);
pthread_join(tid,&res);
printf("child pthread has been killed me,and it exit with %d\n",(int) res);
return 0;
}
效果:
这里要注意一点,
pthread_cancel
工作的必要条件是进入内核,如果fun
真的离谱到没有进入内核pthread_cancel
不能杀死线程,此时需要手动设置取消点,就是pthread_testcancel()
还有一点,如果子线程被pthread_cancel()
杀死,无论函数里写的是返回是什么,最后都是返回-1
.
int pthread_detach(pthread_t tid);
tid
,待分离的线程id源代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
void* fun(void* arg)
{
printf("i am child pthread\n");
return NULL;
}
int main()
{
pthread_t tid;
pthread_create(&tid,NULL,fun,NULL);
pthread_detach(tid);
sleep(1);
int res=pthread_join(tid,NULL);
if(res!=0)
fprintf(stderr,"pthread join error:%s\n",strerror(res));
return 0;
}
效果:
pthread_attr_t attr;
pthread_attr_init(&attr)
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
pthread_create(&tid,&attr,fun,NULL)
进程 | 线程 |
---|---|
fork() | pthread_create() |
wait()/waitpid() | pthread_join() |
kill | pthread_cancel() |
getpid | pthread_self() |
exit | pthread_exit()/return |
pthread_detach() |
pthread_exit
pthread_join
thread_detach
pthread_create
指定分离属性join
线程可能在join
函数返回前就释放自己的所有内存资源,所以不应当返回被回收线程栈中的值malloc
和mmap
申请的内存可以被其他线程释放fork
,除非马exec
,子线程中只有调用fork
的线程存在,其他线程在子进程中均pthread_exit
个人亲身经验:我们学习的一系列Linux命令,一定要自己亲手去敲。不要只是看别人敲代码,不要只是停留在眼睛看,脑袋以为自己懂了,等你实际上手去敲会发现许许多多的这样那样的问题。毕竟“实践出真知”。
如果你觉得我写的题解还不错的,请各位王子公主移步到我的其他题解看看
- 数据结构与算法部分(还在更新中):
- C++ STL总结 - 基于算法竞赛(强力推荐)
- 动态规划——01背包问题
- 动态规划——完全背包问题
- 动态规划——多重背包问题
- 动态规划——分组背包问题
- 动态规划——最长上升子序列(LIS)
- 二叉树的中序遍历(三种方法)
- 最长回文子串
- 最短路算法——Dijkstra(C++实现)
- 最短路算法———Bellman_Ford算法(C++实现)
- 最短路算法———SPFA算法(C++实现)
- 最小生成树算法———prim算法(C++实现)
- 最小生成树算法———Kruskal算法(C++实现)
- 染色法判断二分图(C++实现)
- Linux部分(还在更新中):
“种一颗树最好的是十年前,其次就是现在”
所以,
“让我们一起努力吧,去奔赴更高更远的山海”
如果有错误?,欢迎指正哟😋
🎉如果觉得收获满满,可以动动小手,点点赞👍,支持一下哟🎉