父子进程相关知识:
1.子进程结束时,系统 会立即自动刷新行缓存
2.手动结束进程:
exit()
exit(int status):结束当前调用的进程,自动刷新缓存
标准库函数
头文件:#include <stdlib.h>
_exit()
_exit(int status) : 结束当前调用的进程,不刷新缓存
系统调用函数
头文件:#include <unistd.h>
3.僵尸进程
子进程先于父进程结束,父进程没有回收子进程的剩余资源
4.如何避免僵尸进程?
1、子进程结束,通知父进程回收资源,使用wait或者waitpid函数
wait()
pid_t wait(int *status)
功能:阻塞父进程,等待任一子进程结束,回收该子进程资源
在父进程中调用
如果,该进程没有子进程或者子进程已经结束,则函数立即返回
头文件:
#include <sys/types.h>
#include <sys/wait.h>
参数:
status : 整型指针,用来保存子进程退出时的状态
如果status设置为NULL,不保存子进程退出时的状态
必须通过Linux的宏来检测 退出状态
返回值:
成功:退出的子进程的pid
失败:-1,并设置错误信息
5.fork创建子进程时:
if结构 将会决定 子进程拷贝父进程的 哪些内容:
if()
{//出错}
else if()
{//子进程}
else
{//父进程}
注意:else中的父进程区域内容不会被拷贝到子进程中
if()
{//出错}
else if()
{//子进程}
//父进程区域(没有else)
注意:父进程的内容,将会被子进程 拷贝所有内容(父进程号除外)
waitpid()
pid_t waitpid(pid_t pid,int *status,int options)
waitpid(-1,NULL,0) == wait(NULL)
功能:
回收指定 pid的子进程 资源
参数:
pid:要回收的子进程的pid号;
status:保存指定pid的子进程退出的状态
option:WNOHANG:非阻塞模式;子进程未结束,立即返回0值;当指定的子进程PID结束立即回收资源
0:阻塞模式,等同于wait
返回值:
成功:返回退出的子进程PID号;
失败:返回-1;
5.孤儿进程
父进程先于子进程结束,子进程被init.d托管,init进程的PID号为1
缺点:
孤儿进程无法受当前终端控制。除非使用kill命令杀死或者系统结束,否则一直存在。
应用:用来制作守护进程
6.创建守护进程
ps -ef 全部进程
ps -axj 查看守护进程
ps -aux 全部进程
1、先创建子进程,父进程结束,形成孤儿进程
2、创建新的会话期,使进程完全独立,摆脱当前进程的控制
setsid函数用于创建一个新的会话期,kill -9杀不死
3、改变进程的工作目录为根目录:chdir
“/”或者“/tmp”作为守护
4、重设文件权限掩码:umask(0)
目的:增加守护进程的灵活性
5、关闭其他不相关的文件描述符。
运行之后,可以用kill -9 强制杀死守护进程,因为kill -9是在内核层面上杀死,而守护进程开辟是在用户层面的进程可以被其他杀死。
hqyj@ubuntu:~/5.15$ cat guer1_demo.c
/*===============================================
* 文件名称:fork_demo.c
* 创 建 者:memories
* 创建日期:2023年05月15日
* 描 述:
================================================*/
创建守护进程
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
void process();
int main(int argc, char *argv[])
{
pid_t pid=fork();
if(pid==-1)
{
perror("fork");
return -1;
}
else if(pid==0)
{
//2.创建新的会话期
if(setsid()<0)
{
perror("setsid");
return -1;
}
//3.设置进程的工作目录为"/tmp"
if(chdir("../5.15")<0)
{
perror("chidr");
return -1;
}
//4.重设文件权限掩码
umask(0);
//5.关闭不相关的文件描述符(文件描述符来自于父进程)
//getdtablesize():返回当前的文件描述符个数
for(int i=0;i<getdtablesize();i++)
{
close(i);
}
//调用接口函数,实现记录功能,每隔一秒钟向文件下入记数
process();
}
else
{
//1.父进程推出 孤儿进程创建
exit(0);
}
return 0;
}
void process()
{
int fd = open("file1.txt",O_RDWR|O_CREAT,0640);
int count = 0;
while(1)
{
char buf[5]={0};
sprintf(buf,"%d ",count++);
write(fd,buf,strlen(buf));
sleep(1);
}
}
hqyj@ubuntu:~/5.15$ cat file1.txt
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
hqyj@ubuntu:~/5.15$ cat file1.txt
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
线程
-
每个进程的地址空间都是私有的,都拥有自己的task_struct结构体,每个task_struct都会指向自己映射的memory map虚拟地址映射表
-
当进程间进行任务切换时,都需要访问不同的task_struct结构体,因此需要不断刷新cache高速缓存器和TLB页表,导致系统开销大。因此,引入线程的概念,减小任务切换时系统开销较大的问题,提升系统性能
-
线程是轻量级的进程。
同一个进程中开启的多个线程,共享同一个进程地址空间
多个线程共同使用一个task_struct结构体
-
使用线程的好处
1、提高了线程间任务切换的效率
2、避免了额外的TLB和cache缓存的刷新
- posix线程库操作函数接口:
pthread
1、int pthread_create(pthread_t *tid,const pthread_attr_t *attr,void *( *routine)(void *),void *arg);
功能:
创建新的子线程,指定线程的属性,指定线程的执行函数
头文件:
#include <pthread.h>
参数:
tid:保存创建成功的线程ID号
attr:指定线程的属性,默认为NULL缺省属性
routine:指针函数指针,指向线程的执行函数(void *函数名(void *);指针函数)
arg:传递到线程执行函数的参数
返回值:
成功:0
失败:posix库中的错误码
***注意:程序编译时必须指定链接的动态库 -lpthread
gcc *.c -lpthread;
/*===============================================
* 文件名称:pthread_demo.c
* 创 建 者:memories
* 创建日期:2023年05月15日
* 描 述:
================================================*/
创建线程1,2,打印相应的内容
并不关闭线程
#include <stdio.h>
#include <unistd.h>
#include <pthread.h> //第三方线程库的头文件
void *thread1(void *arg)//定义一个指针函数,用作一个线程的执行函数
{
//线程的代码区域
while(1)//阻止子线程的结束
{
printf("child thread1\n");
sleep(1);
}
}
void *thread2(void *arg)//定义一个指针函数,用作一个线程的执行函数
{
//线程的代码区域
while(1)//阻止子线程的结束
{
printf("child thread2\n");
sleep(1);
}
}
int main(int argc, char *argv[])
{
//创建线程:pthread_create(pthread_t *tid,const pthread_attr_t *attr,void *(* routine)(void *),void *arg);
pthread_t tid;
if(pthread_create(&tid,NULL,thread1,NULL) <0 )
{
printf("create thread1 filed\n");
return -1;
}
if(pthread_create(&tid,NULL,thread2,NULL) <0 )
{
printf("create thread2 filed\n");
return -1;
}
while(1)
{
printf("main process\n");
sleep(1);
}
return 0;
}
hqyj@ubuntu:~/5.15$ gcc pthread_demo.c -lpthread
hqyj@ubuntu:~/5.15$ ./a.out
main process
child thread1
child thread2
main process
child thread1
child thread2
main process
child thread1
child thread2
main process
child thread1
child thread2
^C
pthread_exit(void *str);
2、void pthread_exit(void *str);
功能:
手动退出当前线程,传递数据。
参数:
str:要传递的数据的首地址
返回值:
无
/*===============================================
* 文件名称:pthread_demo.c
* 创 建 者:memories
* 创建日期:2023年05月15日
* 描 述:
================================================*/
1.创建子线程1,2
2.阻塞等待,指定的子线程1,2退出,回收资源子线程中的资源,
其中的资源thread 1 exit,thread 2 exit
#include <stdio.h>
#include <unistd.h>
#include <pthread.h> //第三方线程库的头文件
void *thread1(void *arg)//定义一个指针函数,用作一个线程的执行函数
{
//线程的代码区域
printf("child thread1\n");
sleep(2);
pthread_exit("thread 1 exit");
}
void *thread2(void *arg)//定义一个指针函数,用作一个线程的执行函数
{
//线程的代码区域
printf("child thread2\n");
sleep(5);
pthread_exit("thread 2 exit");
}
int main(int argc, char *argv[])
{
//创建线程:pthread_create(pthread_t *tid,const pthread_attr_t *attr,void *(* routine)(void *),void *arg);
pthread_t tid,tid2;
if(pthread_create(&tid,NULL,thread1,NULL) <0 )
{
printf("create thread1 filed\n");
return -1;
}
if(pthread_create(&tid2,NULL,thread2,NULL) <0 )
{
printf("create thread2 filed\n");
return -1;
}
char *res;
pthread_join(tid,(void **)&res);//阻塞等待,指定的子线程退出,回收资源
printf("main process=%s\n",res);
pthread_join(tid2,(void **)&res);
printf("main process=%s\n",res);
while(1);
return 0;
}
hqyj@ubuntu:~/5.15$ gcc pthread_exit.c -lpthread
hqyj@ubuntu:~/5.15$ ./a.out
child thread2
child thread1
main process=thread 1 exit
main process=thread 2 exit
^c
/*===============================================
* 文件名称:pthread_demo.c
* 创 建 者:memories
* 创建日期:2023年05月15日
* 描 述:
================================================*/
1.创建子线程1,2
2.阻塞等待,指定的子线程1,2退出,回收资源子线程中的资源,
其中的资源thread 1 exit,thread 2 exit
#include <stdio.h>
#include <unistd.h>
#include <pthread.h> //第三方线程库的头文件
void *thread1(void *arg)//定义一个指针函数,用作一个线程的执行函数
{
//线程的代码区域
printf("child thread1\n");
sleep(2);
pthread_exit("thread 1 exit");
}
void *thread2(void *arg)//定义一个指针函数,用作一个线程的执行函数
{
//线程的代码区域
printf("child thread2\n");
sleep(5);
pthread_exit("thread 2 exit");
}
int main(int argc, char *argv[])
{
//创建线程:pthread_create(pthread_t *tid,const pthread_attr_t *attr,void *(* routine)(void *),void *arg);
pthread_t tid,tid2;
if(pthread_create(&tid,NULL,thread1,NULL) <0 )
{
printf("create thread1 filed\n");
return -1;
}
if(pthread_create(&tid2,NULL,thread2,NULL) <0 )
{
printf("create thread2 filed\n");
return -1;
}
pthread_detach(tid);//非阻塞等待
pthread_detach(tid2);
printf("main-------\n");
while(1);
return 0;
}
qyj@ubuntu:~/5.15$ gcc pthread_exit.c -lpthread
hqyj@ubuntu:~/5.15$ ./a.out
main-------
child thread2
child thread1
^c
pthread_join(pthread_t tid,void **str);
3、 int pthread_join(pthread_t tid,void **str);
功能:阻塞主进程,等待指定线程的退出,回收资源,并保存子线程退出时传递的数据
参数:
tid:线程ID
str:二级指针,用来保存线程退出时传递的数据
返回值:
成功:0
失败:线程库的错误码
/*===============================================
* 文件名称:pthread_demo.c
* 创 建 者:memories
* 创建日期:2023年05月15日
* 描 述:
================================================*/
1.创建线程1,2
2.等待线程1关闭之后打印pthread1 quit
3.等待线程2关闭之后打印main process
#include <stdio.h>
#include <unistd.h>
#include <pthread.h> //第三方线程库的头文件
void *thread1(void *arg)//定义一个指针函数,用作一个线程的执行函数
{
//线程1的代码区域
printf("child thread1\n");
sleep(3);
}
void *thread2(void *arg)//定义一个指针函数,用作一个线程的执行函数
{
//线程2的代码区域
printf("child thread2\n");
sleep(5);
}
int main(int argc, char *argv[])
{
//创建线程:pthread_create(pthread_t *tid,const pthread_attr_t *attr,void *(* routine)(void *),void *arg);
pthread_t tid,tid2;
if(pthread_create(&tid,NULL,thread1,NULL) <0 )
{
printf("create thread1 filed\n");
return -1;
}
if(pthread_create(&tid2,NULL,thread2,NULL) <0 )
{
printf("create thread2 filed\n");
return -1;
}
pthread_join(tid,NULL);//阻塞等待 指定的子线程退出 回收资源
printf("thread1 quit\n");
pthread_join(tid2,NULL);
printf("main process\n");
return 0;
}
hqyj@ubuntu:~/5.15$ gcc pthread1_demo.c -lpthread
hqyj@ubuntu:~/5.15$ ./a.out
child thread1
child thread2
thread1 quit
main process
同一进程中的多个线程之间进行数据传递必须使用全局变量。
/*===============================================
* 文件名称:pthread_demo.c
* 创 建 者:memories
* 创建日期:2023年05月15日
* 描 述:
================================================*/
1.定义一个全局变量a,将a在两个线程之间进行数据传递
2.也可以在进程中定义一个变量b,但只能以函数传递参数的方式,使得b在两个线程之间进行数据传递
#include <stdio.h>
#include <unistd.h>
#include <pthread.h> //第三方线程库的头文件
int a=10;
void *thread1(void *arg)//定义一个指针函数,用作一个线程的执行函数
{
//int b=*(int *)arg;
//线程的代码区域
while(1)
{
printf("child thread1=%d,b=%d\n",a++,(*(int *)arg)++);
sleep(1);
}
}
void *thread2(void *arg)//定义一个指针函数,用作一个线程的执行函数
{
//int b=*(int *)arg;
//线程的代码区域
while(1)
{
printf("child thread2=%d,b=%d\n",a,*(int *)arg);
sleep(1);
}
}
int main(int argc, char *argv[])
{
int b=10;
//创建线程:pthread_create(pthread_t *tid,const pthread_attr_t *attr,void *(* routine)(void *),void *arg);
pthread_t tid,tid2;
if(pthread_create(&tid,NULL,thread1,(void *)&b) <0 )
{
printf("create thread1 filed\n");
return -1;
}
if(pthread_create(&tid2,NULL,thread2,(void *)&b) <0 )
{
printf("create thread2 filed\n");
return -1;
}
while(1);
return 0;
}
hqyj@ubuntu:~/5.15$ gcc pthread_data.c -lpthread
hqyj@ubuntu:~/5.15$ ./a.out
child thread1=10,b=10
child thread2=11,b=11
child thread1=11,b=11
child thread2=12,b=12
child thread1=12,b=12
child thread2=13,b=13
child thread1=13,b=13
child thread2=14,b=14
child thread1=14,b=14
child thread2=15,b=15
^C
/*===============================================
* 文件名称:pthread_demo.c
* 创 建 者:memories
* 创建日期:2023年05月15日
* 描 述:
================================================*/
a为全局变量则可以使得a现在线程1,2之间进行数据传递
但b这种不同于上种类型 当int b=*(int *)arg; 在线程2定义过一次之后,此时b在线程2为局部变量,无法进行线程间的数据传递
#include <stdio.h>
#include <unistd.h>
#include <pthread.h> //第三方线程库的头文件
int a=10;
void *thread1(void *arg)//定义一个指针函数,用作一个线程的执行函数
{
//int b=*(int *)arg;
//线程的代码区域
while(1)
{
printf("child thread1=%d,b=%d\n",a++,(*(int *)arg)++);
sleep(1);
}
}
void *thread2(void *arg)//定义一个指针函数,用作一个线程的执行函数
{
int b=*(int *)arg;
//线程的代码区域
while(1)
{
printf("child thread2=%d,b=%d\n",a,b);
sleep(1);
}
}
int main(int argc, char *argv[])
{
int b=10;
//创建线程:pthread_create(pthread_t *tid,const pthread_attr_t *attr,void *(* routine)(void *),void *arg);
pthread_t tid,tid2;
if(pthread_create(&tid,NULL,thread1,(void *)&b) <0 )
{
printf("create thread1 filed\n");
return -1;
}
if(pthread_create(&tid2,NULL,thread2,(void *)&b) <0 )
{
printf("create thread2 filed\n");
return -1;
}
while(1);
return 0;
}
hqyj@ubuntu:~/5.15$ gcc pthread_data.c -lpthread
hqyj@ubuntu:~/5.15$ ./a.out
child thread1=10,b=10
child thread2=11,b=11
child thread1=11,b=11
child thread2=12,b=11
child thread1=12,b=12
child thread2=13,b=11
child thread1=13,b=13
child thread2=14,b=11
child thread1=14,b=14
child thread2=15,b=11
child thread1=15,b=15
child thread2=16,b=11
child thread1=16,b=16
child thread2=17,b=11
^C