fork进程

任务队列是一个双向循环链表


产生进程机制:fork() + exec() //exec:负责读取可执行文件并将其载入地址空间中运行


fork()使用写时拷贝实现, 只有在需要写入时,数据才会被复制,从而使各个进程拥有各自的拷贝


fork()之后,子进程先执行(内核有意选择子进程先执行),因为一般子进程都会马上调用exec()函数,这样可以避免写时拷贝的额外开销,如果父进程首先执行,有可能会开始向地址空间写入。
子进程开始执行后,父进程不是马上恢复执行,而是一直等待,直到子进程通过vfork_done指针向他发送信号


Linux 把所有的线程都当做进程来实现(分配task_struct...,没有专门的数据结构和调度算法等),所以线程仅仅被视为一个与其他进程共享某些资源的进程


线程的创建和进程创建类似, 只不过在clone()时需要添加对应的参数标志


内核线程(一般用于后台一些操作)只能由其他内核线程创建,创建方法 kthread_create()、kthread_run(),两者之间区别后者一创建就开始运行,前者需要显示调用wake_up_process()唤醒
### fork 进程间通信方法 `fork()` 创建的新进程(子进程)与原进程(父进程)共享许多资源,但由于它们是独立的进程,因此需要某种机制来实现两者之间的通信。以下是常见的 `fork()` 子进程与父进程之间的通信方式: #### 1. **管道 (Pipe)** 管道是一种简单的单向通信机制,适用于父子进程间的通信。创建管道后,一端用于写入数据,另一端用于读取数据。需要注意的是,默认情况下,匿名管道仅限于具有共同祖先的进程之间使用。 ```c #include <unistd.h> #include <stdio.h> int main() { int pipefd[2]; pid_t cpid; char buf; if (pipe(pipefd) == -1) { perror("pipe"); return 1; } cpid = fork(); if (cpid == -1) { perror("fork"); return 1; } if (cpid == 0) { // 子进程 close(pipefd[1]); // 关闭写端 read(pipefd[0], &buf, 1); printf("Child received %c\n", buf); close(pipefd[0]); } else { // 父进程 close(pipefd[0]); // 关闭读端 char data = 'A'; write(pipefd[1], &data, 1); close(pipefd[1]); } } ``` 上述代码展示了如何利用管道让父进程向子进程传递字符 `A`[^1]。 --- #### 2. **命名管道 (FIFO)** 命名管道类似于匿名管道,但它提供了一个文件路径名作为接口,允许可无亲缘关系的进程进行通信。由于其基于文件系统的特性,命名管道可以跨越不同的进程树。 ```bash mkfifo my_fifo echo "Message from parent" > my_fifo & cat my_fifo ``` 此示例说明了如何通过命令行操作 FIFO 文件完成简单通信。 --- #### 3. **信号 (Signal)** 信号是一种异步通知机制,可用于提醒目标进程发生特定事件。虽然信号本身不携带大量数据,但可以通过触发某些动作间接实现进程间交互。 ```c #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> void handler(int sig) { printf("Received signal %d\n", sig); } int main() { struct sigaction sa; sa.sa_handler = handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGUSR1, &sa, NULL); if (fork()) { // Parent process sends a signal to child. sleep(1); kill(getppid(), SIGUSR1); } else { // Child waits for the signal. pause(); exit(EXIT_SUCCESS); } } ``` 该程序演示了父进程向子进程发送自定义信号 `SIGUSR1` 的过程。 --- #### 4. **共享内存 (Shared Memory)** 共享内存是最高效的 IPC 方法之一,因为它允许两个或多个进程访问同一块物理内存区域。通常会结合信号量或其他同步工具一起使用以防止竞争条件。 ```c #include <sys/ipc.h> #include <sys/shm.h> #include <string.h> #include <stdio.h> #define SHM_SIZE 1024 /* Shared memory segment size */ int main() { key_t key = ftok("/tmp", 65); int shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0666); void *shm = shmat(shmid, NULL, 0); strcpy((char *)shm, "Hello from shared memory"); printf("%s\n", (char *)shm); shmdt(shm); shmctl(shmid, IPC_RMID, NULL); } ``` 这里展示了一种基本的方式,在其中分配并初始化一块共享内存区[^2]。 --- #### 5. **套接字 (Socket)** 尽管主要用于网络通信,但在本地机器上也可以借助 Unix 域套接字实现父子进程间的高效交流。这种方式特别适合复杂场景下的跨平台应用开发。 ```c #include <sys/socket.h> #include <sys/un.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main() { int sockfd, new_sock; struct sockaddr_un addr; socklen_t addrlen = sizeof(addr); unlink("/tmp/sock"); memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, "/tmp/sock", sizeof(addr.sun_path)-1); sockfd = socket(AF_UNIX, SOCK_STREAM, 0); bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)); listen(sockfd, SOMAXCONN); if (!fork()) { // Child accepts connection and reads message. new_sock = accept(sockfd, (struct sockaddr *)&addr, &addrlen); char buffer[1024]; recv(new_sock, buffer, sizeof(buffer), 0); printf("Child got '%s'\n", buffer); close(new_sock); exit(0); } new_sock = connect_to_server("/tmp/sock"); send(new_sock, "Parent says hi!", strlen("Parent says hi!")+1, 0); close(new_sock); } ``` 这个例子表明即使是在同一个系统内部也能采用套接字来进行可靠的双向通讯[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值