Procesy w Linuxie - microshell i "Segmentation fault"

0

Witam.
Mam pytanie dotyczace programu, który jest swego rodzaju microshellem napisanym w jezyku C i standardach ansi. Jest to dość ważna praca, którą potrzebuje na studia (nawiasem mówiąc, nie wiąże swojej przyszlosci z C, bardziej interesuje mnie szerko pojety "Web Development" i w tą stronę będę szedł).
Mam natomiast pytanie, gdyż napisałem kod, który ma wyświetlić procesy z poziomu mojego microshella i kod, który odpowiada komendzie 'less /proc/pid/stat i wyswietla peirwsze cztery wartosci zwracane przez to polecenie.
Kod odpowiadający za wypisanie "większej ilosci informacji" o danym procesie wygląda tak:

void  moreps2(char **args) {
    int val;
    val = atoi(args[1]);
    printf("pid = %d\n", val);

    char filename[1000];
    sprintf(filename, "/proc/%d/stat", val);
    FILE *f = fopen(filename, "r");

    int unused;
    char comm[1000];
    char state;
    int ppid;
    fscanf(f, "%d %s %c %d", &unused, comm, &state, &ppid);
    printf("comm = %s\n", comm);
    printf("state = %c\n", state);
    printf("parent pid = %d\n", ppid);
    fclose(f);
}

Jednak występuje taki błąd, który zamiescilem w zalaczniku
Czym on jest spowodowany? Czy mógłby mi ktoś z tym pomóc?
Pozdrawiam, Dawid

2

Strzał na oślep: f == NULL?

0
alagner napisał(a):

Strzał na oślep: f == NULL?

No najprawdopodbniej tak, tylko teraz pytanie, dlaczego moje polecenie "sproc" wyświetla proces, który "nie istnieje"?
Kod funkcji sproc tez moge wrzucic

2

Na screenshot widać (jakbyś nie mógł skopiować tekstu z konsoli), że podałeś PID który do niczego nie pasuje.
Wpisałeś pid 14895 a takiego nie ma, jest za to 14871 i 11895, z których kawałków poskładałeś nieistniejący pid.
Dlatego strzał na oślep od @alagner trafia w 10kę.

Czyli pozdrowienia dla dyslektyka :).

Co do kodu:

  1. Trzeba było dopisać if (!f) {return;}
  2. zmienna unused jest niepotrzebna, bo można zrobić:
fscanf(f, "%*d %s %c %d", comm, &state, &ppid);
  1. Prototyp funkcji, woła o pomstę do nieba void moreps2(char **args). Nie mogło być void moreps2(const char *args) albo lepiej void moreps2(int pid)? Albo jeszcze lepiej int getProcesStat(int pid, struct ProcessStat *out)
    Słyszałeś o SRP?
0

@MarekR22: Fakt - przeoczylem warunek, moje niedopatrzenie. Natomiast i tak zastanawia mnie, dlaczego polecmie "sproc" pokazuje PID procesu, który nie istnieje

s444406 @ [/home/students/s444406/Microshell] $ sproc
Pid: 9226  Name:	Dawid
Pid: 10433  Name:	Dawid
Pid: 14171  Name:	systemd
Pid: 14259  Name:	bash
s444406 @ [/home/students/s444406/Microshell] $ moreps2 9226
pid = 9226
comm = (Dawid)
state = R
parent pid = 14259
s444406 @ [/home/students/s444406/Microshell] $ sproc
Pid: 9226  Name:	Dawid
Pid: 11573  Name:	Dawid
Pid: 14171  Name:	systemd
Pid: 14259  Name:	bash
s444406 @ [/home/students/s444406/Microshell] $ moreps2 11573
pid = 11573
Segmentation fault
s444406@lts:~/Microshell$

A co w takiej sytuacji?
Problem z int pid jest taki, ze ciezko bedzie wywolac to, podczas gdy wywolaniem funkcji jest polcenie moreps2 pid, ktore przechwytywane jest jako char w tablicy args. Dlatego jest tam wykorzystanie atoi().

2

Nie nauczyli cię korzystać z debuggera?
Jeśli nie to wstyd jak na studenta informatyki!
A nauczyli cię dekodować crash logi?

Jeśli nie to popraw kod tak:

void  moreps2(char **args) {
    int val;
    val = atoi(args[1]);
    printf("pid = %d\n", val);

    char filename[1000];
    sprintf(filename, "/proc/%d/stat", val);
    FILE *f = fopen(filename, "r");
    if (!f) {
        fprintf(stderr, "Failed to open stat file: %s - %s !\n", filename, strerror(errno));
        return;
    }

    char comm[1000];
    char state;
    int ppid;
    int count = fscanf(f, "%*d %s %c %d", comm, &state, &ppid);
    fclose(f);

    if (3 != count) {
        fprintf(stderr, "Failed to read data from stat file: %s !\n%d\n%s\n", filename, count, strerror(errno));
        return;
    }
    printf("comm = %s\n", comm);
    printf("state = %c\n", state);
    printf("parent pid = %d\n", ppid);
}

Zobaczymy co dalej.

0
MarekR22 napisał(a):

Nie nauczyli cię korzystać z debuggera?
Jeśli nie to wstyd jak na studenta informatyki!
A nauczyli cię dekodować crash logi?

Jeśli nie to popraw kod tak:

void  moreps2(char **args) {
    int val;
    val = atoi(args[1]);
    printf("pid = %d\n", val);

    char filename[1000];
    sprintf(filename, "/proc/%d/stat", val);
    FILE *f = fopen(filename, "r");
    if (!f) {
        fprintf(stderr, "Failed to open stat file: %s - %s !\n", filename, strerror(errno));
        return;
    }

    char comm[1000];
    char state;
    int ppid;
    int count = fscanf(f, "%*d %s %c %d", comm, &state, &ppid);
    fclose(f);

    if (3 != count) {
        fprintf(stderr, "Failed to read data from stat file: %s !\n%d\n%s\n", filename, count, strerror(errno));
        return;
    }
    printf("comm = %s\n", comm);
    printf("state = %c\n", state);
    printf("parent pid = %d\n", ppid);
}

Zobaczymy co dalej.

Fakt, działa teraz - w sesnie, nie wyrzuca błędu "Segmentation Fault", ale nadal zastanawia mnie, dlaczego podczas wywolania funckji sproc pokazuje mi proces, który nie istnieje. Dlatego wrzucam Wam tutaj kod funkcji sproc

void process (){
    if (fork() == 0){
        if (chdir("/proc") != 0){
            perror("cant open /proc");
            exit(0);
        }
        else{ /*weszlismy w 1 fork do katalogu proc */
            DIR *d;
            struct dirent *ent;
            int i, p;

            if ((d = opendir("/proc")) != NULL){ /*otwieramy proc*/
                while ((ent = readdir(d)) != NULL){ /*czytamy zawartosc*/
                    char *name = ent->d_name;
                    p=0;
                    for (i=0; i<strlen(name); i++)
                        if (!isdigit(name[i])){
                            p=1;
                            break;
                        }
                    if(p)
                        continue; /*jesli nie jest liczba to idziemy dalej*/

                    char *direc;
                    direc = (char*)malloc((strlen(name) + 10) * sizeof(char));
                    direc[0] = '\0';
                    strcat(direc, "./");
                    strcat(direc, name);
                    strcat(direc, "/status"); /*tworzenie sciezki*/

                    if (fork() == 0){
                        printf("Pid: %s  ", name);
                        exit(0);
                    }
                    else
                        wait(NULL);

                    if (fork() == 0){
                        execlp("sed", "sed", "-n", "1p", direc, NULL);
                    }
                    else
                        wait(NULL);
                }
            }
        }
        exit(0);
    }
    else
        wait(NULL);
}

Oczywiscie w ramach wyjasnienia - poleceniem "sproc" wywoluje w moim microshellu funkcje process();

0

Takie jest wyjście programu po poprawkach przeprowadzonych przez @MarekR22:

s444406 @ [/home/students/s444406/Microshell] $ sproc
Pid: 6641  Name:	Dawid
Pid: 6994  Name:	Dawid
Pid: 14171  Name:	systemd
Pid: 14259  Name:	bash
s444406 @ [/home/students/s444406/Microshell] $ moreps2 6641
pid = 6641
comm = (Dawid)
state = R
parent pid = 14259
s444406 @ [/home/students/s444406/Microshell] $ sproc
Pid: 6641  Name:	Dawid
Pid: 8062  Name:	Dawid
Pid: 14171  Name:	systemd
Pid: 14259  Name:	bash
s444406 @ [/home/students/s444406/Microshell] $ moreps2 8062
pid = 8062
Failed to open stat file: /proc/8062/stat !
s444406 @ [/home/students/s444406/Microshell] $ 
1

spróbuj z innymi pid-ami łącznie z tymi systemowymi.
Zmodyfikowałem kod, żeby wyświetlił więcej informacji o błędzie (tekst).

0
MarekR22 napisał(a):

spróbuj z innymi pid-ami łącznie z tymi systemowymi.
Zmodyfikowałem kod, żeby wyświetlił więcej informacji o błędzie (tekst).

s444406 @ [/home/students/s444406/Microshell] $ sproc
Pid: 6641  Name:	Dawid
Pid: 14171  Name:	systemd
Pid: 14259  Name:	bash
Pid: 28624  Name:	Dawid
s444406 @ [/home/students/s444406/Microshell] $ moreps2 14171
pid = 14171
comm = (systemd)
state = S
parent pid = 1
s444406 @ [/home/students/s444406/Microshell] $ sproc
Pid: 6641  Name:	Dawid
Pid: 14171  Name:	systemd
Pid: 14259  Name:	bash
Pid: 31077  Name:	Dawid
s444406 @ [/home/students/s444406/Microshell] $ moreps2 14259
pid = 14259
comm = (bash)
state = S
parent pid = 14246
s444406 @ [/home/students/s444406/Microshell] $ 

Jest tak, jak chciałem, żeby było! Wielkie dziękuję @MarekR22! Ale nadal powtorze pytanie zwiazane z tym, dlaczego wyświetlają mi sie dwa procesy o name: Dawid?

1

Przyznam, że nie analizowałem zbyt dokładnie Twojego kodu ale jeśli chodzi o dwa procesy o nazwie Dawid to podejrzewam, że chodzi o forka, którego wywołujesz (nawet kilka razy). Więc tworzysz proces na krótką chwilę i dla tego nie widzisz go.

0

W jaki sposób wiec przerobić ten kod, żeby nadal działał, ale był bez bodajże 3-krotnego wywołania fork()?

0

A jest jakiś konkretny powód dla tych trzech forków? Pytam bo nie znam zamiarów autora (Ciebie?).
Myślę, że wystarczy zostawić to wywołanie fork(), które odpowiada za odpalenie programu sed. Dwa pierwsze wywołania fork() usuń (i oczywiście towarzyszące if/else i wait(NULL).

0

Po usunięciu pierwszych dwóch forków, dzieje się niestety to:

s444406 @ [/home/students/s444406/Microshell] $ sproc
sed: can't read ./9083/status: No such file or directory
sed: can't read ./26221/status: No such file or directory
sed: can't read ./26360/status: No such file or directory

po czym wywala program...

0
void process (){
            if (chdir("/proc") != 0){
              perror("cant open /proc");
              exit(0);
            }
            else {
              DIR *d;
              struct dirent *ent;
              int i, p;

              if ((d = opendir("/proc")) != NULL){ /*otwieramy proc*/
                while ((ent = readdir(d)) != NULL){ /*czytamy zawartosc*/
                  char *name = ent->d_name;
                  p=0;
                  for (i=0; i<strlen(name); i++)
                    if (!isdigit(name[i])){
                        p=1;
                        break;
                    }
                  if(p)
                    continue; /*jesli nie jest liczba to idziemy dalej*/

                  char *direc;
                  direc = (char*)malloc((strlen(name) + 10) * sizeof(char));
                  direc[0] = '\0';
                  strcat(direc, "./");
                  strcat(direc, name);
                  strcat(direc, "/status"); /*tworzenie sciezki*/
                  printf("Pid: %s  ", name);


                  if (fork() == 0){
                      execlp("sed", "sed", "-n", "1p", direc, NULL);
                  }
                  else
                      wait(NULL);
                }
              }
            }
    exit(0);
}

Tak wyglada obecnie mój kod... wyrzuca go podczas wywoływania funkcji, zaraz po printf("Pid: %s ", name);

0

Dobra, problem rozwiazany, ale pojawil sie nastepny, zgodnie z zasada, ze im glebiej w las, tym wiecej drzew:
chciałbym, żeby procesy wyswitlily sie tak ładnie:

Pid: 11573  Name:   Dawid
Pid: 14171  Name:   systemd
Pid: 14259  Name:   bash

Ale one są uparte i robią tak:

Name:	Dawid
Name:	systemd
Name:	bash
pid: 19627  pid: 26221  pid: 26360

Ponizej zamieszczam kod:

void process() {
	DIR *d;
	struct dirent *ent;
	int i;

	if ((d = opendir("/proc")) == NULL)
		return;
	while ((ent = readdir(d)) != NULL) { /* czytamy zawartosc */
		char *name = ent->d_name;

		for (i=0; name[i]; i++)
			if (!isdigit(name[i]))
				break;
		if (name[i])
			continue; /* jesli nie jest liczba to idziemy dalej */

		char *direc = malloc(i + 10); /* wow! w i jest teraz długość name! */
		sprintf(direc, "/proc/%s/status", name);

		printf("pid: %s  ", name);
		if (fork() == 0)
			execlp("sed", "sed", "-n", "1p", direc, NULL);
		else
			wait(NULL);

		free(direc); /* po każdym malloc trzeba posprzątać */
	}
	closedir(d);
}

1 użytkowników online, w tym zalogowanych: 0, gości: 1