10.09
注意:这个是Linux高级编程的简明教程,是Linux应用程序的开发,而不是底层程序的开发。
内容是关于操作系统和网络编程的吗?
#include //fork函数、wait函数、mkfifo函数(有名管道)、msgget函数(消息队列)、kill函数(发送信号signal)
#include //fork函数、pipe函数
#include //mkfifo函数(有名管道)、sem_open函数
#include //wait函数、waitpid函数
#include //pthread_create函数(创建线程)、几个互斥锁函数pthread_mutex_init函数、pthread_mutex_lock函数、pthread_mutex_unlock函数
#include //read、open、write、close文件、sem_open函数
#include //shmget函数
#include //shmget函数
#include //msgget函数
#include //msgget函数
#include //sem_init函数、sem_wait函数、sem_post函数、sem_open函数、sem_close函数
#include //mmap函数
#include //kill函数(发送信号signal)、signal函数()
#include
using namespace std;
int main(){
...
return 0;
}
视频课就来自于嵌入式技术公开课公众号的老师讲的一个课程,名称就叫Linux简明系统编程;
这个CSDN专栏写的很好:linux系统编程。
程序:
进程:
线程:
任务:
每个进程都有一个进程号,叫pid(process id),
man getpid //查询手册
-p表示pid,即进程号。
pstree -p
fork()函数:返回值依然是pid_t
类型。
man fork
通过复制当前的进程,创建一个新进程,新的进程是当前进程的子进程。
示例:
#include
#include
#include
using namespace std;
int main(){
pid_t pid;
pid = fork();
cout << "pid = " << pid << endl;
cout << "hello, world" << endl;
return 0;
}
结果:
//父进程返回的内容:
pid = 1200482
hello, world
//子进程返回的内容:
pid = 0
hello, world
解释:
父进程返回的是子进程的PID,子进程返回0;
所以说上面的1200482是新创建的子进程的PID,这个信息是由父进程来返回的。
程序修改一下:
#include
#include
#include
using namespace std;
int main(){
// pid_t pid;
// pid = fork();
// cout << "pid = " << pid << endl;
// cout << "hello, world" << endl;
pid_t pid1, pid2;
pid1 = fork();
pid2 = fork();
cout << "pid1 = " << pid1 << "; pid2 = " << pid2 << endl;
return 0;
}
结果会是什么呢?
pid1 = 0; pid2 = 0 //进程D(拷贝进程B,所以和B的pid1相同)
pid1 = 0; pid2 = 1204505 //进程B
pid1 = 1204503; pid2 = 1204504 //进程A
pid1 = 1204503; pid2 = 0 //进程C(拷贝进程A,所以和A的pid1相同)
进程A的pid未知;进程B的pid为1204503;
进程C的pid为1204504;进程D的pid为1204505;
解释:
以下内容参考链接:https://www.csdn.net/tags/OtDakg2sMTU0OTItYmxvZwO0O0OO0O0O.html
程序1:
#include
#include
#include
using namespace std;
int main(){
pid_t pid;
pid = getpid();
fork();
cout << "pid = " << pid << "; getpid() = " << getpid() << endl;
//pid为父进程的进程ID号,而getpid()是获取当前进程的ID号。
return 0;
}
结果:
pid = 1336259; getpid() = 1336259 //父进程
pid = 1336259; getpid() = 1336264 //子进程
由于fork函数创建了一个新的子进程,所以fork函数后的语句会执行两次,也就是打印两次进程的pid号。因为cout语句第一次运行在父进程,所以两个pid的值相等,但cout语句第二次运行在子进程,所以两个pid就不相等了,也就是说此时的pid2是子进程的进程ID。
程序2:
#include
#include
#include
using namespace std;
int main(){
pid_t pid;
pid = getpid();
cout << "before fork, pid = " << getpid() << endl;
fork();
cout << "after fork, pid = " << getpid() << endl;
//cout << "pid = " << pid << "; getpid() = " << getpid() << endl;
if(getpid() == pid) cout << "这是父进程,此时的pid = " << getpid() << endl;
else cout << "这是子进程,此时的pid = " << getpid() << endl;
return 0;
}
结果:
before fork, pid = 1356246
after fork, pid = 1356251
这是子进程,此时的pid = 1356251
after fork, pid = 1356246
这是父进程,此时的pid = 1356246
程序3:fork函数的返回值
#include
#include
#include
using namespace std;
int main(){
pid_t pid;
//pid = getpid();
cout << "before fork, pid = " << getpid() << endl;
pid = fork();
//cout << "after fork, pid = " << getpid() << endl;
//cout << "pid = " << pid << "; getpid() = " << getpid() << endl;
if(pid == 0) cout << "这是子进程,此时的pid = " << getpid() << endl;
else cout << "这是父进程,此时的pid = " << getpid() << endl;
return 0;
}
结果:
before fork, pid = 1356723
这是子进程,此时的pid = 1356728
这是父进程,此时的pid = 1356723
参考链接2:
这篇讲的也很好,可以直接看这篇博客 linux中fork函数及子进程父进程执行顺序
参考链接3:
操作系统fork()进程
pid_t pid;
pid = fork();
cout << "pid = " << pid << endl;
fork是返回两个值:
一个代表父进程:代表父进程的值是一串数字,这串数字是子进程的ID(地址);
一个代表子进程:返回值为0。
多次手动运行这个程序,会发现以下两个结果:
//第一种结果:
pid = 1358110
pid = 0
//第二种结果:
pid = 0
pid = 1358828
说明fork函数之后先运行父进程还是子进程,是不确定的,父子进程在争用系统资源,看谁先执行。
参考链接4:
fork() && fork() || fork();会产生几个子进程
关键在下面的理解:
上面的参考链接3中的程序:
注意:不能通过判断谁先打印就说谁先执行,因为打印函数本来就是个很复杂的过程,并不能说先打印出父进程的pid就说先执行的父进程。
宏观上来说是同时执行;
微观上来说是交替进行的;
这就是并发;计算机操作系统笔记—并行和并发的区别
Linux2.6之后默认是父进程先调用,因为父进程一直处于活跃状态;并且父子进程谁先执行带来的影响几乎可以忽略不计,如果一定要先让谁运行,后让谁运行,这就涉及到后面要说的同步问题,同步就是让程序按照人为设定的顺序去执行。
问:执行一次fork函数 先运行子进程还是先运行父进程?
答:(闫波)
没有顺序的
为了可以同步(同步就是有顺序),所以引入了信号量之类的东西;
执行同步就是让程序以一个确认的顺序运行,其实自己项目多线程用的多,包括咱们科研的,进程环境切换代价太大了,一般都是多线程;
现在电脑也是,8核16线程
这种,线程用的多,高并发也是线程;
参考链接:Linux C++多线程同步的四种方式
让子进程和父进程都执行一个for循环,子进程执行20次,父进程执行10次
#include
#include
#include
using namespace std;
int main(){
pid_t pid;
int count = 0;
pid = fork();
if(pid == 0){
for(int i = 0; i < 20; ++i){
//while(1)
++count;
cout << "子进程:执行次数" << count << endl;
sleep(2);
}
}
else if(pid > 0){
for(int i = 0; i < 10; ++i){
//while(1)
++count;
cout << "父进程:执行次数" << count << endl;
sleep(2);
}
}
else
perror("fork");
return 0;
}
结果是父子进程各自值行自己的++count
和cout
操作,所以父子进程之间是相互独立的,互不影响;
另外当父进程执行10次之后,子进程会继续执行,直到打印完20次才停止,也就是说父进程的结束并不会导致子进程也停止。
注意,不要写上面的那个while(1),那样会导致子进程根本停不下来,Ctrl + C
也没用。
man 2 wait
所有这些系统调用都用于等待调用进程的子进程的状态更改,并获取状态已更改的子进程的信息。
wait()系统调用暂停调用线程的执行,直到它的一个子线程终止。
waitpid()系统调用暂停调用线程的执行,直到pid参数指定的子线程改变状态。默认情况下,waitpid()只等待终止的子进程。
返回值:
wait():如果成功,返回被终止子进程的pid
;发生错误时,返回-1
。
waitpid():如果成功,返回状态改变的子进程的pid
;如果指定了WNOHANG,并且pid指定的一个或多个子进程(ren)已经存在,但是还没有改变状态,则返回0。发生错误时,返回-1。
示例:
创建三个子进程,分别在5秒、10秒、15秒之后结束,父进程一直等待,直到所有子进程都运行结束,父进程才停止运行,在这个过程中,父进程一直监控几个子进程的运行状态。
代码:
#include
#include
#include
#include
using namespace std;
int main(){
int arr[4] = {
0, 10, 5, 15};
pid_t child_pid;
for(int i = 1; i <= 3; ++i){
switch (fork())
{
case -1:
perror("fork");
exit(0);//break;
case 0:
cout << "子进程 " << i << " 已创建,pid = " << getpid() << ",ppid = " << getppid() << ",sleeping 时长为 " << arr[i] << "秒。" << endl;
sleep(arr[i]);
exit(0);//break;
default:
break;
}
}
int numDead = 0;
while(true){
child_pid = wait(nullptr);//等待子进程结束
if(child_pid < 0){
cout << "子进程都结束了,再见!" << endl;
break;
}
++numDead;
cout << "wait()函数返回了 pid = " << child_pid << "的子进程,它是第 " << numDead << " 个结束的子进程;" << endl;
}
return 0;
}
结果:
子进程 1 已创建,pid = 4168680,ppid = 4168679,sleeping 时长为 10秒。
子进程 2 已创建,pid = 4168681,ppid = 4168679,sleeping 时长为 5秒。
子进程 3 已创建,pid = 4168682,ppid = 4168679,sleeping 时长为 15秒。
wait()函数返回了 pid = 4168681的子进程,它是第 1 个结束的子进程;
wait()函数返回了 pid = 4168680的子进程,它是第 2 个结束的子进程;
wait()函数返回了 pid = 4168682的子进程,它是第 3 个结束的子进程;
子进程都结束了,再见!
程序:源代码,指令,程序是静态的概念,比如一个安装包,就存放在电脑磁盘里,不进行任何操作
进程:正在执行的程序的实例,是程序的动态的概念,比如qq和微信是两个独立的进程。
1.进程的创建和销毁会带来很大的资源消耗;
2.每个进程都有独立的进程号PID
;
3.注意:进程之间是相互独立的;
4.注意:套接字中的端口号和进程号不是一回事;
套接字Socket = (IP地址:端口号)
端口号和进程号的关系:
线程:线程从属于进程,一个进程可以有多个线程,线程之间共享
进程的资源;
max pthread_create
头文件:
#include
四个参数:
pthread_t *threak
//pthread_t* 类型,表示线程ID号,这里写某个pthread_t类型变量的地址
const pthread_attr_t *attr,
//线程结构体指针类型,一般写null
void *(*start routine)(void *)
//函数指针类型,函数是指某个线程函数,函数的参数是void*,返回值也是void*,这里写函数的地址,即函数名
void *arg
//传递给线程函数的参数,一般写null
返回值:int类型,如果成功创建了一个线程,就返回0
;否则返回一个错误的数字。
示例:
#include
#include
#include
#include
using namespace std;
//线程函数:
void* thread_func(void* arg){
return nullptr;
}
int main(){
pthread_t pthread;//线程ID号
int res;
res = pthread_create(&pthread, nullptr, thread_func,