exec函数簇:
1.概念:
函数族提供了一种在进程中启动另一个程序执行的方法。
它可以根据指定的文件名或目录名找到可执行文件,并用它来取代原调用进程的数据段、代码段和堆栈段,在执行完之后,原调用进程的内容除了进程号外,其他全部被新程序的内容替换了。另外,这里的可执行文件既可以是二进制文件,也可以是Linux下任何可执行脚本文件。
比如bash用到了exec函数来执行我们的可执行文件。
2.在Linux中使用exec函数族主要有以下两种情况
当进程认为自己不能再为系统和用户做出任何贡献时,就可以调用任何exec函数族让自己重生。
如果一个进程想执行另一个程序,那么它就可以调用fork函数新建一个进程,然后调用任何一个exec函数使子进程重生。
一般使用第二种
3.函数
#include <unistd.h>
int exec1(const char *path,const char *arg,...);
int execv(const char *path,char *const argv[]);
int execlp(const char *file,const char *arg,...);
int execvp(const char *fi1e,char *const argv[]);
int execle(const char *path,const char *arg,...,char *const envp[]);
int execve(const char *path,char *const argv[],char *const envp[]);
返回值:
成功不返回
失败返回-1 更新 errno
注意:
exec函数的参数表传递方式以函数名的第五位字母来区分:
字母为"l"(list)的表示逐个列举的方式;
字母为"v" (vertor)的表示将所有参数构造成指针数组传递;
以p结尾的函数可以只给出文件名
事实上,这6个函数中真正的系统调用只有execve,其他5个都是库函数,它们最终都会调用execve这个系统调用
例:
//在屏幕上持续打印时间time.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
while(1)
{
time_t t;
time(&t);
char *p = ctime(&t);
printf("%s\n", p);
sleep(1);
}
return 0;
}
gcc time.c -o test
//exec函数族示例代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc,int *argv[]){
printf ( "111111111\n");
//char *arg[] = {"test" ,NULL};
//int ret = execv("./test", arg);
//int ret = execl("./test","test",NULL);
//int ret = execlp = ("ls","ls","-l",NULL);
//char *arg[] = {"ls","-l",NULL};
//int ret = execvp("ls", arg);
//char *envp[] = {"PATH = .",NULL}; 设置当前环境变量
//int execle("./test","test",NULL,envp);
//char *arg[] = {"test",NULL};
//char *envp[] = {"PATH = .",NULL}; 设置当前环境变量
//int execve("./test",arg,envp);
if(ret == -1){
perror ("execl") ;
exit(-1);
}
printf("222222222\n");
return 0;
}
守护进程:
1.守护进程:
在linux中与用户交互的界面叫终端,从终端运行起来的程序都依附于这个终端,当终端关关闭时,相应的进程都会被关闭,守护进程可以突破这个限制。
2.特点:
在后台服务的进程
生存期很长
守护进程独立于控制终端
比如: init进程pid=1开机运行关机才结束
3.守护进程创建流程:
1.创建子进程,父进程退出
fork(void);
2.在子进程中创建新会话
setsid(void);
3.修改工作目录
chdir("");
4.修改umask (增加安全性)
umask();
5.关闭文件描述(回收资源)
close();
例:把进程变为守护进程,以下程序将进程的父进程改变为init()守护进程,关闭终端,不会将进程杀死,可通过kill命令杀死
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
int main(int argc,const char *argv[]){
pid_t pid = fork();
if(pid < 0){
perror("fork");
exit(1);
}
else if(pid == 0){
setsid(); //第二步:开启新的回话
chdir("/tmp"); //第三步:改变工作目录
umask(0); //第四步:增加安全性
close(0);
close(1);
close(2); //第五步:关闭文件描述符
FILE *fp;
while(1){
fp = fopen("/tmp/test.txt","a");
time_t t;
time(&t);
fprintf(fp,"%s",ctime(&t));
fflush(fp); //因为fpintf是全缓存,所以需要强制刷新
sleep(1);
}
}else{
exit(0); //第一步:父进程退出
}
}
线程:
线程是CPU能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位,
一个进程可以开启多个线程,其中有一个主线程来调用本进程中的其他线程。
1.每个用户进程有自己的虚拟地址空间(32位机0~4G,64位机0~8G)
⒉.系统为每个用户进程创建一个task_struct结构体来描述该进程struct task_struct
3.task_struct和地址空间映射表一起用来表示一个进程
4.由于进程的虚拟地址空间是私有的,因此进程切换开销很大
5.为了提高系统的性能,linux引入轻量级进程,起名为线程
6.在同一个进程中创建的线程共享该进程的地址空间
7.Linux里同样用task_struct结构体来描述一个线程。线程和进程都参与统一的调度
总结:
1.通常线程指的是共享相同虚拟地址空间的多个任务
⒉.使用多线程,大大提高了任务切换的效率
线程需要多大的虚拟内存?
进程中的所有线程是共享进程的地址空间,但是系统会为每个线程分配独立的调用栈,也就说每个线程都会有一个自己的调用栈。所以线程需要的虚拟内存大小也就是我们的机器上调用栈的内存大小
每个进程中至少有一个线程,就是主线程,还可以产生多个线程共享4G内存空间,线程切换只需要虚拟CPU(寄存器)同样用task_struct结构体来描述一个线程,线程和进程都参于统一的调度
进程代表资源分配的最小单位
线程是最小的调度单位
当进程中只有一份线程时,进程和线程没有区别
当进程中有两个或两个以上时,进程和线程有区别
线程相关函数:
创建线程
#include <pthread .h>
pthread_t tid; //线程号
int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
功能:创建线程
参数:thread:&tid 线程号的地址
attr:NULL 默认参数(缺省参数),线程的属性
start_routine:线程函数的函数名 (第三个参数是一个回调函数)
arg:给线程函数传递的参数,如果不传参写NULL
返回值:
成功:0
失败:错误码
compile and link with -pthread. //gcc编译时需要链接pthread三方库
例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <time.h>
void *fun(void *arg);
int main(int argc,const char *argv[]){
pthread_t tid;
int ret = pthread_create(&tid,NULL,fun,NULL);
if(ret == -1){
perror("pthread_create");
return -1;
}
while(1){
printf("main is running!\n");
sleep(1);
}
return 0;
}
void *fun(void *arg){
while(1){
printf("fun is running!\n");
sleep(1);
}
return 0;
}
//两个线程运行时,cpu对他们的调度是随机的,没有先后
//编译时需要链接第三方pthread库,不然会报错 gcc mian.c -pthread
回收线程资源
int pthread_join(pthread_t thread,void **retva1);
功能:主线程等待子线程结束,回收它的资源(阻塞)
参数:
thread :线程号
retval :子线程的返回值
返回值:
成功:0
失败:-1
int pthread_detach(pthread_t thread);
功能:主线程和子线程分离开来,自动回收资源(非阻塞)
参数:
thread:线程号
返回值:
成功:0
失败:-1
例:阻塞状态,子进程结束,父进程回收子进程资源之后再继续
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
void *fun(void *arg);
int main(int argc,const char *argv[]){
pthread_t tid;
int ret = pthreat_create(&tid,NULL,fun,NULL);
if(ret == -1){
perror("pthreat_create");
return -1;
}
printf("aaaaaaaaa\n");
pthread_join(tid,NULL);
printf("bbbbbbbbb\n");
while(1);
return 0;
}
void *fun(void *arg){
int n = 5;
while(n--){
printf("fun is running!\n");
sleep(1);
}
return 0;
}
例:非阻塞状态,子进程和父进程分离开来进行,子进程结束时父进程自动回收资源
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
void *fun(void *arg);
int main(int argc,const char *argv[]){
pthread_t tid;
int ret = pthreat_create(&tid,NULL,fun,NULL);
if(ret == -1){
perror("pthreat_create");
return -1;
}
printf("aaaaaaaaa\n");
pthread_detach(tid);
printf("bbbbbbbbb\n");
while(1);
return 0;
}
void *fun(void *arg){
int n = 5;
while(n--){
printf("fun is running!\n");
sleep(1);
}
return 0;
}
多线程举例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
void fun();
int main(int argc,const char *argv[]){
pthread_t tid,tid1,tid2;
int ret = pthreat_create(&tid,NULL,fun,NULL);
if(ret == -1){
perror("pthreat_create");
return -1;
}
printf("aaaaaaaaa\n");
pthread_detach(tid);
pthread_create(tid1,NULL,fun1,NULL);
pthread_detach(tid1);
pthread_create(tid2,NULL,fun2,NULL);
pthread_detach(tid2);
printf("bbbbbbbbb\n");
while(1);
return 0;
}
void *fun(void *arg){
int n = 3;
while(n--){
printf("fun is running!\n");
sleep(1);
}
return 0;
}
void *fun1(void *arg){
int n = 5;
while(n--){
printf("fun1 is running!\n");
sleep(1);
}
return 0;
}
void *fun2(void *arg){
int n = 7;
while(n--){
printf("fun2 is running!\n");
sleep(1);
}
return 0;
}
//此处加上main函数这个线程,一共用4个线程
结束子线程
void pthread_exit(void *retva1);
功能:结束子线程
参数:
retval : void *
返回值:
成功:0
失败:-1
线程传参的两种方式:值传递和地址传递
值传递
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
void *fun(void *arg);
int main(int argc,const char *argv[]){
pthread_t tid;
int count = 100;
pthread_create(&tid,NULL,fun,(void *)count); //这个地方的void *强转只是为了将count的值传递到join函数中,而join函数中是一个二级指针,所以进行强转
void *m; //定义一个一级指针
pthread_join(tid,&m); //对一级指针取地址 (结合函数定义:int pthread_join(pthread_t thread,void **retva1);)
printf("%d %d\n",count,(int)m);
return 0;
}
void *fun(void *arg){
int m = (int)arg;
printf("%d\n",m);
pthread_exit(arg); //将arg的值返回,相当于return arg
}
结果:
100
100 100
编译运行会出现如下警告:
warning: cast to pointer from integer of different size
原因:在ubuntu20.04中,void *类型和int类型的大小分别为8bit和4bit,所以会出现警告
join函数可以用来接收exit函数的返回值
fun函数中arg的类型是*类型,在main函数中要接收返回值,则需要对其取地址
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
void fun();
int main(int argc,const char *argv[]){
pthread_t tid;
int count = 100;
pthread_create(&tid,NULL,fun,(void *)count);
while(1);
return 0;
}
void *fun(void *arg){
int m = (int)*(int *)arg;
printf("%d\n",m);
}
结果:
100
100 100
地址传递
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
void *fun(void *arg);
int main(int argc,const char *argv[]){
pthread_t tid;
char buf[10] = "Hello";
pthread_create(&tid,NULL,fun,(void *)buf);
void *arg; //定义一个一级指针
pthread_join(tid,&arg); //对一级指针取地址 (结合函数定义:int pthread_join(pthread_t thread,void **retva1);)
printf("%s %s\n",buf,(char *)arg);
return 0;
}
void *fun(void *arg){
char *p = (char *)arg;
printf("%s\n",p);
pthread_exit(arg); //将arg的值返回,相当于return arg
}
结果:
Hello
Hello Hello
关闭线程
int pthread_cance1(pthread_t thread);
例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
void fun(void *arg);
int main(int argc,const char *argv[]){
pthread_t tid;
pthread_create(&tid,NULL,fun,(void *)buf);
pthread_detach(tid);
char buf[10] = {0};
while(1){
gtes(buf);
if(strncmp(buf,"q",1) == 0){
pthread_cancel(tid);
}
if(strncmp(buf,"Q",1) == 0){
exit(0);
}
}
return 0;
}
void fun(void *arg){
printf("%s\n",arg);
}
结果:获取到键盘输入的字符数
优点:
线程间很容易进行通信
通过至局变量实现数据共享和交换
缺点:
多个线程同时访问共享对象时需要引入同步和互斥机制
同步和互斥︰
保护共享资源,避免竟态
同步:多个任务按理想的顺序/步调来进行
互斥:不能同时访问
都是为了避免竟态:多个任务同时访问共享资源
线程的同步----信号量
#include <semaphore.h>
sem_t sem;//信号量的变量
int sem_init(sem_t *sem,int pshared,unsigned int value);
功能:初始化信号量
参数:
sem : &sem信号量的变量的地址
pshared: 0 :线程 1 :进程
value:信号量的初值
返回值:
成功:0
失败:-1
int sem_wait(sem_t *sem); //-1
//如果初值为0,会阻塞
int sem_post(sem_t *sem); //+1
先post再wait
例:主线程等待子线程结束
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
sem_t sem;
void *fun(void *arg);
int main(){
sem_init(&sem,0,0); //信号量的初始化
pthreate_t tid;
pthread_create(&tid,NULL,fun,NULL);
printf("111111\n");
sem_wait(&sem); //会阻塞,因为初值赋的是0
printf("222222\n");
}
void *fun(void *arg){
int n = 5;
while(n--){
printf("333333\n");
sleep(1);
}
sem_post(&sem);
}
结果:
11111
33333
33333
33333
33333
33333
22222
线程的互斥----互斥锁
pthread_mutex_t mutex; //互斥锁的变量
int pthread_mutex_init(pthread_mutex_t *mute,const pthread_mutexattr_t *mutexattr);
功能:初始化互斥锁
参数:
mutex : &mutex
mutexattr: NULL(默认属性)
返回值:
成功:О
失败:-1
int pthread_mutex_1ock(pthread_mutex_t *mutex); //加锁
int pthread_mutex_unlock(pthread_mutex_t *mutex) ; //解锁
例:不加互斥锁同时访问同一片地址空间
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int i,value1,value2;
pthread_mutex_t mutex;
void *fun1(void *arg){
while(1){
i++;
value1 = i;
value2 = i;
}
}
void *fun2(void *arg){
while(1){
if(value1 != value2){
printf("%d %d %d",i,value1,value2);
sleep(1);
}
}
}
int main(){
pthread_t tid1,tid2;
pthread_create(&tid1,NULL,fun1,NULL);
pthread_detach(tid1);
pthread_create(&tid2,NULL,fun2,NULL);
pthread_detach(tid2);
}
例:加互斥锁
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int i,value1,value2;
pthread_mutex_t mutex;
void *fun1(void *arg){
while(1){
pthread_mutex_lock(&mutex);
i++;
value1 = i;
value2 = i;
pthread_mutex_unlock(&mutex);
}
}
void *fun2(void *arg){
while(1){
pthread_mutex_lock(&mutex);
if(value1 != value2){
printf("%d %d %d",i,value1,value2);
}
pthread_mutex_unlock(&mutex);
}
}
int main(){
//初始化互斥锁
pthread_mutex_init(&mutex, NULL);
pthread_t tid1,tid2;
pthread_create(&tid1,NULL,fun1,NULL);
pthread_detach(tid1);
pthread_create(&tid2,NULL,fun2,NULL);
pthread_detach(tid2);
}