一、进程相关概念
1.什么是程序,什么是进程,有什么区别
程序是静态的概念,gcc xxx.c -o pro,磁盘中生成的pro文件叫程序
进程是程序的一次的活动,只要程序跑,系统中就多了一个进程
2.如何查看进程
ps 或ps -aux|grep 要查找的进程
3.什么是进程标识符
每个进程都有一个非负整数表示唯一ID,叫做pid,有点像身份证
pid=0:称为交换进程,作用:进程调度
pid=1:init进程,作用:系统初始化
调用getpid()获取自身进程标识符,getppid()获取获取父进程标识符
4.什么是父进程,什么是子进程
进程A创建了进程B,A就是父进程,B就是子进程
5.C语言的存储空间是如何分配的(自己画的,有点丑)
二、创建进程fork函数使用
fork函数调用成功,返回两次,返回值为0,代表当前进程是子进程,非负数为父进程
创建子进程的目的:复制父进程,在父进程等待客户端的服务请求,当这种请求到达时,父进程调用fork,让子进程去处理。
通俗点说就是:你爸让你去买烟,让你去干活
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main(){
//pid_t getpid(void);//获取自身的进程标识符
//pid_t getppid(void);//获取父进程的进程标识符
pid_t pid;//pid_t类似一个类型,就像int型一样,int型定义的变量都是整型的,pid_t定义的类型都是进程号类型。>用来接收进程号的返回值
pid_t pid2;
pid=getpid();
//pid_t fork(void);//创建进程,fork函数调用成功,返回两次,返回值为0,代表当前进程是子进程,返回值为非负数,代表当前进程为父进程
pid2=fork();
if(pid2 > 0){
printf("这是父进程! pid=%d,pid2=%d\n",pid,pid2);
}
else if(pid2 == 0){
printf("这是子进程! pid=%d,pid2=%d\n",pid,pid2);
}
return 0;
}
三、进程创建发生了什么事?
发生了复制,就是把代码段啊,数据段啊这些都统统复制了一遍,新进程就去执行这些
四、创建新进程实际应用案例
不输入指定的数字就一直等待数字的输入
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
pid_t pid;
int data;
int main(){
while(1){
printf("please input data:");
scanf("%d",&data);
if(data==1){
pid=fork();
if(pid > 0){
}
else if(pid==0){
while(1){
printf("do net request!,pid=%d\n",getpid());
sleep(3);
}
}
}
else{
printf("waiting insert data......\n");
}
}
return 0;
}
运行结果:
五、vfork创建进程
vfork与fork的区别:
1.vfork直接使用父进程的存储空间,不拷贝
2.vfork保证子进程先运行,当子进程调用exit退出后,父进程才执行
int main(){
int cnt=0;
pid_t pid;
pid_t repid;
pid=getpid();
repid=vfork();
if(repid > 0){
while(1){
printf("cnt=%d\n",cnt);
printf("this is father pid,repid=%d\n",repid);
sleep(1);
}
}
else if(repid == 0){
while(1){
printf("this is child pid,repid=%d\n",repid);
sleep(1);
cnt++;
if(cnt==2){
exit(0);
}
}
}
return 0;
}
运行结果:
六、进程退出
正常退出:
1.main函数调用return
2.进程调用exit(),标准C库
3.进程调用_exit()或者_Exit(),系统调用
4.进程最后一个线程返回
5.最后一个线程调用pthread_exit
异常退出
1.调用abort
2.当进程收到某些信号时,如ctrl+c
3.最后一个线程对取消(cancellation)请求作出响应
注意:不管进程如何终止,最后都会执行内核中的同一段代码,这段代码为相应进程关闭所有打开描述符,释放它所使用的存储器等。
七、父进程等待子进程退出
pid_t wait(int *status);
status参数:整型指针
非空:子进程退出状态放在它所指向的地址中
空:不关心退出状态
int main(){
pid_t pid;
pid_t repid;
int cnt=0;
int status;
pid=getpid();
repid=fork();
if(repid > 0){
wait(&status);//等待子进程退出
//子进程正常退出,WEXITSTATUS
printf("status=%d\n",WEXITSTATUS(status));//把终止的子进程通知给父进程
while(1){
printf("this is father pid,repid=%d\n",repid);
sleep(1);
}
}
else if(repid == 0){
while(1){
printf("this is child pid,repid=%d\n",repid);
sleep(1);
cnt++;
if(cnt==5){
printf("cnt=%d\n",cnt);
exit(5);
}
}
}
return 0;
}
八、exec族群
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(){
//int execl(const char *path, const char *arg, .../* (char *) NULL */);
/* path:可执行文件的路径名字
arg:可执行程序所带的参数,第一个参数为可执行文件名字,没有带路径且arg必须以NULL结束
file:如果参数file中包含/,则就将其视为路径名,否则就按 PATH环境变量,在它所指定的各目录中搜寻可执行文件
exec族函数参数极难记忆和分辨,函数名中的字符会给我们一些帮助:
l : 使用参数列表
p:使用文件名,并从PATH环境进行寻找可执行文件
v:应先构造一个指向各参数的指针数组,然后将该数组的地址作为这些函数的参数。
e:多了envp[]数组,使用新的环境变量代替调用进程的环境变量
exec函数族的函数执行成功后不会返回,调用失败时,会设置errno并返回-1,然后从原程序的调用点接着往下执行。
*/
printf("before execl\n");
if(execl("/bin/ls","ls",NULL,NULL) == -1)
{
printf("execl failed!\n");
perror("why");
}
printf("after execl\n");
return 0;
}
int main(){
//int execlp(const char *file, const char *arg, .../* (char *) NULL */);
printf("before execl\n");
if(execlp("ls","ls","-l",NULL) == -1)
{
printf("execl failed!\n");
perror("why");
}
printf("after execl\n");
return 0;
}
int main(){
//int execv(const char *path, char *const argv[]);
char* argv[]={"ls",NULL,NULL};
if(execv("/bin/ls",argv) == -1)
{
printf("execl failed!\n");
perror("why");
}
printf("after execl\n");
return 0;
}
int main(){
// int execvp(const char *file, char *const argv[]);
char* argv[]={"ls","-l",NULL};
if(execvp("ls",argv) == -1)
{
printf("execl failed!\n");
perror("why");
}
printf("after execl\n");
return 0;
}
九、exec与fork相结合
int main(){
pid_t pid;
pid_t repid;
int data;
while(1){
printf("please input a number:");
scanf("%d",&data);
if(data==1){
repid=fork();//创建进程
if(repid > 0){
wait(NULL);
}
if(repid == 0){
execl("./changeNumToFile.out","changeNumToFile.c","changeNum.c",NULL);
exit(0);
}
}
else{
printf("waiting!\n");
}
}
return 0;
}
//changeNumToFile.c
int main(int argc,char* argv[]){
int fd;
char* readBuf;
//int open(const char *pathname, int flags);
fd=open("./changeNum.c",O_RDWR);//打开文件
int size=lseek(fd,0,SEEK_END);
lseek(fd,0,SEEK_SET);
//ssize_t read(int fd, void *buf, size_t count);
readBuf=(char*)malloc(sizeof(char)*size+8);
int n_read=read(fd,readBuf,size);
char* p=strstr(readBuf,"ouliang=");
if(p==NULL){
printf("not fingd\n");
}
else{
p=p+strlen("ouliang=");
*p='5';
lseek(fd,0,SEEK_SET);
int n_write=write(fd,readBuf,size);
}
close(fd);
return 0;
}
//changeNum
dugad=100;
ouliang=3;
aisi=120;
运行结果:
十、system函数
int main(){
pid_t pid;
pid_t repid;
int data;
while(1){
printf("please input a number:");
scanf("%d",&data);
if(data==1){
repid=fork();//创建进程
if(repid > 0){
wait(NULL);
}
if(repid == 0){
//execl("./changeNumToFile.out","changeNumToFile.c","changeNum.c",NULL);
system("./changeNumToFile.out");//执行完该函数后,还会执行后面的函数
exit(0);
}
}
else{
printf("waiting!\n");
}
}
return 0;
十一、popen函数
#include <stdio.h>
#include <stdlib.h>
// FILE *popen(const char *command, const char *type);
// FILE *fopen(const char *path, const char *mode);
// size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
int main(){
// system("ls");
FILE* fp;
char ret[1024];
fp=popen("ls -l","r");
int nread=fread(ret,1,1024,fp);
if(nread != -1){
printf("read sucess!\n");
printf("return nread=%d,read datas=\n%s\n",nread,ret);
}
else{
printf("no read datas\n");
}
return 0;
}