Process API Homework CC=gcc GCCFLAGS= -g -Wall TARGET = $(word 1,$(MAKECMDGOALS) ) SRC=$(word 1,$(MAKECMDGOALS) ) .c $(TARGET) :$(SRC) $(CC) $(GCCFLAGS) $(SRC) -o $(TARGET)
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> int main () { int x = 1 ; printf ("%d\n" , x); x = 2 ; pid_t rc = fork(); if (rc < 0 ) { fprintf (stderr , "fork failed\n" ); exit (1 ); } else if (rc == 0 ) { printf ("fork %d\n" , x); x = 3 ; printf ("fork %d\n" , x); } else { wait(NULL ); printf ("%d\n" , x); } }
grxer@grxer /m/h/S/O/o/cpu-api> make 1 gcc -g -Wall 1.c -o 1 1.c: In function ‘main’: 1.c:20:9: warning: implicit declaration of function ‘wait’ [-Wimplicit-function-declaration] wait(NULL); ^ grxer@grxer /m/h/S/O/o/cpu-api> ./1 1 fork 2 fork 3 2
子进程和父进程变量没有任何关系
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> int main () { int fd = open("./flag" ,O_RDWR|O_APPEND); assert(-1 != fd); int pid = fork(); if (pid < 0 ) { fprintf (stderr , "fork failed\n" ); exit (1 ); } else if (0 ==pid){ char buf[100 ] = { 0 }; read(fd, buf, sizeof (char ) * 10 ); printf ("child:%s\n" , buf); memcpy (buf, "child" , strlen ("child" )); for (size_t i = 0 ; i < 10 ; i++) { int err = write(fd, buf, strlen (buf) * sizeof (char )); assert(err); } } else { char buf[100 ] = { 0 }; read(fd, buf, sizeof (char ) * 10 ); printf ("parent:%s\n" , buf); memcpy (buf, "parent" , strlen ("parent" )); for (size_t i = 0 ; i < 10 ; i++) { int err = write(fd, buf, strlen (buf) * sizeof (char )); assert(err); } wait(NULL ); } }
grxer@grxer /m/h/S/O/o/cpu-api> cat flag 1111111111xxxxxxxxxxx grxer@grxer /m/h/S/O/o/cpu-api> ./2 parent:1111111111 child:xxxxxxxxxx grxer@grxer /m/h/S/O/o/cpu-api> cat flag 1111111111xxxxxxxxxxx parent1111parent1111childxxxxxchildxxxxxparent1111parent1111childxxxxxchildxxxxxparent1111parent1111childxxxxxparent1111parent1111childxxxxxchildxxxxxparent1111parent1111childxxxxxchildxxxxxchildxxxxx⏎
可以看出父子是共享文件描述符的,父进程读后文件的读写位置往后移到了xxxxx,可以用 lseek(fd, 0, SEEK_SET);
把他移到开头
并发的写入由于操作系统调度顺序问题,写入顺序是随机的,竞争关系的他们并不能同时使用fd,操作系统应该加了一些锁
#include <unistd.h> #include <stdio.h> #include <stdlib.h> int main () { int x = 10 ; int pid = vfork(); if (pid < 0 ) { fprintf (stderr , "fork failed\n" ); exit (1 ); } else if (0 == pid) { x = 66 ; printf ("hello " ); exit (0 ); } else { printf ("world-----" ); printf ("%d" ,x); } }
grxer@grxer /m/h/S/O/o/cpu-api> make 3 gcc -g -Wall 3.c -o 3 grxer@grxer /m/h/S/O/o/cpu-api> ./3 hello world-----66⏎
可以调用vfork函数,和fork函数的区别就是他会阻塞父进程,直到子进程调用exec或exit才恢复调度可能,在此之前和父进程共享所以内存包括栈!可以看的x已经被修改
#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main (int argc, char * argv[], char * envp[]) { int pid = fork(); char * cmd = "/bin/ls" ; char * name = "ls" ; char * arg[] = { "ls" , "-a" , "-l" , "-h" , NULL }; if (pid < 0 ) { perror("error" ); exit (1 ); } else if (pid == 0 ) { execlp(name, "ls" ,"-a" , "-l" , "-h" ,NULL ); } else { } }
以exec为前缀
l(list)使用参数地址列表,以空指针结束标志execl("/bin/ls", "ls", "-a", "-l", "-h", NULL);
。第二个参数ls没有太大意义,如果要给ls传参必须写上这个字符串,
v(vector) 以 NULL结尾字符串数组的指针作参数
p(path) PATH环境变量指定的目录搜索可执行文件,可以不用写路径
e(environment) 可以传存有环境变量envp字符串地址的指针数组的地址,无后缀e的话使用当前进程环境变量
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/wait.h> #include <assert.h> int main () { int pid = vfork(); if (pid < 0 ) { fprintf (stderr , "fork failed\n" ); exit (1 ); } else if (0 == pid) { puts ("gr" ); int rs = wait(NULL ); printf ("%d\n" , rs); exit (0 ); } else { int rs = wait(NULL ); assert(pid == rs); } }
rxer@grxer /m/h/S/O/o/cpu-api> make 5 gcc -g -Wall 5.c -o 5 grxer@grxer /m/h/S/O/o/cpu-api> ./5 gr -1
父进程wait成功等待了返回子进程id,否则-1,子进程返回-1
其他更精细的事情,比如非阻塞等待:希望子进程退出能够被我父进程检测到,同时我又不希望我父进程处于阻塞等待waitpid(pid,NULL,WNOHANG)
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/wait.h> #include <assert.h> int main () { int pid = fork(); if (pid < 0 ) { fprintf (stderr , "fork failed\n" ); exit (1 ); } else if (0 == pid) { printf ("child hello" ); close(STDOUT_FILENO); printf ("grxer" ); exit (0 ); } else { printf ("parent : hello" ); wait(NULL ); printf (" world\n" ); } }
grxer@grxer /m/h/S/O/o/cpu-api> make 7 gcc -g -Wall 7.c -o 7 grxer@grxer /m/h/S/O/o/cpu-api> ./7 parent : hello world
没有输出的同时可以看出子进程继承了文件描述符,但是继承过后就是私有的了,不会影响父进程
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <string.h> #include <assert.h> #include <sys/wait.h> int main () { pid_t pid[2 ]; int pidfd[2 ]; int res = pipe(pidfd); assert(0 == res); pid[0 ] = fork(); if (pid[0 ] < 0 ) { perror("fork1 fail" ); exit (-1 ); } else if (0 == pid[0 ]) { close(pidfd[0 ]); assert(dup2(pidfd[1 ], STDOUT_FILENO) == STDOUT_FILENO); printf ("i am from pid[0]" ); close(pidfd[1 ]); } else { pid[1 ] = fork(); if (pid[1 ] < 0 ) { perror("fork2 fail" ); exit (-2 ); } else if (0 == pid[1 ]) { char buf[30 ] = {0 }; close(pidfd[1 ]); assert(dup2(pidfd[0 ], STDIN_FILENO) == STDIN_FILENO); read(STDIN_FILENO,buf,30 ); printf ("pid[1]:where are you from??\n %s" ,buf); close(pidfd[0 ]); } else { waitpid(pid[0 ], NULL , 0 ); waitpid(pid[1 ], NULL , 0 ); } } }
grxer@grxer /m/h/S/O/o/cpu-api> make 8 gcc -g -Wall 8.c -o 8 grxer@grxer /m/h/S/O/o/cpu-api> ./8 pid[1]:where are you from?? i am from pid[0]⏎
#include <unistd.h> int pipe (int pipefd[2 ]) ;成功返回0 失败返回-1
管道:半双工通道,只允许数据在一个方向上传输的通道。发送方和接收方不能同时发送数据,只能轮流进行发送和接收。用于进程间通信
pipe函数创建一个管道,pipefd返回管道两端的文件描述符,pipefd[0]是读端,pipefd[1]是写端
管道可以理解为进程共用的内核空间里的一块内存来充当虚拟文件
#include <unistd.h> int dup (int oldfd) ;int dup2 (int oldfd, int newfd) ;On success, these system calls return the new descriptor. On error, -1 is returned, and errno is set appropriately.
dup函数创建一个oldfd文件描述符的副本(而且是动态的副本,本质上是一个,只不过起了不同名字),不过不共享文件描述符标志,返回新文件描述符是取系统当前可用的最小整数值
dup2 和dup功能一样,不过它可以用newfd指定返回的新文件描述符号,如果newfd是一个已经打开的描述符,会把他先关闭