多线程和多进程的管理
来源:互联网 发布:手机淘宝网店实名认证 编辑:程序博客网 时间:2024/06/15 22:11
多线程和多进程的管理
基础知识介绍
进程标识符
每个进程都有一个非负整型表示的唯一进程ID,除 了进程ID,每个进程还有一些其他的标识符。可通过下列函数来 返回。
pid_t getpid(void);//返回调用进程的进程ID pid_t getppid(void); //返回调用进程的父进程ID uid_t getuid(void); //返回调用进程的实际用户ID uid_t geteuid(void); //返回调用进程的有效用户IDgid_t getgid(void); //返回调用进程的实际组ID gid_t getegid(void);//返回调用进程的有效组ID
进程创建的时机
进程的创建过程
- 取空白PCB,生成惟一的进程标识号。
- 为新进程分配必要的资源(包括程序段、数据段、栈段)。
- 初始化PCB。
- 将新进程的PCB插入就绪队列
Linux中进程的创建
Linux启动在内核态,在系统初始化结束时,初始进程启动一个核心进程(称为init),除了init进程外,Linux 中的所有进程都是由其他进程创建的。
使用系统调用fork()进行进程的创建。
子进程是父进程的映像,子进程除了进程的状态、标识 和与时间有关的控制项外,其余都与父进程相同。
fork系统调用
当进程调用fork创建子进程后,根据fork的返回值来判断:当
前执行的是父进程的程序段还是子进程的程序段。
返回值:
等于0,当前进程是子进程。 大于0,当前进程是父进程,返回值是子进程的ID。 等于-1,创建失败。
格式:
int pid_t fork(void);
头文件:
#include<sys/types.h> //提供pid_t的定义#include<unistd.h>
当一个程序中调用fork函数后,内核会完成如下工作:
1. 内核系统分配新的内存块和内核数据结构。
2. 复制原来的进程到新的进程。
3. 向运行进程集添加新的进程。
4. 将进程号返回给两个进程。
常见问题
父进程为什么要创建子进程?
多用户操作系统、竞争资源、创建子进程争夺资源、父子进程一起从fork处继续执行。
父进程创建子进程后,子进程能不能执行不同的程序?
答案是可以的,这种情况下,子进程从fork返回后要立即调 用exec系统调用来实现。
怎样才能使父进程在子进程结束之后才执行?
调用wait系统调用来实现父子进程同步执行。
exec系统调用
用fork函数创建子进程后,子进程往往要调用一种exec函数以执行另一个程序。
当进程调用一种exec函数时,该进程完全由新程序代换,而新程序则从其main函数开始执行。
因为调用exec并不创建新进程,所以前后的进程ID并未改变。 exec只是用另一个新程序替换了当前进程的正文、数据、堆和栈 段。
有六种不同的exec函数,它们被统称为exec函数。
execl (const char * filepath, const char* arg1, char* arg2......)execv (const char* filepath,char* argv[])execle(const char*filepath,const char*arg1,const char*arg2,....., char* cons envp[])execve (const char* filepath,char*argv[],char* const envp[]) execlp(const char *filename, const char *arg1, const char *arg2.....) execvp (const char * filename, char* argv[])
进程终止
进程正常结束 在进程执行期间调用exit系统调用。
进程的异常结束 在进程运行期间,由于出现错误或故障而被迫结束。
exit系统调用
格式:void exit(status)
参数status是调用进程终止时传递给父进程的值。如果调用进程 还有子进程,则将其所用子进程的父进程改为1号进程。
wait系统调用
wait函数用于使父进程阻塞,直到一个子进程结束。父进程调用
wait,该父进程可能会:
1. 阻塞
2. 带子进程的终止状态立即返回
3. 出错立即返回 格式:pid_t wait(int *status)
参数status是一个指向int类型的指针,用来保存被收集进程退出时的一 些状态。但如果我们对子进程是如何死掉的毫不在意,则pid=wait (NULL);
头文件:
#include<sys/types.h> #include<sys/wait.h >
线程相关知识
线程标识
就像每个进程都有一个进程ID一样,每个线程也有一个线程ID,
但线程ID只在它所属的进程环境中有效。
定义方法为:
pthread_t id;
线程创建
pthread库
Linux下的多线程遵循POSIX线程接口,称为pthread。pthread是 一套用户级线程库,具有很好的移植性。但在linux上实现时,却使用了内 核级线程来完成,这样提高的线程的并发性。 Linux下的多线程程序, 需要使用头文件pthread.h,连接时需要使用库pthread。
gcc example.c -lpthread -o example
pthread系统调用
pthread_create();//:创建一个线程。pthread_join();//:阻塞当前的线程,直到另外一个线程运行结束。pthread_exit();//:终止当前线程。pthread_cancel();:中断另外一个线程的运行。pthread_attr_init();//:初始化线程的属性。pthread_attr_setdetachstate()/;;:设置脱离状态的属性(决定这 个线程在终止时是否可以被结合)。pthread_attr_getdetachstate();//:获取脱离状态的属性。 pthread_attr_destroy();//:删除线程的属性。 pthread_kill();//:向线程发送一个信号。
pthread_create函数用来创建一个线程,它的原型为:
int pthread_create ( pthread_t *tidp, pthread_attr_t *attr, void*(*start_routine) (void *), void *arg);
参数1:指向线程标识符的指针。
参数2:用来设置线程属性,若为NULL,则用默认属性。
参数3:新线程运行函数的起始地址。
参数4:运行函数的参数。
正确返回:0。线程标识存放在thread中
错误返回:非0的错误代码。
pthread_join 函数用来等待一个线程的结束,函数原型为:
int pthread_join ( pthread_t th, void** thread_return );
参数1:为等待终止的子线程标识符。
参数2:用户定义的指针,它可以用来存储被等待线程的返回值。
这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止。
pthread_exit函数用来终止线程,函数原型为:
void pthread_exit ( void *_retval );
唯一的参数是函数的返回代码.
只要pthread_join中的第二个参数thread_return不是NULL, 这个值将被传递给thread_return。
相关练习
练习要求
1号进程创建2,3号两个进程
2号进程执行系统命令,ls,ps,cp等
3号进程创建4,5号两个进程
4号进程创建两个线程Thread1,Thread2。
Thread1:求(1~n)之间的素数
Thread2:生成Fibonacci序列
5号进程执行一个用户编写的可执行文件
每个进程输出自己的进程ID和父进程的进程ID,观察分析,并画出程序的进程树结构。
代码实现
#include <stdio.h>#include <sys/types.h> // tigong pid_t define#include <unistd.h>#include <sys/wait.h>#include <pthread.h>#include <math.h>#include <stdlib.h>void* thread_findss(){ int n=100; printf("prime number:\n"); int i,j; for(i=2;i<=n;i++) { for(j=2;j<i;j++) { if(i%j==0) break; } if(j>=i) printf("%d ",i); } printf("\n "); pthread_exit(0);}int Fibo(int n){ if(n==1||n==2) return 1; return Fibo(n-1)+Fibo(n-2);}void* thread_fibonacci(){ int n=10; printf("fibonacci:\n"); int i; for(i=1;i<=n;i++) { printf("%d ",Fibo(i)); } printf("\n"); pthread_exit(0);}int main(int argc,char *argv[]){ pid_t pid2; pid2=fork(); if(pid2<0) { fprintf(stderr,"2 fork failed"); exit(-1); } else if(pid2==0) //son 2 { // ls,ps,cp; // printf("2 this is parent %d,child is %d\n",getppid(),getpid()); pid_t pid21; pid21=fork(); if(pid21<0) { fprintf(stderr,"2 fork failed"); exit(-1); } else if(pid21==0) //son 2 { execlp("/bin/ls", "/bin/ls",NULL); exit(0); } else{} pid_t pid22; pid22=fork(); if(pid22<0) { fprintf(stderr,"2 fork failed"); exit(-1); } else if(pid22==0) //son 2 { execlp("/bin/ps", "/bin/ps",NULL); exit(0); } else{} exit(0); } else{} pid_t pid3; pid3=fork(); if(pid3<0){ fprintf(stderr,"3 fork failed"); exit(-1); } else if(pid3==0) //son 3 { // printf("3 this is parent %d,child is %d\n",getppid(),getpid()); // creat 4,5 pid_t pid4; pid4=fork(); if(pid4<0){ fprintf(stderr," 4 fork failed"); exit(-1); } else if(pid4==0) //son 4 { // printf("4 this is parent %d,child is %d\n",getppid(),getpid()); pthread_t id1; pthread_attr_t attr1; pthread_attr_init(&attr1); pthread_create(&id1,&attr1,thread_findss,argv[1]); pthread_join(id1,NULL); pthread_t id2; pthread_attr_t attr2; pthread_attr_init(&attr2); pthread_create(&id2,&attr2,thread_fibonacci,argv[1]); pthread_join(id2,NULL); exit(0); } else { } pid_t pid5; pid5=fork(); if(pid5<0){ fprintf(stderr," 5 fork failed"); exit(-1); } else if(pid5==0) //son 5 { //printf("5 this is parent %d,child is %d\n",getppid(),getpid()); execl("./sy1-help","sy1-help",NULL); exit(0); } else { } exit(0); } else { } exit(0); return 0;}
本博客内容到此结束,欢迎指正!
- 多线程和多进程的管理
- 【操作系统】多线程和多进程的管理
- 多进程和多线程
- 多线程和多进程
- 多进程和多线程
- 多进程和多线程
- 多线程和多进程
- 多进程和多线程
- 多进程和多线程
- 多进程和多线程
- 多进程和多线程
- 多进程和多线程
- 多线程和多进程
- 多进程和多线程
- 多线程和多进程
- 多进程和多线程
- 多线程和多进程
- 多进程和多线程
- MT6737 7.0Camera总结
- java环境变量配置的目的与作用
- extern的使用方法
- 算法课第十一周作业 | Longest Consecutive Sequence
- 实例变量与static变量的区别
- 多线程和多进程的管理
- 使用qsort排序一个整型数组,一个浮点型数组,一个字符串数组
- GitHub For Windows 使用教程
- LeetCode:36. Valid Sudoku,数独是否有效 :
- Yii2 使用Event -2 ,如何使用事件_@TERRY
- 自己实现一个bubble_sort(冒泡排序),可以完成不同类型数据的排序
- Linux中确定CPU的情况
- 原码反码补码
- es6 -- 语法(扩展)