目录
如何判断一个程序是进程
bash
$ ps axj | head -1 ; ps axj | grep myprocess
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
8032 9193 9193 8032 pts/2 9193 S+ 1004 0:00 ./myprocess
获得进程的pid值
使用getpid函数获得进程本身的id
使用<sys/types.h>和<unistd.h>
c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
while(1)
{
printf("I am a process, pid: %d\n", getpid());
sleep(1);
}
return 0;
}
使用getppid获得父进程的pid
使用<sys/types.h>和<unistd.h>
c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
while(1)
{
printf("I am a process, pid: %d, ppid: %d\n", getpid(), getppid());
sleep(1);
}
return 0;
}
何为父进程?
Linux系统中,新的进程,往往通过父进程的方式创建出来的
bash
$ ./myprocess
I am a process, pid: 9986, ppid: 8032
$ ./myprocess
I am a process, pid: 9997, ppid: 8032
$ ./myprocess
I am a process, pid: 10001, ppid: 8032
可以发现,父进程一直不变
查询发现,该进程是-bash
bash
$ ps axj | head -1 && ps axj | grep 8032
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
8031 8032 8032 8032 pts/2 10138 Ss 1004 0:00 -bash
结论:自己写的程序,会交给命令行解释器创建进程
-bash(命令行解释器)是命令行中执行所有程序的父进程
如何创建子进程?
创建子进程,本质是OS内部多了一个子进程(task struct + 代码和数据)
但是这个过程是一个系统调用
使用fork进行创建子进程
textile
NAME
fork - create a child process
SYNOPSIS
#include <unistd.h>
pid_t fork(void);
c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
printf("I am a process, pid: %d, ppid: %d\n", getpid(), getppid());
fork();
printf("I am a process(fork), pid: %d, ppid: %d\n", getpid(), getppid());
sleep(1);
return 0;
}
bash
$ ./myprocess
I am a process, pid: 15728, ppid: 8032
I am a process(fork), pid: 15728, ppid: 8032
I am a process(fork), pid: 15729, ppid: 15728
可以发现,后面一句printf居然被打印了两次
因为原本的进程15728在fork();这句时创建了一个子进程15729
此时,两个进程会从fork();这行继续往下执行
因此出现了这种打印结果
c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
printf("I am a process, pid: %d, ppid: %d\n", getpid(), getppid());
fork();
while(1)
{
printf("I am a process(fork), pid: %d, ppid: %d\n", getpid(), getppid());
sleep(1);
}
return 0;
}
bash是怎么创建子进程的?
bash
$ which bash
/usr/bin/bash
$ ldd /usr/bin/bash
linux-vdso.so.1 => (0x00007fff1d9ec000)
libtinfo.so.5 => /lib64/libtinfo.so.5 (0x00007ffaf79f2000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007ffaf77ee000)
libc.so.6 => /lib64/libc.so.6 (0x00007ffaf7420000)
/lib64/ld-linux-x86-64.so.2 (0x00007ffaf7c1c000)
可以发现bash是用c语言写的,因此内部也是通过fork创建子进程
在/proc文件夹中查看进程
bash
$ ls /proc/
1 12 17065 2016 22977 25238 26366 278 296 38 5001 541 595 7475 9021 buddyinfo devices fs keys mdstat pagetypeinfo softirqs timer_stats
10 12328 17374 20986 22987 25699 26377 28 297 386 501 5726 596 771 9029 bus diskstats interrupts key-users meminfo partitions stat tty
10387 13 18 21 23 25702 27 280 29732 39 509 5733 5973 8 904 cgroups dma iomem kmsg misc sched_debug swaps uptime
10395 14 19 22 232 25703 270 28881 3 47 51 5745 5994 835 9040 cmdline driver ioports kpagecount modules schedstat sys version
10406 16 19920 22605 24 25747 271 289 36 49 52 577 65 837 927 consoles execdomains irq kpageflags mounts scsi sysrq-trigger vmallocinfo
1089 16640 2 22641 25 26 272 28900 365 5 536 580 7 839 96 cpuinfo fb kallsyms loadavg mtrr self sysvipc vmstat
11 16659 20 22663 25226 26359 275 29 37 50 538 584 7468 9 acpi crypto filesystems kcore locks net slabinfo timer_list zoneinfo
其中,新建一个进程就会新建一个文件夹(名称同pid)
进程消失后,文件夹消失
文件夹内部是各种属性的记录,其中一个重要属性是cwd(currect work diratore)
这个属性会记录当前进程所处的工作路径(可执行文件的路径)
编程时所说的当前路径就是通过cwd来获取的
c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
FILE *fp = fopen("log.txt", "w");
if(fp == NULL)
{
perror("fopen");
}
fclose(fp);
return 0;
}
如何证明:
使用chdir更改cwd
c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
chdir("/home/suliiik/bite");
FILE *fp = fopen("log.txt", "w");
if(fp == NULL)
{
perror("fopen");
}
fclose(fp);
return 0;
}
fork的返回值
前面我们没有获取fork的返回值,因此两个进程执行接下来的代码块,没有意义
因此要进行分流
text
fork
(fork有两个返回值)
如果成功,子进程的pid会返回给父进程,0被返回给子进程;
如果失败,-1被返回给父进程,没有子进程被创建
c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
printf("I am a process, pid: %d, ppid: %d\n", getpid(), getppid());
pid_t id = fork();
if(id < 0)
{
return 1;
}
else if(id == 0)
{
//子进程
while(1)
{
printf("I am a child process, pid: %d, ppid: %d\n", getpid(), getppid());
sleep(1);
}
}
else
{
//父进程
while(1)
{
printf("I am a parent process, pid: %d, ppid: %d\n", getpid(), getppid());
sleep(1);
}
}
return 0;
}
返回值用于做父子分流

为什么给子进程返回的是0,给父进程返回的是子进程的pid
父进程:子进程=1:n
子进程只需要表明自己是否创建成功即可
给父进程返回标识指定的一个子进程,方便未来控制特定的子进程
fork()是函数,为什么能返回两次?
当一个函数已经准备return了,相当于这个函数的核心功能已经完成了
fork之后,return之前,父子进程已经存在了
也就是说,父子都需要执行return
所以就能返回两次
一个id,为什么能接收两个不同的值?既==0,又>0?
这个问题暂时无法深入解答,要学习虚拟地址空间后才能理解
因为两个进程在物理内存中使用同一块内存(共享)
但是一旦某一方需要对某块内存进行修改,操作系统就会对拷贝一块新的空间(写时拷贝)

因此两个进程的id变量,虽然名称相同,但是实际物理地址不同