1. 进程操作

1.1 环境变量

1.1.1 关于环境变量 修改方式

image.png

1.1.2 环境变量加载的顺序

image.png

1.1.3 Linux下操作环境变量的函数

include
//这个全局变量是直接导出的,能够直接访问到所有的环境变量
extern char **environ;

void showallenviron()
{
for (char *ptr = environ; ptr != NULL; ++ptr)
puts(ptr);
}
//设置环境变量
void getenvirton()
{
// 设置环境变量 key:value
setenv(“TEST”,”12345678”,1);
// 获取环境变量内容
char
test = getenv(“TEST”);
puts(test);
}
int main()
{
//showallenviron();
getenvirton();
return 0;
}

1.2 进程相关

1.2.1 基本信息

进程对象:PCB
结构体名:task_struct
内存布局:
image.png

1.2.2 命令

  1. ps命令 ps -aux

image.png
如果想要查看某个进程的信息
grep 进行一个过滤
image.png
上面这个命令,不是动态

  1. Linux版本的任务管理器-top命令

image.png

  1. 另外一个比较好用的进程查看工具

htop 默认是不安装的
apt-get install htop
image.png
如果想要结束一个进程
按 F9 选择9号 结束进程
也可以

  1. 结束进程

kill -9 进程的ID 也可以
killall 进程名

  1. 查看进程树

image.png

1.2.3 proc文件系统

image.png
我们去1645中查看
image.png
这些文件就是进程相关的信息
比如:
cat maps 查看进程的内存布局
image.png
cat cmdline查看命令行参数
image.png
cat status 查看进程的一些状态信息
image.png
还有一个进程信息的查看方式
cat stat 对于我们来说 可读性比较差 对于软件来说,可以按照一定的格式去读取他们
image.png

1.2.4 遍历进程的思路

我们可以遍历 proc目录下的 所有数字文件夹,然后就能够得到所有的进程ID,之后,进入ID文件夹,通过读取文件,就能够获取到进程的信息。
我们可以参考PS命令的实现方式:
下载源码:
image.png

  1. PS的源码:
  2. int main(int argc, char *argv[]){
  3. arg_parse(argc, argv);
  4. #if 0
  5. choose_dimensions();
  6. #endif
  7. if(!old_h_option){
  8. const char *head;
  9. switch(ps_format){
  10. default: /* can't happen */
  11. case 0: head = " PID TTY TIME CMD"; break;
  12. case 'l': head = "F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD"; break;
  13. case 'f': head = "USER PID PPID C STIME TTY TIME CMD"; break;
  14. case 'j': head = " PID PGID SID TTY TIME CMD"; break;
  15. case 'u'|0x80: head = "USER PID %CPU %MEM VSZ RSS TTY S START TIME COMMAND"; break;
  16. case 'v'|0x80: head = " PID TTY S TIME MAJFL TRS DRS RSS %MEM COMMAND"; break;
  17. case 'j'|0x80: head = " PPID PID PGID SID TTY TPGID S UID TIME COMMAND"; break;
  18. case 'l'|0x80: head = "F UID PID PPID PRI NI VSZ RSS WCHAN S TTY TIME COMMAND"; break;
  19. }
  20. printf("%s\n",head);
  21. }
  22. if(want_one_pid){
  23. if(stat2proc(want_one_pid)) print_proc();
  24. else exit(1);
  25. }else{
  26. struct dirent *ent; /* dirent handle */
  27. DIR *dir;
  28. int ouruid;
  29. int found_a_proc;
  30. found_a_proc = 0;
  31. ouruid = getuid();
  32. dir = opendir("/proc");
  33. while(( ent = readdir(dir) )){
  34. if(*ent->d_name<'0' || *ent->d_name>'9') continue;
  35. if(!stat2proc(atoi(ent->d_name))) continue;
  36. if(want_one_command){
  37. if(strcmp(want_one_command,P_cmd)) continue;
  38. }else{
  39. if(!select_notty && P_tty_num==NO_TTY_VALUE) continue;
  40. if(!select_all && P_euid!=ouruid) continue;
  41. }
  42. found_a_proc++;
  43. print_proc();
  44. }
  45. }
  46. }
  47. 另外一段
  48. static int stat2proc(int pid) {
  49. char buf[800]; /* about 40 fields, 64-bit decimal is about 20 chars */
  50. int num;
  51. int fd;
  52. char* tmp;
  53. struct stat sb; /* stat() used to get EUID */
  54. snprintf(buf, 32, "/proc/%d/stat", pid);
  55. if ( (fd = open(buf, O_RDONLY, 0) ) == -1 ) return 0;
  56. num = read(fd, buf, sizeof buf - 1);
  57. fstat(fd, &sb);
  58. P_euid = sb.st_uid;
  59. close(fd);
  60. if(num<80) return 0;
  61. buf[num] = '\0';
  62. tmp = strrchr(buf, ')'); /* split into "PID (cmd" and "<rest>" */
  63. *tmp = '\0'; /* replace trailing ')' with NUL */
  64. /* parse these two strings separately, skipping the leading "(". */
  65. memset(P_cmd, 0, sizeof P_cmd); /* clear */
  66. sscanf(buf, "%d (%15c", &P_pid, P_cmd); /* comm[16] in kernel */
  67. num = sscanf(tmp + 2, /* skip space after ')' too */
  68. "%c "
  69. "%d %d %d %d %d "
  70. "%lu %lu %lu %lu %lu %lu %lu "
  71. "%ld %ld %ld %ld %ld %ld "
  72. "%lu %lu "
  73. "%ld "
  74. "%lu %lu %lu %lu %lu %lu "
  75. "%u %u %u %u " /* no use for RT signals */
  76. "%lu %lu %lu",
  77. &P_state,
  78. &P_ppid, &P_pgrp, &P_session, &P_tty_num, &P_tpgid,
  79. &P_flags, &P_min_flt, &P_cmin_flt, &P_maj_flt, &P_cmaj_flt, &P_utime, &P_stime,
  80. &P_cutime, &P_cstime, &P_priority, &P_nice, &P_timeout, &P_alarm,
  81. &P_start_time, &P_vsize,
  82. &P_rss,
  83. &P_rss_rlim, &P_start_code, &P_end_code, &P_start_stack, &P_kstk_esp, &P_kstk_eip,
  84. &P_signal, &P_blocked, &P_sigignore, &P_sigcatch,
  85. &P_wchan, &P_nswap, &P_cnswap
  86. );
  87. /* fprintf(stderr, "stat2proc converted %d fields.\n",num); */
  88. P_vsize /= 1024;
  89. P_rss *= (PAGE_SIZE/1024);
  90. memcpy(P_tty_text, " ? ", 8);
  91. if (P_tty_num != NO_TTY_VALUE) {
  92. int tty_maj = (P_tty_num>>8)&0xfff;
  93. int tty_min = (P_tty_num&0xff) | ((P_tty_num>>12)&0xfff00);
  94. snprintf(P_tty_text, sizeof P_tty_text, "%3d,%-3d", tty_maj, tty_min);
  95. }
  96. if(num < 30) return 0;
  97. if(P_pid != pid) return 0;
  98. return 1;
  99. }
  100. 遍历进程的实现
  101. #include <stdio.h>
  102. #include <dirent.h>
  103. #include <string.h>
  104. #include <sys/stat.h>
  105. #include <errno.h>
  106. // 显示进程名称
  107. void proc_show_name(int pid)
  108. {
  109. // 打开文件
  110. char path[266];
  111. sprintf(path, "/proc/%d/status", pid);
  112. FILE *fd = fopen(path, "r");
  113. if (fd == NULL)
  114. {
  115. printf("无法打开文件 :%s\n", path);
  116. return;
  117. }
  118. fscanf(fd, "Name: %s\n", path);
  119. printf(" %s ", path);
  120. // 关闭文件.
  121. fclose(fd);
  122. }
  123. // 显示进程模块名称
  124. void proc_show_module(int pid)
  125. {
  126. char path[266];
  127. sprintf(path, "/proc/%d/maps", pid);
  128. FILE *fd = fopen(path, "r");
  129. if (fd == NULL)
  130. {
  131. printf("无法打开文件 :%s\n", path);
  132. }
  133. // %llx-%llx %4s %x %5s %d %s
  134. unsigned long long base = 0, end = 0;
  135. char pageatt[5];
  136. int offset;
  137. char time[6];
  138. unsigned int inode;
  139. while (
  140. 7 ==
  141. fscanf(fd, "%llx-%llx %4s %x %5s %d %s",
  142. &base, &end, pageatt, &offset, time, &inode, path))
  143. {
  144. if (offset == 0)
  145. {
  146. printf("\t%llx-%llx %4s %x %5s %d %s\n",
  147. base, end, pageatt, offset, time, inode, path);
  148. }
  149. }
  150. // 关闭文件.
  151. fclose(fd);
  152. return;
  153. }
  154. int main()
  155. {
  156. //1.遍历/proc目录
  157. struct DIR *pdir = opendir("/proc");
  158. if (pdir == NULL)
  159. {
  160. printf("无法打开/proc目录\n");
  161. return;
  162. };
  163. // 2.遍历目录下所有数字的目录
  164. int pid;
  165. char path[266] = {0};
  166. char proc_name[100];
  167. struct dirent *ent = NULL;
  168. while (ent = readdir(pdir))
  169. {
  170. // 得到进程id,如果无法将字符串转换成数字,说明目录名不是进程id
  171. if (1 != sscanf(ent->d_name, "%d", &pid))
  172. continue;
  173. // 进程信息(包括进程名)保存在status文件中中
  174. proc_show_name(pid);
  175. // 进程pid
  176. printf("%6d \n", pid);
  177. proc_show_module(pid);
  178. }
  179. // 3.关闭目录流cl
  180. closedir(pdir);
  181. }

1.2.5 创建进程

  1. 使用fork创建一个子进程 ```cpp

    include

    include

    include

void CreateChild() { pid_t pid; const char *message = NULL; int n = 0; int i =0; pid = fork(); // 创建进程,子进程代码也是从这个地方开始执行 if(pid<0) { // pid大于0时表示当前处于父进程 perror(“fork failed\n”); return; } if (pid == 0){ //pid为0时时子进程 message = “this is the child\n”; n = 6; } else{ // pid是-1时错误 message = “this is the parent\n”; n=3; } for(i =0;i<n;i++) { printf(message); sleep(1); } return; } int main() { CreateChild(); return 0; }

  1. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/22743586/1638952286287-d90cb5ee-bb3b-4b1c-8e37-9570790bd5e0.png#clientId=ue2b19156-ddc7-4&from=paste&id=ub752cb82&margin=%5Bobject%20Object%5D&name=image.png&originHeight=330&originWidth=392&originalType=url&ratio=1&size=35876&status=done&style=none&taskId=u7b0fcc2a-5ac1-454a-adc7-f933b8fd3bf)
  2. <a name="D8mhq"></a>
  3. ### **1.2.6 创建进程并且能够 执行一个其他程序**
  4. ```cpp
  5. #include <unistd.h>
  6. #include <stdio.h>
  7. pid_t CreateProcess(char *name)
  8. {
  9. pid_t pid;
  10. pid = fork(); // 创建子进程
  11. if(pid<0)
  12. {
  13. perror("fork failed\n");
  14. return -1;
  15. }
  16. if (pid == 0)
  17. {
  18. execl(name,NULL); // 让子进程执行新程序
  19. }else{
  20. return pid;
  21. }
  22. return pid;
  23. }
  24. int main()
  25. {
  26. CreateProcess("/home/pb/app");
  27. return 0;
  28. }

1.2.7 关于子进程的等待

子进程是需要被父进程等待,才能够清理完全,

  1. #include <sys/types.h>
  2. #include <sys/wait.h>
  3. #include <unistd.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. int main(void)
  7. {
  8. pid_t pid;
  9. pid = fork(); // 创建子进程
  10. if (pid < 0)
  11. { // -1时错误
  12. perror("fork failed");
  13. exit(1);
  14. }
  15. if (pid == 0) { // 子进程
  16. int i;
  17. for (i = 3; i > 0; i--) {
  18. printf("This is the child\n");
  19. sleep(1);
  20. }
  21. exit(3);
  22. }
  23. else
  24. { //父进程
  25. int stat_val;
  26. waitpid(pid, &stat_val, 0); // 等待指定的子进程结束,否则子进程会变成僵尸进程
  27. if (WIFEXITED(stat_val))// 如果子进程正常结束则为非0值。
  28. printf("Child exited with code %d\n", WEXITSTATUS(stat_val));
  29. else if (WIFSIGNALED(stat_val)) // 进程是因为信号而结束则此宏值为真
  30. printf("Child terminated abnormally, signal %d\n", WTERMSIG(stat_val));
  31. }
  32. return 0;
  33. }

2. 线程

2.1 创建线程

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <pthread.h>
  5. // 线程回调函数
  6. void *thread_run(void *arg){
  7. int n = (int)arg;
  8. for(int i=0;i<10000;i++){
  9. printf("child: %d out number%d \n",i,n);
  10. }
  11. // 释放线程资源
  12. pthread_detach(pthread_self());
  13. return NULL;
  14. }
  15. int main(){
  16. //1.创建线程
  17. pthread_t pthread;
  18. pthread_create(&pthread,NULL,thread_run,100);
  19. for(int i=0;i<10000;i++){
  20. printf("main: %d \n",i);
  21. }
  22. //2. 等待线程结束
  23. pthread_join(pthread,NULL);
  24. return 0;
  25. }
  26. // 编译 gcc 01创建线程.c -g -lpthread -o app
  27. 总结:
  28. 子线程结束的时候,一定要调用pthread_detach 这个函数。
  29. 创建线程的地方,应该调用pthread_join去等待线程结束。

2.2 Linux上的线程同步问题的解决

  1. 问题:
  2. #include <stdio.h>
  3. #include <pthread.h>
  4. #include <unistd.h>
  5. // 全局变量(共享资源)
  6. int g_source = 0;
  7. // 线程1
  8. void *workthread1(void * param){
  9. // 循环增加10000次
  10. for (size_t i = 0; i < 10000; i++){
  11. g_source += 1;
  12. }
  13. // 销毁线程
  14. pthread_detach(pthread_self());
  15. return NULL;
  16. }
  17. // 线程2
  18. void *workthread2(void * param){
  19. // 循环增加10000次
  20. for (size_t i = 0; i < 10000; i++){
  21. g_source += 1;
  22. }
  23. // 销毁线程
  24. pthread_detach(pthread_self());
  25. return NULL;
  26. }
  27. int main(){
  28. pthread_t threads[2]={};
  29. // 创建两个线程
  30. pthread_create(&threads[0],NULL,workthread1,0);
  31. pthread_create(&threads[1],NULL,workthread2,0);
  32. // 等待线程销毁
  33. for (size_t i = 0; i < 2; i++)
  34. {
  35. pthread_join(threads[i],NULL);
  36. }
  37. // 显示全局变量的内容
  38. printf("g_source = %d \n",g_source);
  39. return 0;
  40. }
  41. 如何解决这个问题呢???
  42. 可以使用互斥锁
  43. #include <stdio.h>
  44. #include <pthread.h>
  45. #include <unistd.h>
  46. // 全局变量(共享资源)
  47. int g_source = 0;
  48. pthread_mutex_t counter_mutex=PTHREAD_MUTEX_INITIALIZER;
  49. // 线程1
  50. void *workthread1(void * param){
  51. // 循环增加10000次
  52. for (size_t i = 0; i < 100000; i++){
  53. pthread_mutex_lock(&counter_mutex); //获取锁
  54. g_source += 1;
  55. pthread_mutex_unlock(&counter_mutex); //释放锁,其它线程才可以取得锁
  56. }
  57. // 销毁线程
  58. pthread_detach(pthread_self());
  59. return NULL;
  60. }
  61. // 线程2
  62. void *workthread2(void * param){
  63. // 循环增加10000次
  64. for (size_t i = 0; i < 100000; i++){
  65. pthread_mutex_lock(&counter_mutex); //获取锁
  66. g_source += 1;
  67. pthread_mutex_unlock(&counter_mutex); //释放锁,其它线程才可以取得锁
  68. }
  69. // 销毁线程
  70. pthread_detach(pthread_self());
  71. return NULL;
  72. }
  73. int main(){
  74. pthread_t threads[2]={};
  75. // 创建两个线程
  76. pthread_create(&threads[0],NULL,workthread1,0);
  77. pthread_create(&threads[1],NULL,workthread2,0);
  78. // 等待线程销毁
  79. for (size_t i = 0; i < 2; i++)
  80. {
  81. pthread_join(threads[i],NULL);
  82. }
  83. // 显示全局变量的内容
  84. printf("g_source = %d \n",g_source);
  85. return 0;
  86. }
  87. 信号量解决问题
  88. #include <stdio.h>
  89. #include <pthread.h>
  90. #include <unistd.h>
  91. #include <semaphore.h>
  92. // 全局变量(共享资源)
  93. int g_source = 0;
  94. // 信号量
  95. sem_t g_semaphore;
  96. // 线程1
  97. void *workthread1(void * param){
  98. // 循环增加10000次
  99. for (size_t i = 0; i < 10000; i++){
  100. sem_wait(&g_semaphore); //阻塞,直到获取信号,信号数-1
  101. g_source += 1; //访问全局变量,不会被其它线程打断
  102. sem_post(&g_semaphore); //释放一个信号数+1,让其它线程可以获取信号
  103. }
  104. // 销毁线程
  105. pthread_detach(pthread_self());
  106. return NULL;
  107. }
  108. // 线程2
  109. void *workthread2(void * param){
  110. // 循环增加10000次
  111. for (size_t i = 0; i < 10000; i++){
  112. sem_wait(&g_semaphore); //阻塞,直到获取信号,信号数-1
  113. g_source += 1; //访问全局变量,不会被其它线程打断
  114. sem_post(&g_semaphore); //释放一个信号数+1,让其它线程可以获取信号
  115. }
  116. // 销毁线程
  117. pthread_detach(pthread_self());
  118. return NULL;
  119. }
  120. int main(){
  121. pthread_t threads[2]={};
  122. // 创建信号量
  123. sem_init(
  124. &g_semaphore, //信号量
  125. 0, //0表示同一进程间线程同步
  126. 1); //信号最大值
  127. // 创建两个线程
  128. pthread_create(&threads[0],NULL,workthread1,0);
  129. pthread_create(&threads[1],NULL,workthread2,0);
  130. // 等待线程销毁
  131. for (size_t i = 0; i < 2; i++)
  132. {
  133. pthread_join(threads[i],NULL);
  134. }
  135. // 显示全局变量的内容
  136. printf("g_source = %d \n",g_source);
  137. // 销毁信号量
  138. sem_destroy(&g_semaphore);
  139. return 0;
  140. }
  141. 信号量,比较适合解决生产消费的问题。

3. 网络

image.png

image.png