旅行社手机网站建设方案百度收录快的发帖平台
一)进程1
1) pstree,查看进程树
2)umask,是打印当前程序的umask;为用户文件创建权限掩码;
3) 在shell下输入命令可以运行一个程序,是因为shell进程在读取用户输入命令的命令之后,会调用fork复制出一个新的shell进程;
4) fork创建一个子进程,说是叫子进程,其实就是复制一份父进程,除了进程号不一样;用返回值分辨谁是父,谁是子;
5)refer to as:称作,称为的意思;
6)当子进程结束,会有尸体,这个尸体会由父进程收尸;
//子进程和父进程的简单案例:
#include<sys/types.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<unistd.h>
#include<fcntl.h>int main(int argc, char** argv)
{//调用fork会进入内核空间,pid_t pid = fork(); //理论上返回的应该是孩子id,叫childpid,简写为pid; int n;const char* message;if(pid < 0) //子进程从这里就开始判断了{perror("pid create");exit(1);}if(pid == 0){n = 6;message = "son process\n";}else{n = 3;message = "parent process\n";}while(n > 0){n--;printf(message);sleep(1); //调用sleep,程序进入内核空间,当睡眠时间到,程序会进入就绪态,等待cpu调度,//当cpu调度程序的时候,程序又返回用户空间,开始运行程序;}return 0;
}
进程二)
1)getpid,getppid的学习,跟fork返回的pid毛关系都没有;
ps aux|grep 18878; //ps aux是显示所有进程,grep 18878是搜索是否有这个18878进程;
#include<sys/types.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<unistd.h>
#include<fcntl.h>int main(int argc, char** argv)
{//调用fork会进入内核空间,pid_t pid = fork(); //理论上返回的应该是孩子id,叫childpid,简写为pid; int n; if(pid < 0) //子进程从这里就开始判断了{perror("pid create");exit(1);}if(pid == 0){n = 6; while( n > 0){n--;//只有在这里可以得到子进程的id,别无它法;printf("child self id is %d, parent = %d\n",getpid(),getppid()); //父进程是fork创建后大于0的那个进程sleep(1);}}else{n = 3; while( n > 0){n--; printf("parent self id is %d, parent = %d\n",getpid(),getppid()); //父进程是shell的进程sleep(1); }}return 0;
}2)循环创建10个子进程案例
#include<sys/types.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
//重点: 只让父进程循环创建子进程,不能让子进程再次创建子进程;
int main(int argc, char** argv)
{for(int i = 0; i< 10; i++){pid_t pid = fork();if(pid < 0){perror("pid create");exit(1);}if(pid == 0) //子进程必须break,否则子子孙孙,无穷匮也;{printf("i is %d,child self id is %d, parent = %d\n",i,getpid(),getppid()); //父进程是fork创建后大于0的那个进程break; //如果不终止,则创建出来的子进程也会参与循环创建,理论上子进程个数为10*9*...*1这么多个;}else //父进程直接continue;{continue;}}return 0;
}
//输出结果如下:
i is 0,child self id is 19469, parent = 19468
i is 1,child self id is 19470, parent = 19468
i is 2,child self id is 19471, parent = 19468
i is 3,child self id is 19472, parent = 19468
i is 4,child self id is 19473, parent = 19468
i is 5,child self id is 19474, parent = 19468
i is 6,child self id is 19475, parent = 19468
i is 7,child self id is 19476, parent = 19468
i is 8,child self id is 19477, parent = 19468
i is 9,child self id is 19478, parent = 19468
3)gdb多进程调试及exec函数的用法
exec中'l'是list的意思,参数为不定参数;exec中'v'是vector的意思,是定参的,是数组;'p'是路径的意思;'e'是环境变量表需要传进来;set follow-fork-mode child/parent //模式设置
当进程调用exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新的程序的启动例程开始执行;
execve是真正的可执行程序函数;
environ英 [ɪn'vaɪərən] v 包围,环绕;
getenv和setenv函数的用法及案例:
#include<sys/types.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<unistd.h>
#include<fcntl.h>int main(int argc, char** argv)
{// extern char** environ; //引入一个已经定义的变量// for(int i = 0; environ[i] != NULL; i++)// {// printf("%s\r\n",environ[i]); //打印环境变量值// }char* path = getenv("PATH"); //从当于从父进程的环境变量里面复制了一份,传到这里;printf("[%s]\r\n",path); //加一个中括号,就知道从哪里开始,到哪里结束setenv("PATH","helllo",1); //主进程环境变量没有改,改的是子进程的环境变量path = getenv("PATH");printf("[%s]\r\n",path);return 0;
}
4)exec函数的使用方法:
#include<sys/types.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<unistd.h>
#include<fcntl.h>int main(int argc, char** argv)
{//带'/',则理解为路径;不带'/',则到$PATH这个路径下找;//第二个不定长参数:主函数传参的所有参数都列出来,注意,第0个参数也要传进来,对与"ls",来说,就是ls,execlp("/bin/ls", "hahaha", "-a", "-l", NULL); //hahaha这个参数没有任何用,写空也行,hahaha应该是ls参数,尽管它没有作用;perror("exec"); //这句话不会执行exit(1);return 0;
}
5)自己实现流的重定向
读一个文件,实现小写转大写,并把转完的数据写入到另一个文件中,包括5.1和5.2两个程序;
5.1)getchar函数的使用方法;
#include<stdio.h>
#include<unistd.h>
#include<ctype.h>//测试程序: ./target < test.txt //从文件读数据,'<' 相当于重定向;
int main(int argc, char** argv)
{int ch;while((ch = getchar()) != EOF) //getchar()函数的作用是从计算机终端(一般为键盘)获取一个无符号字符;{putchar(toupper(ch));}return 0;
}
5.2)输入、输出重定向,把标准输入和标准输出重定向为对文件的操作;
#include<stdio.h>
#include<unistd.h>
#include<ctype.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<sys/types.h>
#include<stdlib.h>//输入、输出重定向,把标准输入和标准输出重定向为对文件的操作;
int main(int argc, char** argv)
{if(argc < 2){printf("cmd + inputfile + outputfile\r\n");return 1;}int fd = open(argv[1],O_RDONLY); if(fd < 0){perror("open read");exit(1);} dup2(fd,0); //修改了标准输入流的位置,相当于0取代了3,言外之意就是,从标准输入得到的数据,改为从打开的文件中读取;close(fd); //因为这里关闭了fd = open(argv[2],O_WRONLY | O_CREAT, 0644); //如果没有文件,就创建一个if(fd < 0){perror("open write");exit(1);} //下面代码的意思是:按道理应该打印到屏幕上(标准输出),dup2重定向后,把数据写到文件中去了。dup2(fd,1); //修改了标准输出流的位置,相当于1取代了3; 因为上面关闭了,所以还是3close(fd);execl("./upper","./upper",NULL);perror("exec");return 0;
}
6)wait和waitpid的作用,是收尸的
一个程序运行起来,分两块,一个是内核空间,一个是用户空间;
defunct 美 [dɪˈfʌŋkt] 失效的
僵尸进程不能被kill -9 ps_id 杀死,相当于是已经死了,杀尸体屁用没有;
如果父进程被杀死了,子进程就会被孤儿院收尸了。
//父进程等待子进程退出的简单案例:
#include<sys/types.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<unistd.h>
#include<wait.h>int main(int argc, char** argv)
{pid_t pid = fork(); //一上电就先创建两个进程;int n = 0;if(pid < 0){perror("pid create");exit(1);}if(pid == 0) //子进程{n = 3; while( n > 0){n--;printf("child self id is %d, parent = %d\n",getpid(),getppid()); //父进程是fork创建后大于0的那个进程sleep(1);}exit(3); //这里正常退出}else //父进程调用waitpid等待子进程退出,这样就会变成同步机制(没有调用wait的时候,是异步机制){int stat_val;waitpid(pid,&stat_val,0); if(WIFEXITED(stat_val)) //正常退出,打印出状态码 //WIFEXITED 定义格式:wait if exited的缩写{printf("child exited wist code %d\n",WEXITSTATUS(stat_val)); }else if(WIFSIGNALED(stat_val)) //信号退出,打印出状态码 {printf("signal teminal %d\n",WTERMSIG(stat_val));}}return 0; //这里返回0,会被exit读到;
}
输出结果:
child self id is 6638, parent = 6637
child self id is 6638, parent = 6637
child self id is 6638, parent = 6637
child exited wist code 3
7)pipe的学习,父进程写数据,子进程读数据的案例:
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<unistd.h>
#include <fcntl.h>
#include <wait.h>int main(int argc, char** argv)
{pid_t pid;int fd[2];if(pipe(fd) < 0) //创建管道{perror("pipe");exit(1);}pid = fork(); //创建进程if(pid < 0){perror("fork");exit(1); }//相当于父进程有一个fd[2]数组,子进程还有一个fd[2]数组;if(pid > 0){close(fd[0]); //关闭读端write(fd[1],"hello world\n",11);wait(NULL); //父进程等待子进程结束,给子进程收尸}if(pid == 0){char buf[120];close(fd[1]); //关闭写端;sleep(2); //不着急读,让子弹飞一会int n = read(fd[0],buf,20);// printf("buf is %s",buf);write(1,buf,n); //等价于printf}return 0;
}
//在终端输入,用“echo $?”命令,查看程序的退出状态;
8)popen和pclose的学习
popen函数的功能,创建一个管道,调用fork产生一个子进程,执行一个shell命令来开启一个进程;
8.1)popen read的使用;
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<ctype.h>
int main(int argc, char** argv)
{FILE* fp;fp = popen("ls -l","r"); //r或者w,只能选一种if(!fp){perror("popen");exit(1);}int c;while((c = fgetc(fp)) != EOF) //从终端输出的数据,读到文件fp中,并转成大写打印到屏幕上;{putchar(toupper(c));}pclose(fp);return 0;
}
8.2)popen write的使用;
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<ctype.h>
int main(int argc, char** argv)
{FILE* fp; //前提要有upper这个程序;fp = popen("./upper","w"); //往fp上write,等价于往upper上write;if(!fp){perror("popen");exit(1);}int n = 5;while(n > 0) {n--;fprintf(fp,"hello world,%d\r\n",n);}pclose(fp);return 0;
}
9)有名管道的读写操作
先用mkfifo命令创建一个管道,如mkfifo aa,利用这个aa进行操作;
9.1) fifo有名管道的的读操作
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main(int argc, char** argv) //read
{char buf[20];int fd = open("./aa",O_RDONLY);if(fd < 0){perror("open");exit(1);}ssize_t n = read(fd,buf,20);write(1,buf,n); //屏幕上输出close(fd);return 0;
}9.2) fifo有名管道的的写操作
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main(int argc, char** argv) //read
{int fd = open("./aa",O_WRONLY); //卡主到这里,打开管道的时候if(fd < 0){perror("open");exit(1);}write(fd,"hello world\n",12); //把文件写到fifo中去;close(fd); return 0;
}
上面打开任何一个读操作和写操作,都会阻塞,只有成对出现才可以;
10)共享内存(创建的ipc共享内存,可以用ipcs命令进行查看) ipcs -> ipc show的意思;
ipc相关命令:
ipcs -m,只查看共享内存的;
ipcrm -m 65538(shmid),删除一个共享内存例如:0x63056ad9 32771 jiang 664 20 0
key shmid owner perms bytes nattch status
0x00000000 32768 jiang 600 524288 2 dest
上面两条信息中, perms是权限的意思,nattch->number attch多少个程序共享了;
criteria 美 [kraɪ'tɪriə] 原则,标准
//共享内存简单案例分析:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<sys/ipc.h>
#include<sys/shm.h>
//ipc-共享内存有句柄的意思;
int main(int argc, char** argv)
{key_t key = ftok("./test.txt",99); //key是一个索引值,根据hash算法得到的;printf("key is %#x\n",key);//把用户空间的20个字节,映射到内核空间;int shmId = shmget(key,20,IPC_CREAT | 0664); //向内核申请一块空间,并返回共享内存的id;跟open打开一个文件一样;if(shmId < 0){perror("shmget");exit(1);}printf("shmId = %d\n",shmId); //打印共享内存文件描述符,类似文件描述符fd; //创建粘连关系void* shmp = shmat(shmId,NULL,0); //把创建的共享内存(从内核创建的),映射到我们的用户空间;shmat -> shared memory attach的意思;if(shmp < 0)//不成功{perror("shmat");exit(1); }printf("shmp = %p\n",shmp); //得到内核返回来的空间地址char* cp = (char*)shmp;//下面两条语句是互斥的,相当于两个程序// snprintf(cp,20,"123456\n"); //向共享内存(内核开辟的存储空间)写入数据printf("%s",cp); //读共享内存里面的数据//取消粘连关系shmdt(shmp); //取消映射关系return 0;
}
11)多进程利用共享内存进行通信
shmctl这个函数没有用到,用的是ftok,fork,shmget,shmat,shmdt等系统函数;
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<string.h>
#include<wait.h>//ipc-共享内存有句柄的意思;
int main(int argc, char** argv)
{key_t key = ftok("./test.txt",99); //key是一个索引值,根据hash算法得到的;printf("key is %#x\n",key);//虽然你想开辟一个20byte的大小,但系统还是会给你开辟一个4096大小的空间int shmId = shmget(key,20,IPC_CREAT | 0664); //向内核申请一块空间,并返回共享内存的id;跟open打开一个文件一样;if(shmId < 0){perror("shmget");exit(1);}printf("shmId = %d\n",shmId); //打印共享内存文件描述符,类似文件描述符fd; //创建粘连关系void* shmp = shmat(shmId,NULL,0); //把创建的共享内存(从内核创建的),映射到我们的用户空间;shmat -> shared memory attach的意思;char* charp = (char*)shmp; if(shmp < 0)//不成功{perror("shmat");exit(1); }printf("shmp = %p\n",shmp); //得到内核返回来的空间地址bzero(charp,100); //保证前100个字符是空的pid_t pid = fork();if(pid < 0){perror("fork");exit(1);}if(pid > 0) //父进程{while(1){scanf("%s",charp); //scanf是阻塞读,把读到的数据存放到共享内存中去了if(!strcmp(charp,"quit")){break;}}wait(NULL); //退出后,等待给儿子收尸}else //子进程{while(1){if(!strcmp(charp,"quit")){break;}if(*charp) //如果是空,就等待;不是空,就打印里面的内容;{printf("child read is %s\n",charp);bzero(charp,100);}sleep(1); //禁止高速运转判断}}shmdt(shmp);return 0;
}
12)消息队列(系统内核维护了一个存放消息的队列)
作为一个程序员,应该对边界问题比较敏感!这是一个合格的程序员的标准;
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<string.h>
#include<wait.h>//消息队列,相当于晾衣架那样,一个人把东西一个一个挂上去,另外一个人从另一端拿走;
//发消息相当于把衣服挂到队列上,读消息相当于把衣服拿走;
//消息队列的操作和共享内存的操作,几乎是一样的;
//注意3个系统函数,msgget,msgsnd,msgrcv;
#define msglen 20
typedef struct myMsg
{
long mtype;
char mtext[msglen];
}msg_t;
int main(int argc, char** argv)
{key_t key = ftok("./test.txt",19); //key是一个索引值,根据hash算法得到的;printf("key is %#x\n",key);int mqid = msgget(key,IPC_CREAT | 0666);if(mqid < 0){perror("ipc queue");exit(1);}printf("mqid is = %d\n",mqid);msg_t buf;//下面的发送数据和接收数据其实是两个程序,是互斥的,需要编译两次;//发消息队列到内核空间 // buf.mtype = 1; //存储第一个类型数据// strncpy(buf.mtext,"hello world\n",msglen);// msgsnd(mqid,&buf,msglen,0);// buf.mtype = 2; //存储第二个类型数据// strncpy(buf.mtext,"online\n",msglen);// msgsnd(mqid,&buf,msglen,0); //写完的数据,可以用"ipcs -q"查询数据,是否正确写到消息队列中去了;//从内核空间读数据到用户空间//如果一个类型的消息读完,不同类型的消息也是不能瞎读的,根据类型读,即使有消息,不同类型的消息也是不能读走;//如果没有该类型的消息,就会阻塞到这里;msgrcv(mqid,&buf,msglen,1,0); //取走第1个类型信息,取消息也是根据类型type取消息printf("msg type is: %ld, msg text is: %s",buf.mtype,buf.mtext);msgrcv(mqid,&buf,msglen,2,0); //取走第2个类型信息printf("msg type is: %ld, msg text is: %s",buf.mtype,buf.mtext);return 0;
}//读消息队列的输出结果
key is 0x13056ad9
mqid is = 4
msg type is: 1, msg text is: hello world
msg type is: 2, msg text is: online