Linux进程、线程

在 Linux 系统中, 进程和线程⼏乎没有区别。

进程

⽤ C 语⾔写⼀个 hello 程序, 编译后得到⼀个可执⾏⽂件, 在命令⾏运⾏就可以打印出⼀句 hello world, 然后程序退出。

在操作系统层⾯, 就是新建了⼀个进程, 这个进程将我们编译出来的可执⾏⽂件读⼊内存空间, 然后执⾏, 最后退出。

你编译好的那个可执⾏程序只是⼀个⽂件, 不是进程, 可执行文件必须要载入内存, 包装成⼀个进程才能真正跑起来。 进程是要依靠操作系统创建的,每个进程都有它的固有属性, ⽐如进程号(PID) 、 进程状态、 打开的⽂件等等, 进程创建好之后, 读⼊你的程序, 你的程序才被系统执⾏。

操作系统是如何创建进程的呢? 对于操作系统, 进程就是⼀个数据结构, 直接来看 Linux 的源码:

struct task_struct {
// 进程状态
long state;
// 【mm 指向的是进程的虚拟内存, 也就是载⼊资源和可执⾏⽂件的地⽅】
struct mm_struct *mm;
// 进程号
pid_t pid;
// 指向⽗进程的指针
struct task_struct __rcu *parent;
// ⼦进程列表
struct list_head children;
// 存放⽂件系统信息的指针
struct fs_struct *fs;
// 【files指向⼀个数组, 这个数组⾥装着所有该进程打开的⽂件的指针】
struct files_struct *files;
};

task_struct 就是 Linux 内核对于⼀个进程的描述, 也可以称为「进程描述符」。

files是⼀个⽂件指针数组。 ⼀般来说, ⼀个进程会从 files[0] 读取输⼊, 将输出写⼊ files[1] , 将错误信息写⼊ files[2] 。举个例⼦,  C 语⾔的 printf 函数是向命令⾏打印字符, 但是从进程的⾓度来看, 就是向 files[1] 写⼊数据; 同理, scanf 函数就是进程试图从 files[0] 这个⽂件中读取数据。
每个进程被创建时, files 的前三位被填⼊默认值, 分别指向标准输⼊流、 标准输出流、 标准错误流。 我们常说的「⽂件描述符」 就是指这个⽂件指针数组的索引, 所以程序的⽂件描述符默认情况下 0 是输⼊, 1 是输出,2 是错误。对于⼀般的计算机, 输⼊流是键盘, 输出流是显⽰器, 错误流也是显⽰器,所以现在这个进程和内核连了三根线。 因为硬件都是由内核管理的, 进程需要通过「系统调⽤」 让内核进程访问硬件资源。如果写的程序需要其他资源, ⽐如打开⼀个⽂件进⾏读写, 进⾏系统调⽤, 让内核把⽂件打开, 这个⽂件就会被放到 files 的第 4个位置。同理,输入重定向和输出重定向就是分别把1和2位置指向改成磁盘文件。

管道符也类似, 把⼀个进程的输出流和另⼀个进程的输⼊流接起⼀条「管道」 , 数据就在其中传递。
 

进程v.s.线程

系统调⽤ fork() 可以新建⼀个⼦进程, 函数 pthread() 可以新建⼀个线程。 但⽆论线程还是进程, 都是⽤ task_struct 结构表⽰的, 唯⼀的区别就是共享的数据区域不同。

线程看起来跟进程没有区别, 只是线程的某些数据区域和其⽗进程是共享的, ⽽⼦进程是拷⻉副本,⽽不是共享。 就⽐如说, mm 结构和 files 结构在线程中都是共享的。所以说, 多线程程序要利⽤锁机制, 避免多个线程同时往同⼀区域写⼊数据, 否则可能造成数据错乱。现实中数据共享的并发更普遍!

只有 Linux 系统将线程看做共享数据的进程, 不对其做特殊看待, 其他的很多操作系统是对线程和进程区别对待的, 线程有其特有的数据结构,
 

扫码关注公众号:瑞行AI,欢迎交流AI算法、数据分析等技术,提供技术方案咨询和就业指导服务!

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值