奇怪的shell进程和运行结果
现在有以下代码,注意在子进程的printf之前有一个sleep
#include <stdio.h>
#include <unistd.h>
int main(){
int x=100, p;
printf("x=%d\n", x);
p=fork();
if (p){
x=200;
printf("parent x=%d\n", x);
printf("this is parent, pid=%d\n", getpid());
}else{
sleep(1);
x=300;
printf("child x=%d\n", x);
printf("this is child, pid=%d\n", getpid());
printf("my parent pid is %d\n", getppid());
}
return 0;
}
其运行结果为下图
可以发现在命令提示符出现之后子进程的信息才会输出出来,而且这个时候会不再有一个新的命令提示符。但是这个时候在输入一个ls,仍然会被shell执行。
原因解释
如果修改test.c,在他的父进程中打印一下它的父进程,会发现他的父进程标志号就是shell的进程标志号。
所以可以做以下解释:
当我们从shell中运行./test的时候,shell进程会委托os执行test程序(创建test的父进程),所以shell进程是test父进程的父进程。这个时候shell由于没有输入所以会变成阻塞态。
当父进程fork的时候又委托os创建他的子进程,然后父进程会先变成阻塞态。当子进程创建完成后,子进程变成就绪态被排在就绪队列中,os进程创建完子进程后变为阻塞态这一事件会触发父进程变为就绪态,排进就绪队列,(这个时候父进程在子进程的后面)。
由于子进程sleep了1秒的时间,这时父进程早就执行完后被os回收。
这就导致shell进程从阻塞态变为就绪态,然后又转为运行态并输入命令提示符。再之后shell进程又变为阻塞态。
重点来了: 这个时候子进程sleep完后要打印信息就会打印在shell的命令提示符后,就出现了上图的结果。
打印完后子进程也被os回收。这个时候shell进程仍然在阻塞态,所以这个时候如果在输入一个ls命令,尽管不是紧跟在命令提示符后面输入的,但是依然会被执行。
换行符
linux下printf()是以"行"来做缓冲区来刷新stdout的,如果遇到’\n’会强制立即刷新;
否则刷新可能会延迟。当printf()中没有包含’\n’,内核决定满一行再刷新.
可以把printf("%c\n", 'A'+i);
换成printf("%c", 'A'+i); fflush(stdout);
这里贴两个个老师给的链接,感觉写的真心好
https://mp.weixin.qq.com/s/l244huNdyfABYmvF0LmdZg
https://mp.weixin.qq.com/s/jXS7mAPG5FScn68miXlI7Q
多进程并发
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(){
pid_t pid, pid1;
pid = fork();
if (pid<0){
perror("fork failed\n");
exit(-1);
}else if (pid==0){ //child_1
printf("child1 pid is %d\n", getpid());
int i;
for (i=0; i<26; i++){
sleep(1);
printf("%c\n", 'A'+i);
}
}else{ //parent (pid>0)
pid1=fork();
if (pid1<0){
perror("fork failed\n");
exit(-1);
}else if (pid1==0){ //child_2
printf("child2 pid is %d\n", getpid());
int i;
for (i=0; i<26; i++){
sleep(1);
printf("%c\n", 'a'+i);
}
}else{ //parent pid>0 && pid1>0
printf("parent pid is %d\n", getpid());
int i;
for (i=1; i<=26; i++){
sleep(1);
printf("%d\n", i);
}
}
}
return 0;
}
运行结果如下
可以看到两组的输出顺序是不一样的,这是因为尽管在某一时刻我们可以很清楚的看到多个就绪进程在就绪队列中的排列顺序,但是当某个队首的进程在刚要执行的时候如果被其他进程抢占,那么这个进程就会被安排倒就绪队列的末尾。从而导致输出在其他的两个进程后面。
所以对于多进程并发的观察应该是在一段时间内,如果只是看某一个时刻是没有意义的。
多进程并发框架
exec1.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define SIZE 1024
int main(int argc, char* argv[]){
int p;
char *arg[]={"./exec3", "i am child", NULL};
p=fork();
if (p){
//parent
wait(0);
execl("./exec2", "i am parent", NULL);
}else{
//child
execv("./exec3", arg);
}
return 0;
}
exec2.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define SIZE 1024
int main(int argc, char* argv[]){
int key, shmid;
char *shmaddr=NULL;
printf("argv[0]=%s, argv[1]=%s\n", argv[0], argv[1]);
key=ftok("/boot", 'x');
shmid=shmget(key, SIZE, IPC_CREAT|0600);
shmaddr=(char*)shmat(shmid, NULL, 0);
strcpy(shmaddr, "this is a message");
shmdt(shmaddr);
return 0;
}
exec3.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define SIZE 1024
int main(int argc, char* argv[]){
int key, shmid;
char *shmaddr=NULL;
printf("argv[0]=%s, argv[1]=%s\n", argv[0], argv[1]);
key=ftok("/boot", 'x');
shmid=shmget(key, SIZE, IPC_EXCL);
shmaddr=(char*)shmat(shmid, NULL, 0);
printf("%s\n", shmaddr);
shmdt(shmaddr);
shmctl(shmid, IPC_RMID, NULL);
return 0;
}
说明:
我们通常在linux下执行一个ls-l的时候,相当于执行了execl("/bin/ls", “-l”, NULL);