linux系统编程之三
- 手机
- 2025-08-16 14:12:04

一)进程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 = 194683)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; 因为上面关闭了,所以还是3 close(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 37)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: onlinelinux系统编程之三由讯客互联手机栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“linux系统编程之三”
上一篇
Linux考试复习整理
下一篇
BootStrap-前端框架